I am newbie in C++, althouth I need to parser a SQL-like expression: country='USA' AND state='CA' AND price >= 100.0, it is just a fake example, but it's feasible.
So, I tried to solve using spirit QI. Each column has a specific type: float, integer, double, char and string. I wanna create a reusable grammar to support those column types with template or/and trait, but I'm blocked.
I want something like that:
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;
};
I tried to use the Signature template parameter, but didn't work.
MyTrait.h: using namespace 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;
};
The basic idea in MyTrait is just to convert primitive types (double, float...) to spirit QI types, in order to use them in the grammar. Note that MyTrait<\double>::type results in double_type from QI, but in the grammar must be 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;
}
Am I on the right track?
Follow compiler error messages as required by 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
As far as I understand you want to automatically hardwire a particular parser to a given result_type. Your going to give up much of qi's flexibility in doing so. I recommend reviewing your approach at all.
The code sample below does what I understood so far what you want to achieve.
But (!) the actual result of the parsing will not be float or what but a vector. There are two floats in your input string. One of that gets lost. I did not do anything to prevent that.
And (!) the code below does not address mix and match of different types (columns) you might want to parse. I did not do anything to make that possible.
The code below just plainly adresses the input you gave. It compiles and I hope this helps to climb the next step. ;)
#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;
}