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

Creating a reusable grammar with Spirit QI

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

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

我想要这样的东西:

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...)转换为 spirit QI 类型,以便在语法中使用它们。请注意 MyTrait<\double>::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

据我了解,您想自动将特定的解析器硬连接到给定的 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;
}