boost:spirit :: qi解析器使用多个语法和phoenix :: construct

时间:2012-04-08 11:38:57

标签: c++ parsing boost-spirit-qi

我在编写使用另一种Qi语法的Qi语法时遇到了麻烦。类似的问题被问到here,但我也试图使用phoenix :: construct并有编译困难。

这是我正在尝试做的简化版本。我意识到这个例子可能很容易使用BOOST_FUSION_ADAPT_STRUCT完成,但我的实际代码处理更复杂的对象类型,所以我希望有一种方法可以使用语义动作来实现这一点。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_container.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <cstdlib>
#include <iostream>


namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

// grammar for real numbers
template <typename Iterator>
struct Real : qi::grammar<Iterator, long double()>
{
    qi::rule<Iterator, long double()> r;
    Real() : Real::base_type(r)
    {
        r %= qi::long_double;
    }
};

// grammar for complex numbers of the form a+bi
template <typename Iterator>
struct Complex : qi::grammar<Iterator, std::complex<long double>()>
{
    qi::rule<Iterator, std::complex<long double>()> r;
    Real<Iterator> real;
    Complex() : Complex::base_type(r)
    {
        r = real [qi::_a = qi::_1] >> (qi::lit("+") | qi::lit("-")) 
            >> real [qi::_b = qi::_1] >> -qi::lit("*") >> qi::lit("i")
            [
                qi::_val = phx::construct<std::complex<long double> >(qi::_a, qi::_b)
            ];
    }
};

int main()
{
    // test real parsing
    std::cout << "Parsing '3'" << std::endl;
    std::string toParse = "3";
    Real<std::string::iterator> real_parser;
    long double real_val;
    std::string::iterator beginIt = toParse.begin();
    std::string::iterator endIt = toParse.end();
    bool r = qi::parse(beginIt, endIt, real_parser, real_val);
    if(r && beginIt == endIt)
        std::cout << "Successful parse: " << real_val << std::endl;
    else
        std::cout << "Could not parse" << std::endl;

    // test complex parsing
    std::cout << "Parsing '3+4i'" << std::endl;
    toParse = "3+4i";
    Complex<std::string::iterator> complex_parser;
    std::complex<long double> complex_val;
    beginIt = toParse.begin();
    endIt = toParse.end();
    r = qi::parse(beginIt, endIt, complex_parser, complex_val);
    if(r && beginIt == endIt)
        std::cout << "Successful parse: " << real_val << std::endl;
    else
        std::cout << "Could not parse" << std::endl;
}

我能够使用Spirit的文档中演示的phrase_parse方法解析Complex,但我希望能够轻松地将Complex语法集成到其他解析器(例如表达式解析器)中。是否有一些我缺少的东西可以让我将Real和Complex对象解析为不同的实体,同时仍能在其他规则/语法中有效地使用它们?

1 个答案:

答案 0 :(得分:2)

qi::_aqi::_b代表规则的第一个和第二个局部变量。只有在规则qi::locals<long double, long double>的声明中添加r作为模板参数时,这些变量才可用(在本例中也是qi::grammar...,因为起始规则传递给语法的构造函数需要与语法兼容,即具有相同的模板参数。)

下面你可以看到另一种不需要局部变量的替代方案:

// grammar for complex numbers of the form a+bi
template <typename Iterator>
struct Complex : qi::grammar<Iterator, std::complex<long double>()>
{
    qi::rule<Iterator, std::complex<long double>()> r;
    Real<Iterator> real;
    Complex() : Complex::base_type(r)
    {
        r = (
                real  >> (qi::lit("+") | qi::lit("-"))
                >> real  >> -qi::lit("*") >> qi::lit("i")
            )
            [
                qi::_val = phx::construct<std::complex<long double> >(qi::_1, qi::_2)
            ];
    }
};

在这种情况下,语义操作附加到整个解析器序列,我们可以使用_N占位符获取所需的属性。这里,qi :: _ 1指的是第一个Real解析器匹配的属性,qi :: _ 2指的是第二个。

使用任何替代方案,我们通常可以使用这些语法:

//using complex_parser, real_parser, complex_val and real_val declared in your code
std::cout << "Parsing 'variable=3+4i-2'" << std::endl;
toParse = "variable=3+4i-2";
beginIt = toParse.begin();
endIt = toParse.end();
std::string identifier;
r = qi::parse(beginIt, endIt, *qi::char_("a-z") >> '=' >> complex_parser >> '-' >> real_parser, identifier, complex_val, real_val);
if(r && beginIt == endIt)
    std::cout << "Successful parse: " << identifier << complex_val.real() << " " << complex_val.imag() << " " << real_val << std::endl;
else
    std::cout << "Could not parse" << std::endl;