boost spirit, boost any and quoted string - compile-time error - c++

I have the following code:
#include <boost/any.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
template <typename Iterator>
struct parser : boost::spirit::qi::grammar<Iterator, boost::any(), boost::spirit::qi::ascii::space_type>
{
parser() : parser::base_type(start)
{
start %= boost::spirit::qi::int_ | boost::spirit::qi::lexeme['"' >> +(boost::spirit::qi::char_ - '"') >> '"']; // example: 0 or "str"
}
boost::spirit::qi::rule<Iterator, boost::any(), boost::spirit::qi::ascii::space_type> start;
};
int main()
{
const std::string input_data("\"str\"");
boost::any var = 0;
auto itr = input_data.begin();
auto end = input_data.end();
parser<decltype(itr)> g;
bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, var);
if (res && itr == end)
{
std::cout << "Parsing succeeded \n";
try
{
std::cout << boost::any_cast<std::string>(var) << '\n';
}
catch (const boost::bad_any_cast& ex)
{
std::cerr << ex.what() << '\n';
}
}
else
{
std::cout << "Parsing failed \n";
}
}
It compiles fine until I add
boost::spirit::qi::lexeme['"' >> +(boost::spirit::qi::char_ - '"') >> '"']
I can't post the errors here and on pastie.org because of the characters amount limitations.
What am I doing wrong? How can I fix it?

Again, why would you want to complicate matters and make it slow by using boost::any?
Anyways, +char_ exposes std::vector<char> and apparently the attribute compatibility rules don't want to decide what to transform this to.
Make it explicit with as_string: Live On Coliru
#include <boost/any.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
template <typename Iterator>
struct parser : boost::spirit::qi::grammar<Iterator, boost::any(), boost::spirit::qi::ascii::space_type>
{
parser() : parser::base_type(start)
{
using namespace boost::spirit::qi;
start %= int_ | as_string [ lexeme['"' >> +(char_ - '"') >> '"'] ]; // example: 0 or "str"
}
boost::spirit::qi::rule<Iterator, boost::any(), boost::spirit::qi::ascii::space_type> start;
};
int main()
{
// const std::string input_data("\"str\"");
const std::string input_data("123");
for (std::string const& input_data : { "\"str\"", "123" })
{
boost::any var = boost::none;
auto itr = input_data.begin();
auto end = input_data.end();
parser<decltype(itr)> g;
bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, var);
if (res && itr == end)
{
std::cout << "Parsing succeeded \n";
try { std::cout << boost::any_cast<int> (var) << '\n'; } catch (const boost::bad_any_cast& ex) { std::cerr << ex.what() << '\n'; }
try { std::cout << boost::any_cast<std::string>(var) << '\n'; } catch (const boost::bad_any_cast& ex) { std::cerr << ex.what() << '\n'; }
}
else
{
std::cout << "Parsing failed \n";
}
}
}

Related

How to parse a string in spirit and use it as return value

