使用Spirit QI创建可重用的语法

时间:2016-06-07 18:34:13

标签: boost-spirit-qi

我是C ++的新手,但是我需要解析类似SQL的表达式: 国='美国' AND state =' CA' AND price> = 100.0,这只是一个假的例子,但它是可行的。

所以,我尝试用精神QI来解决。每列都有一个特定的类型:float,integer,double,char和string。我想创建一个可重用的语法来支持那些带有模板或/和特征的列类型,但是我被阻止了。

我想要这样的东西:

template <typename Iterator, typename ColumnType, typename Skipper>
struct test : qi::grammar<Iterator, ColumnType, Skipper>
{
    test() : test::base_type(expression)
    {
        expression = MyTrait<ColumnType>::type >> "AND" >> MyTrait<ColumnType>::type;
    }

    qi::rule<Iterator, ColumnType, Skipper> expression;
};

我尝试使用Signature模板参数,但没有用。

MyTrait.h:     使用名称空间boost :: spirit :: qi;

template< typename T >
struct MyTrait
{
    typedef T type;
};

template<> struct MyTrait< double >
{
    typedef double_type type;
};

template<> struct MyTrait< float >
{
    typedef float_type type;
};

MyTrait的基本思想是将原始类型(double,float ...)转换为精神QI类型,以便在语法中使用它们。请注意,MyTrait&lt; \ double&gt; :: type会从QI中生成double_type,但在语法中必须是double _。

main.cpp中:

int main() {
    std::string input("99.0 AND 2.0");
    std::string::const_iterator iter = input.begin();
    std::string::const_iterator end = input.end();
    test<std::string::const_iterator, double, qi::space_type> test_parser;
    double result;
    bool r = phrase_parse(iter, end, test_parser, qi::space, result);
    return 0;
 }

我是在正确的轨道上吗?

按照Chris Beck的要求遵循编译器错误消息:

g++-4.8 -I/usr/include/boost -O0 -g3 -Wall -c -fmessage-length=0 --std=c++11  -fpermissive -MMD -MP -MF"src/main.d" -MT"src/main.o" -o "src/main.o" "../src/main.cpp"
../src/main.cpp: In instantiation of ‘test<Iterator, ColumnType, Skipper>::test() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >; ColumnType = double; Skipper = boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::standard> >, 0l>]’:
../src/main.cpp:91:60:   required from here
../src/main.cpp:76:57: error: dependent-name ‘MyTrait<ColumnType>::type’ is parsed as a non-type, but instantiation yields a type
     expression = MyTrait<ColumnType>::type >> "AND" >> MyTrait<ColumnType>::type;
                                                     ^
../src/main.cpp:76:57: note: say ‘typename MyTrait<ColumnType>::type’ if a type is meant
../src/main.cpp:76:48: error: dependent-name ‘MyTrait<ColumnType>::type’ is parsed as a non-type, but instantiation yields a type
     expression = MyTrait<ColumnType>::type >> "AND" >> MyTrait<ColumnType>::type;
                                            ^
../src/main.cpp:76:48: note: say ‘typename MyTrait<ColumnType>::type’ if a type is meant

1 个答案:

答案 0 :(得分:0)

据我所知,您希望自动将特定解析器硬连线到给定的result_type。你这样做会放弃qi的灵活性。我建议您仔细检查您的方法。

下面的代码示例执行了我到目前为止所了解的内容。

但是(!)解析的实际结果不会是浮点数,而是一个矢量。输入字符串中有两个浮点数。其中一个迷路了。我没有采取任何措施阻止这种情况。

而且(!)下面的代码不会解决您可能要解析的不同类型(列)的混合和匹配问题。我没有采取任何措施使这成为可能。

以下代码只是明确地说明了您提供的输入。它编译,我希望这有助于攀登下一步。 ;)

#include <boost/spirit/home/qi.hpp>
#include <boost/utility/enable_if.hpp>

namespace qi = boost::spirit::qi;

template <typename T>
typename boost::enable_if<boost::is_same<T,double>, qi::double_type>::type
my_NOT_A_trait()
{
    return qi::double_type();
};

template <typename T>
typename boost::enable_if<boost::is_same<T, float>, qi::float_type>::type
my_NOT_A_trait()
{
    return qi::float_type();
};

template <typename Iterator, typename ColumnType>
struct test : qi::grammar<Iterator, ColumnType(), qi::space_type>
{
    qi::rule<Iterator, ColumnType(), qi::space_type> expression;

    test() : test::base_type(expression)
    {
        expression = my_NOT_A_trait<ColumnType>() >> qi::lit("AND") >> my_NOT_A_trait<ColumnType>();
    }

};

template<typename Iterator, typename Skipper, typename Result>
bool parse(Iterator first, Iterator const& last, Skipper const& skipper, Result& result)
{
    return qi::phrase_parse(first, last, test<Iterator,Result>(), qi::space, result);
}

int main()
{
    float result;

    std::string input = "99.0 AND 2.0";
    auto b(input.begin()), e(input.end());

    if (parse(b, e, qi::space, result))
        std::cout << "Success." << std::endl;
    else
        std::cout << "Failure." << std::endl;

    return 0;
}