【问题标题】:Expression grammar with exponentiation operator using Boost Spirit使用 Boost Spirit 的指数运算符的表达式语法
【发布时间】:2013-07-12 16:01:50
【问题描述】:

我想将求幂运算符添加到expression grammar provided in the Boost spirit samples

BNF 语法如下:(请参阅此答案,例如:"Unambiguous grammar for exponentiation operation"

E -> E + T | E - T | T
T -> T * F | T / F | X
X -> X ^ Y | Y
Y -> i | (E)

我这样翻译成 Boost Spirit:

    template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type>
{
    calculator() : calculator::base_type(expression)
    {
        qi::uint_type uint_;

        expression =
        term
        >> *(   ('+' >> term            [&do_add])
             |   ('-' >> term            [&do_subt])
             )
        ;

        term =
        factor
        >> *(   ( '*' >> factor          [&do_mult])
             |  ('x' >> factor          [&do_mult])
             |   ('/' >> factor          [&do_div])
             );

        factor= expo >> *( '^' >> expo [&do_power]);

        expo =
           uint_                           [&do_int]
        |  '(' >> expression >> ')'
        |  ('-' >> expo[&do_neg])
        |  ('+' >> expo)

        ;
    }

    qi::rule<Iterator, ascii::space_type> expression, term, factor, expo;
};

问题在于这种情况下的 ^ 运算符是左关联的,即 2 ^ 3 ^ 4 被错误地解析为 (2 ^ 3) ^ 4 而不是 2^ (3 ^ 4)

如何重写语法以使^ 变为右结合?显然我在factor 的定义中使用的Kleene 星是不正确的。将语法转换为 Spirit 代码的方法是什么?似乎有一种方法可以从左因子语法转到 Spirit 实现,但我无法立即看到。

以更正式的方式,Spirit 代码如下所示(在我尝试添加指数之前):

E = T ( +T | -T ) *
T = F ( xF | /F ) *
F = int | ( E ) | +F | -F

而左因子语法是

E  =  T E'
E' = +T E' | -T E' | epsilon
T  =  F T'
T' = *F T' | /F T' | epsilon
F  = ( E ) | int | +F | -F

【问题讨论】:

    标签: c++ boost-spirit context-free-grammar boost-spirit-qi associativity


    【解决方案1】:

    我认为你可以使用正确的递归来得到你想要的:

    factor= expo >> -('^' >> factor [&do_power]);
    

    我不确定所需的评估顺序;你可能想要类似的东西

    factor= expo [&do_power] >> -('^' >> factor);
    

    改为。

    这是一个简单的测试程序,展示了它如何处理2^(6/2)^4+1

    编辑Coliru

    上实时观看
    Type an expression...or [q or Q] to quit
    2^(6/2)^4+1
    
    push 2
    push 6
    push 2
    divide
    push 4
    exp
    exp
    push 1
    add
    -------------------------
    Parsing succeeded
    

    完整代码

    #define BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
    #define BOOST_SPIRIT_DEBUG
    
    #include <boost/spirit/include/qi.hpp>
    
    namespace client
    {
        namespace qi = boost::spirit::qi;
        namespace ascii = boost::spirit::ascii;
    
        ///////////////////////////////////////////////////////////////////////////////
        //  Semantic actions
        ////////////////////////////////////////////////////////1///////////////////////
        namespace
        {
            void do_int(int n)  { std::cout << "push " << n << std::endl; }
            void do_add()       { std::cout << "add\n"; }
            void do_subt()      { std::cout << "subtract\n"; }
            void do_mult()      { std::cout << "mult\n"; }
            void do_div()       { std::cout << "divide\n"; }
            void do_power()     { std::cout << "exp\n"; }
            void do_neg()       { std::cout << "negate\n"; }
        }
    
        ///////////////////////////////////////////////////////////////////////////////
        //  Our calculator grammar
        ///////////////////////////////////////////////////////////////////////////////
        template <typename Iterator>
            struct calculator : qi::grammar<Iterator, ascii::space_type>
        {
            calculator() : calculator::base_type(expression)
            {
                qi::uint_type uint_;
    
                expression = term
                    >> *(   ('+' >> term            [&do_add])
                         |  ('-' >> term            [&do_subt])
                         )
                    ;
    
                term = factor
                    >> *(   ( '*' >> factor         [&do_mult])
                         |  ('x' >> factor          [&do_mult])
                         |  ('/' >> factor          [&do_div])
                         );
    
                factor= expo >> -('^' >> factor [&do_power]);
    
                expo = uint_                        [&do_int]
                    |  '(' >> expression >> ')'
                    |  ('-' >> expo[&do_neg])
                    |  ('+' >> expo)
                ;
    
                BOOST_SPIRIT_DEBUG_NODES((expression)(term)(factor)(expo));
            }
          private:
            qi::rule<Iterator, ascii::space_type> expression, term, factor, expo;
        };
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    //  Main program
    ///////////////////////////////////////////////////////////////////////////////
    int
    main()
    {
        std::cout << "/////////////////////////////////////////////////////////\n\n";
        std::cout << "Expression parser...\n\n";
        std::cout << "/////////////////////////////////////////////////////////\n\n";
        std::cout << "Type an expression...or [q or Q] to quit\n\n";
    
        typedef std::string::const_iterator iterator_type;
        typedef client::calculator<iterator_type> calculator;
    
        boost::spirit::ascii::space_type space; // Our skipper
        calculator calc; // Our grammar
    
        std::string str;
        while (std::getline(std::cin, str))
        {
            if (str.empty() || str[0] == 'q' || str[0] == 'Q')
                break;
    
            std::string::const_iterator iter = str.begin();
            std::string::const_iterator end = str.end();
            bool r = phrase_parse(iter, end, calc, space);
    
            if (r && iter == end)
            {
                std::cout << "-------------------------\n";
                std::cout << "Parsing succeeded\n";
                std::cout << "-------------------------\n";
            }
            else
            {
                std::string rest(iter, end);
                std::cout << "-------------------------\n";
                std::cout << "Parsing failed\n";
                std::cout << "stopped at: \" " << rest << "\"\n";
                std::cout << "-------------------------\n";
            }
        }
    
        std::cout << "Bye... :-) \n\n";
        return 0;
    }
    

    【讨论】:

    • 谢谢。它在 OS X 上编译并正常工作,但我有一个 /opt/local/include/boost/mpl/assert.hpp:160:7:当我尝试为 iOS 编译它时,递归模板实例化超过了 128 的最大深度。编译器似乎不接受这种语法。我明天好好看看。
    • 您可以简单地尝试将-ftemplate-depth=256 添加到编译器选项(或根据需要更高)
    猜你喜欢
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 2011-04-05
    • 2013-05-22
    • 2012-01-17
    • 2011-03-05
    相关资源
    最近更新 更多