I need to parse a key value pair, where key itself is a fixed string lke 'cmd' in the example. Unfortunately qi::lit has no synthesized attribute and qi::char_ parses no fixed string.
Following code does not compile. I would need that result.name == cmd after execution.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType
{
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type> rule = qi::lit("cmd") >> "=" >> qi::int_;
for (std::string const s : {"cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
qi::lit does not expose an attribute. qi::string does:
rule = qi::string("cmd") >> "=" >> qi::int_;
Live On Coliru
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType {
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type>
rule = qi::string("cmd") >> "=" >> qi::int_;
for (std::string const s : { "cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
Prints
"cmd = 1" -> result: cmd=1

Catching overflows with boost::lexical_cast

I want to catch boost::lexicat_cast overflows the same way I can catch boost::numeric_cast overflows. Is it possible?
The first try block below throws a boost::numeric::negative_overflow.
The second block does not throw an exception (isn't this a lexical_cast bug?)
Though unsigned int is used in the example below, I am looking for a method that would work for any integer type.
#include <boost/numeric/conversion/cast.hpp>
#include <boost/lexical_cast.hpp>
int main()
{
unsigned int i;
try
{
int d =-23;
i = boost::numeric_cast<unsigned int>(d);
}
catch (const boost::numeric::bad_numeric_cast& e)
{
std::cout << e.what() << std::endl;
}
std::cout << i << std::endl; // 4294967273
try
{
char c[] = "-23";
i = boost::lexical_cast<unsigned int>(c);
}
catch (const boost::bad_lexical_cast& e)
{
std::cout << e.what() << std::endl;
}
std::cout << i << std::endl; // 4294967273
return 0;
}
You could write what you want using a modicum of Spirit:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <iostream>
template <typename Out, typename In> Out numeric_lexical_cast(In const& range) {
Out value;
{
using namespace boost::spirit::qi;
using std::begin;
using std::end;
if (!parse(begin(range), end(range), auto_ >> eoi, value)) {
struct bad_numeric_lexical_cast : std::domain_error {
bad_numeric_lexical_cast() : std::domain_error("bad_numeric_lexical_cast") {}
};
throw bad_numeric_lexical_cast();
}
}
return value;
}
int main()
{
for (std::string const& input : { "23", "-23" }) try {
std::cout << " == input: " << input << " -> ";
auto i = numeric_lexical_cast<unsigned int>(input);
std::cout << i << std::endl;
} catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
}
Prints
== input: 23 -> 23
== input: -23 -> bad_numeric_lexical_cast

Spirit X3 not throwing expectation failure

From the documentation, it said says that when I use the expect operator, I should get an expectation_failure when the operator fails to match. I want to catch the exception to instruct the user where the erroneous input is. But it seems I get some kind of wrapped exception instead:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::spirit::x3::expectation_failure<__gnu_cxx::__normal_iterator<char*, std::string> > > >'
what(): boost::spirit::x3::expectation_failure
Aborted
My catch statements are:
try {
r = parse(iter, end, wctl_parser::entry, root);
} catch (x3::expectation_failure<char const*> const& x) {
std::cout << "Never runs,";
} catch (x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "me neither" << std::endl;
}
Update: Here is a small program exhibiting the behaviour:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace wccs_parser {
namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;
namespace qi = boost::spirit::qi;
struct AstNullProcess;
struct AstChoiceProcess;
using AstAnyProcess = x3::variant<
x3::forward_ast<AstNullProcess>,
x3::forward_ast<AstChoiceProcess>
>;
struct AstNullProcess {};
struct AstChoiceProcess {
AstAnyProcess left;
AstAnyProcess right;
};
} // End namespace
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChoiceProcess, left, right)
namespace wccs_parser {
template <typename T> auto rule = [](const char* name = typeid(T).name()) {
struct _{};
return x3::rule<_, T> {name};
};
template <typename T> auto as = [](auto p) { return rule<T>() = p; };
auto nullProcess = as<AstNullProcess>(x3::omit['0']);
auto const choiceActual = as<AstChoiceProcess> (nullProcess > '+' > nullProcess);
auto const choice = rule<AstAnyProcess> ("choice")
= nullProcess >> !x3::lit('+')
| choiceActual;
auto const entry = x3::skip(ascii::space) [choice];
} //End namespace
namespace x3 = boost::spirit::x3;
int main() {
std::string str("0 + ");
wccs_parser::AstAnyProcess root;
auto iter = str.begin();
auto end = str.end();
bool r = false;
try {
r = parse(iter, end, wccs_parser::entry, root);
} catch (x3::expectation_failure<char const*> const& x) {
std::cout << "Never runs," << std::endl;
} catch (x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "me neither" << std::endl;
}
if (r) {
std::cout << str << std::endl << std::endl << " Parses OK: " << std::endl;
std::cout << "\n-------------------------\n";
} else {
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
if (iter != end) std::cout << "Partial match" << std::endl;
return 0;
}
If you catch by const_iterator, you will want to make sure const_iterator is what you pass to parse:
std::string const str("0 + ");
See it Live On Coliru
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace wccs_parser {
using boost::spirit::x3::variant;
using boost::spirit::x3::forward_ast;
struct AstNullProcess;
struct AstChoiceProcess;
using AstAnyProcess = variant<
forward_ast<AstNullProcess>,
forward_ast<AstChoiceProcess>
>;
struct AstNullProcess {};
struct AstChoiceProcess {
AstAnyProcess left;
AstAnyProcess right;
};
} // End namespace
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChoiceProcess, left, right)
namespace wccs_parser {
namespace x3 = boost::spirit::x3;
using x3::expectation_failure;
template <typename T> auto rule = [](const char* name = typeid(T).name()) {
struct _{};
return x3::rule<_, T> {name};
};
template <typename T> auto as = [](auto p, const char* name = typeid(T).name()) { return rule<T>(name) = p; };
auto nullProcess = as<AstNullProcess>(x3::omit['0'], "nullProcess");
auto const choiceActual = as<AstChoiceProcess> (nullProcess > '+' > nullProcess, "choiceActual");
auto const choice = rule<AstAnyProcess> ("choice")
= nullProcess >> !x3::lit('+')
| choiceActual;
auto const entry = x3::skip(x3::ascii::space) [choice];
}
int main() {
std::string const str("0 + ");
try {
auto iter = str.cbegin();
auto end = str.cend();
wccs_parser::AstAnyProcess root;
bool const r = parse(iter, end, wccs_parser::entry, root);
if (r) {
std::cout << str << "\n\n Parses OK: \n";
std::cout << "\n-------------------------\n";
} else {
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
if (iter != end) {
std::cout << "Partial match, leaving '" << std::string(iter,end) << "'\n";
}
} catch (wccs_parser::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "Expected: " << e.which() << " at '" << std::string(e.where(), str.end()) << "'\n";
return 1;
}
}
Prints
Expected: nullProcess at ' '

parsing 3 floats for glm::vec3 using boost::spirit::qi (error_invalid_expression)

I can parse one float and print it. (test1, test2)
Somehow I am unable to build a rule that parses three floats.
My final goal is to parse three floats and save them to a glm::vec3.
My rule seems to be incorrect:
qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_]
I am not sure if i am using BOOST_FUSION_ADAPT_STRUCT correctly
Here is a source to show what i came up with so far:
#include <iostream>
#include <string>
#include <glm\glm.hpp>
#include <boost\spirit\include\qi.hpp>
#include <boost\fusion\include\adapt_struct.hpp>
namespace qi = boost::spirit::qi;
void test1()
{
std::string test = "1.2";
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space) && (it == test.end()))
std::cout << "test 1 ok" << std::endl;
else
std::cout << "test 1 not ok" << std::endl;
}
void test2()
{
std::string test = "1.2";
auto it = test.begin();
float f;
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space, f) && (it == test.end()))
std::cout << "test 2 ok " << f << std::endl;
else
std::cout << "test 2 not ok" << std::endl;
}
void test3()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space) && (it == test.end()))
std::cout << "test 3 ok " << std::endl;
else
std::cout << "test 3 not ok" << std::endl;
}
BOOST_FUSION_ADAPT_STRUCT(
glm::vec3,
(float, x)
(float, y)
(float, z)
)
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
int main(int argc, char ** argv)
{
test1();
test2();
test3();
test4();
}
The test4 function contains everything I try to get done.
EDIT:
As suggested two things need to be changed:
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
I agree with Pete Becker. Simpler is usually better.
Now, since you are going to end up using spirit, let's look at what can be simpler:
use c++11 adapt:
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
you can do without the adapt (simpler in the demo)
you can (should) drop the skipper from the rule declaration if you're doing a toplevel lexeme[] directive anyways¹. See stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965
better yet, do without the rule for readability here. In reality, you would have a larger grammar with multiple rules. Be sure not to instantiate the grammar each parse (for efficiency).
Demo testbed Live On Coliru
//#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <glm/glm.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
glm::vec3 simpler(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (parse(it, test.end(),
float_ >> ' ' >> float_ >> ' ' >> float_ >> eoi,
result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
glm::vec3 use_skipper(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(),
float_ >> float_ >> float_ >> eoi,
space, result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
glm::vec3 with_adapt(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(), float_ >> float_ >> float_ >>eoi, space, result))
{
return result;
}
throw std::runtime_error("invalid input");
}
int main() {
struct { glm::vec3 (&f)(std::string const&); std::string name; } tests[] = {
{simpler, "simpler"},
{use_skipper, "use_skipper"},
{with_adapt, "with_adapt"}
};
for (auto t : tests) try {
glm::vec3 v = t.f("1.2 2.2 3.3");
std::cout << t.name << " ok (" << v.x << ", " << v.y << ", " << v.z << ")\n";
} catch(std::exception const& e) {
std::cout << t.name << " fail (" << e.what() << ")\n";
}
}
You can be slightly more lightweight Using x3. Not really less work, but certainly less heavy on the compiler.
¹ subtle difference w.r.t. pre/post skipping, but that difference would be gone if there's a containing grammar with skipper
The sequence parser operator is >>, use that instead of << in your rules
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
You've specified a whitespace skipper in your rule, so it can be further simplified by removing the lexeme directive and letting the skipper skip spaces automatically (unless you want to ensure there is a single space character in between the inputs).
VEC3 = qi::float_ >> qi::float_ >> qi::float_;
Also, if you want your rule to return a value, you need to add that signature to the rule type
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
This is unrelated to the compilation error you're seeing, but use forward slashes in your #include directives, that works on all platforms.
#include <boost/spirit/include/qi.hpp>
Seems like an awful lot of work for something that's straightforward.
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::string test = "1.2 2.2 3.3";
float f1, f2, f3;
std::istringstream in(test);
in >> f1 >> f2 >> f3;
return 0;
}

boost::optional to bool, inside boost::spirit::qi grammar

In my boost::spirit grammar I have the following snippet;
implicit_method_declaration = (-(qi::token(ABSTRACT)) >> ...)
The type of -(qi::token(ABSTRACT) is boost::optional<boost::iterator_range<std::string::iterator>> however I'm only using this construct to check if the abstract keyword, is actually present, that is, I'd rather have -(qi::token(ABSTRACT) have the type bool with the value boost::optional<...> operator bool() const.
How would I go about achieving this?
I think you're looking for qi::matches[]:
implicit_method_declaration =
qi::matches[qi::token(ABSTRACT)] >> ...;
An alternative would be to use qi::attr() with alternatives:
implicit_method_declaration =
(
qi::token(ABSTRACT) >> qi::attr(true)
| qi::attr(false)
) >> ...;
A quick demo again: http://coliru.stacked-crooked.com/a/ed8bbad53e8c1943
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, bool(), Skipper>
{
parser() : parser::base_type(implicit_method_declaration)
{
using namespace qi;
implicit_method_declaration = matches["abstract"];
BOOST_SPIRIT_DEBUG_NODES((implicit_method_declaration));
}
private:
qi::rule<It, bool(), Skipper> implicit_method_declaration;
};
bool doParse(const std::string& input)
{
typedef std::string::const_iterator It;
auto f(begin(input)), l(end(input));
parser<It, qi::space_type> p;
bool data;
try
{
bool ok = qi::phrase_parse(f,l,p,qi::space,data);
if (ok)
{
std::cout << "parse success\n";
std::cout << "data: " << data << "\n";
}
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";
if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
return ok;
} catch(const qi::expectation_failure<It>& e)
{
std::string frag(e.first, e.last);
std::cerr << e.what() << "'" << frag << "'\n";
}
return false;
}
int main()
{
doParse("abstract");
doParse("static final");
}
Output
parse success
data: 1
parse success
data: 0
trailing unparsed: 'static final'