使用 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;
}
我是 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;
}