Templating Spirit X3 parser - c++

In Boost Spirit QI it was easy to template the parser so that it could be instantiated for various attribute types. It is unclear to me how to do this with X3. Consider this stripped down version of the roman numerals parser example:
#include <iostream>
#include <iterator>
#include <string>
#include <boost/spirit/home/x3.hpp>
namespace parser {
namespace x3 = boost::spirit::x3;
struct numbers_ : x3::symbols<unsigned> {
numbers_() {
add
("I" , 1)
("II" , 2)
("III" , 3)
("IV" , 4)
("V" , 5)
("VI" , 6)
("VII" , 7)
("VIII" , 8)
("IX" , 9)
;
}
} numbers;
x3::rule<class roman, unsigned> const roman = "roman";
auto init = [](auto &x) { x3::_val(x) = 0; };
auto add = [](auto &x) { x3::_val(x) += x3::_attr(x); };
auto const roman_def = x3::eps [init] >> numbers [add];
BOOST_SPIRIT_DEFINE(roman);
}
int main()
{
std::string input = "V";
auto iter = input.begin();
auto end = input.end();
unsigned result;
bool r = parse(iter, end, parser::roman, result);
if (r && iter == end) {
std::cout << "Success :) Result = " << result << '\n';
} else {
std::cout << "Failed :(\n";
}
}
I'd like to template the parser on the attribute type which is currently hardcoded as unsigned. My first guess was to replace
namespace parser {
// ...
}
with
template < typename int_t >
struct parser {
// ...
};
which is obviously too naïve. How to do this correctly?

In X3 there's not so much pain in combining parsers dynamically. So I'd write your sample as:
template <typename Attribute>
auto make_roman() {
using namespace boost::spirit::x3;
struct numbers_ : symbols<Attribute> {
numbers_() { this-> add
("I", Attribute{1}) ("II", Attribute{2}) ("III", Attribute{3}) ("IV", Attribute{4})
("V", Attribute{5}) ("VI", Attribute{6}) ("VII", Attribute{7}) ("VIII", Attribute{8})
("IX", Attribute{9}) ;
}
} numbers;
return rule<class roman, Attribute> {"roman"} =
eps [([](auto &x) { _val(x) = 0; })]
>> numbers [([](auto &x) { _val(x) += _attr(x); })];
}
See it Live On Coliru

Related

Parsing characters into an std::map<char,int> using boost::qi

I am trying to parse a sequence of characters separated by a "," into an std::map<char,int> of pairs where the key is the character and the value just the a count of parsed characters.
For example, if the input is
a,b,c
The map should contain the pairs:
(a,1) , (b,2) , (c,3)
Here's the code I am using :
namespace myparser
{
std::map<int, std::string> mapping;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
int i = 0;
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, std::map<char,int>& v)
{
using qi::double_;
using qi::char_;
using qi::phrase_parse;
using qi::_1;
using ascii::space;
using phoenix::push_back;
bool r = phrase_parse(first, last,
// Begin grammar
(
char_[v.insert(std::make_pair(_1,0)]
>> *(',' >> char_[v.insert(std::make_pair(_1,0)])
)
,
// End grammar
space);
if (first != last) // fail if we did not get a full match
return false;
return r;
}
//]
}
Then I try to print the pair in main like this:
int main() {
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::map<char,int> v;
std::map<std::string, int>::iterator it = v.begin();
if (myparser::parse_numbers(str.begin(), str.end(), v))
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << str << " Parses OK: " << std::endl;
while (it != v.end())
{
// Accessing KEY from element pointed by it.
std::string word = it->first;
// Accessing VALUE from element pointed by it.
int count = it->second;
std::cout << word << " :: " << count << std::endl;
// Increment the Iterator to point to next entry
it++;
}
std::cout << "\n-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
return 0;
}
I am a beginner and I don't know how to fix this code . I also want to use strings instead of characters so I enter a sequence of strings separated by a "," and store them in a map similar to the one mentioned above. I would appreciate any help !
You cannot use Phoenix place holders outside Phoenix deferred actors. E.g. the type of std::make_pair(qi::_1, 0) is std::pair<boost::phoenix::actor<boost::phoenix::argument<0>>, int>.
Nothing interoperates with such a thing. Certainly not std::map<>::insert.
What you'd need to do is wrap all the operations in semantic actions as Phoenix actors.
#include <boost/phoenix.hpp>
namespace px = boost::phoenix;
Then you can:
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace myparser {
using Map = std::map<char, int>;
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
auto action = px::insert(px::ref(m), px::end(px::ref(m)),
px::construct<std::pair<char, int>>(qi::_1, 0));
bool r = qi::phrase_parse( //
first, last,
// Begin grammar
qi::char_[action] >> *(',' >> qi::char_[action]),
// End grammar
qi::space);
return r && first == last;
}
} // namespace myparser
See it Live
Easy peasy. Right.
I spent half an hour on that thing debugging why it wouldn't work. Why is this so hard?
It's because someone invented a whole meta-DSL to write "normal C++" but with defferred execution. Back when that happened it was pretty neat, but it is the mother of all leaky abstractions, with razor sharp edges.
So, what's new? Using C++11 you could:
Live
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
struct action_f {
Map& m_;
void operator()(char ch) const { m_.emplace(ch, 0); }
};
px::function<action_f> action{{m}};
bool r = qi::phrase_parse( //
first, last,
// Begin grammar
qi::char_[action(qi::_1)] >> *(',' >> qi::char_[action(qi::_1)]),
// End grammar
qi::space);
return r && first == last;
}
Or using c++17:
Live
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
px::function action{[&m](char ch) { m.emplace(ch, 0); }};
bool r = qi::phrase_parse( //
first, last,
// Begin grammar
qi::char_[action(qi::_1)] >> *(',' >> qi::char_[action(qi::_1)]),
// End grammar
qi::space);
return r && first == last;
}
On a tangent, you probably wanted to count things, so, maybe use
Live
px::function action{[&m](char ch) { m[ch] += 1; }};
By this time, you could switch to Spirit X3 (which requires C++14):
Live
#include <boost/spirit/home/x3.hpp>
#include <map>
namespace x3 = boost::spirit::x3;
namespace myparser {
using Map = std::map<char, int>;
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
auto action = [&m](auto& ctx) { m[_attr(ctx)] += 1; };
return x3::phrase_parse( //
first, last,
// Begin grammar
x3::char_[action] >> *(',' >> x3::char_[action]) >> x3::eoi,
// End grammar
x3::space);
}
} // namespace myparser
Now finally, let's simplify. p >> *(',' >> p) is just a clumsy way of saying p % ',':
Live
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
auto action = [&m](auto& ctx) { m[_attr(ctx)] += 1; };
return x3::phrase_parse( //
first, last, //
x3::char_[action] % ',', //
x3::space);
}
And you wanted words, not characters:
Live
#include <boost/spirit/home/x3.hpp>
#include <map>
namespace x3 = boost::spirit::x3;
namespace myparser {
using Map = std::map<std::string, int>;
template <typename Iterator>
bool parse_numbers(Iterator first, Iterator last, Map& m) {
auto action = [&m](auto& ctx) { m[_attr(ctx)] += 1; };
auto word_ = (*~x3::char_(','))[action];
return phrase_parse(first, last, word_ % ',', x3::space);
}
} // namespace myparser
#include <iomanip>
#include <iostream>
int main() {
for (std::string const str : {"foo,c++ is strange,bar,qux,foo,c++ is strange ,cuz"}) {
std::map<std::string, int> m;
std::cout << "Parsing " << std::quoted(str) << std::endl;
if (myparser::parse_numbers(str.begin(), str.end(), m)) {
std::cout << m.size() << " words:\n";
for (auto& [word,count]: m)
std::cout << " - " << std::quoted(word) << " :: " << count << std::endl;
} else {
std::cerr << "Parsing failed\n";
}
}
}
Prints
Parsing "foo,c++ is strange,bar,qux,foo,c++ is strange ,cuz"
5 words:
- "bar" :: 1
- "c++isstrange" :: 2
- "cuz" :: 1
- "foo" :: 2
- "qux" :: 1
Note the behaviour of the x3::space (like qi::space and qi::ascii::space above).

boost spirit x3 variant and std::pair

I tried to run some simple parser that will parse [ 1, 11, 3, 6-4]. Basically, integer list with range notation.
I want to put everything into AST without semantic action. So I use x3::variant. My code 'seems' very similar to the expression example. However, it can't compile under g++ 6.2. It indeed compile ok with clang++ 6.0 but yield wrong result.
The boost version is 1.63.
It seems that I have some 'move' or initialization issue.
#include <iostream>
#include <list>
#include <vector>
#include <utility>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/io.hpp>
namespace ns
{
namespace ast
{
namespace x3 = boost::spirit::x3;
// forward definition
class uintObj;
struct varVec;
// define type
using uintPair_t = std::pair<unsigned int, unsigned int>;
using uintVec_t = std::vector<uintObj>;
// general token value:
class uintObj : public x3::variant <
unsigned int,
uintPair_t
>
{
public:
using base_type::base_type;
using base_type::operator=;
};
struct varVec
{
uintVec_t valVector;
};
}
}
BOOST_FUSION_ADAPT_STRUCT(
ns::ast::varVec,
valVector
)
namespace ns
{
namespace parser
{
// namespace x3 = boost::spirit::x3;
// using namespace x3;
using namespace boost::spirit::x3;
// definition of the range pair:
rule<class uintPair, ast::uintPair_t> const uintPair = "uintPair";
auto const uintPair_def =
uint_
>> '-'
>> uint_
;
rule<class uintObj, ast::uintObj> const uintObj = "uintObj";
auto const uintObj_def =
uint_
| uintPair
;
// define rule definition : rule<ID, attrib>
// more terse definition :
// struct varVec_class;
// using varVec_rule_t = x3::rule<varVec_class, ast::varVec>;
// varVec_rule_t const varVec = "varVec";
// varVec is the rule, "varVec" is the string name of the rule.
rule<class varVec, ast::varVec> const varVec = "varVec";
auto const varVec_def =
'['
>> uintObj % ','
>> ']'
;
BOOST_SPIRIT_DEFINE(
varVec,
uintObj,
uintPair
);
}
}
int main()
{
std::string input ("[1, 11, 3, 6-4]\n");
std::string::const_iterator begin = input.begin();
std::string::const_iterator end = input.end();
ns::ast::varVec result; // ast tree
using ns::parser::varVec; // grammar
using boost::spirit::x3::ascii::space;
bool success = phrase_parse(begin, end, varVec, space, result);
if (success && begin == end)
std::cout << "good" << std::endl;
else
std::cout << "bad" << std::endl;
return 0;
}
Swap the alternative order for the uintObj_def
auto const uintObj_def =
uintPair
| uint_
;
The formulation you have now will always match on a uint_ because the uintPair begins with a valid uint_.
mjcaisse's answer calls out the main problem I think you had. There were a few missing pieces, so I decided to make a simplified version that shows parsing results:
Live On Wandbox
#include <iostream>
#include <iomanip>
//#include <boost/fusion/adapted.hpp>
//#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
namespace x3 = boost::spirit::x3;
namespace ns { namespace ast {
// forward definition
struct uintObj;
//struct varVec;
// define type
using uintPair_t = std::pair<unsigned int, unsigned int>;
using uintVec_t = std::vector<uintObj>;
// general token value:
struct uintObj : x3::variant<unsigned int, uintPair_t> {
using base_type::base_type;
using base_type::operator=;
friend std::ostream& operator<<(std::ostream& os, uintObj const& This) {
struct {
std::ostream& os;
void operator()(unsigned int v) const { os << v; }
void operator()(uintPair_t v) const { os << v.first << "-" << v.second; }
} vis { os };
boost::apply_visitor(vis, This);
return os;
}
};
using varVec = uintVec_t;
} }
namespace ns { namespace parser {
using namespace boost::spirit::x3;
template <typename T> auto as = [](auto p) { return rule<struct _, T> {} = p; };
auto const uintPair = as<ast::uintPair_t> ( uint_ >> '-' >> uint_ );
auto const uintObj = as<ast::uintObj> ( uintPair | uint_ );
auto const varVec = as<ast::varVec> ( '[' >> uintObj % ',' >> ']' );
} }
int main() {
using namespace ns;
std::string const input("[1, 11, 3, 6-4]\n");
auto begin = input.begin(), end = input.end();
ast::varVec result; // ast tree
bool success = phrase_parse(begin, end, parser::varVec, x3::ascii::space, result);
if (success) {
std::cout << "good\n";
for (auto& r : result)
std::cout << r << "\n";
}
else
std::cout << "bad\n";
if (begin != end)
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
Prints
good
1
11
3
6-4

boost::spirit::qi strange attribute print out

#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace test {
template< typename Rng,typename Expr >
bool parse(Rng const& rng,Expr const& expr) noexcept {
auto itBegin = boost::begin(rng);
auto itEnd = boost::end(rng);
try {
return qi::parse(itBegin,itEnd,expr);
} catch(qi::expectation_failure<decltype(itBegin)> const& exfail) {
exfail;
return false;
}
}
template< typename Rng,typename Expr,typename Attr >
bool parse(Rng const& rng,Expr const& expr,Attr& attr) noexcept {
auto itBegin = boost::begin(rng);
auto itEnd = boost::end(rng);
try {
return qi::parse(itBegin,itEnd,expr,attr);
} catch(qi::expectation_failure<decltype(itBegin)> const&) {
return false;
}
}
}
void print1(std::string const& s) {
std::cout<<"n1 = "<<s<<std::endl;
}
void print2(std::string const& s) {
std::cout<<"n2 = "<<s<<std::endl;
}
int main() {
qi::rule<std::string::const_iterator, std::string()> number = +qi::digit;
std::string input = "1+2";
std::string result;
if( test::parse(input, number[print1] >> *( qi::char_("+-") >> number[print2]) >> qi::eoi, result) ) {
std::cout<<"Match! result = "<<result<<std::endl;
} else {
std::cout<<"Not match!"<<std::endl;
}
return 0;
}
I'm expecting the output of this program is,
n1 = 1
n2 = 2
Match! result = 1+2
But the output is actually very strange for n2,
n1 = 1
n2 = 1+2
Match! result = 1+2
Why is the second number's attribute "1+2" instead of just "2"?
I know there are some other ways to parse this expression such as using qi::int_. I'm just wondering why do I get this strange attribute from it. Thanks!
Backtracking doesn't undo changes to container attributes. Adjacent compatible parser expressions bind to the same container attribute.
Both attributes get bound to the same container attribute (result) which means you are printing the same variable twice.
If you didn't want that, be explicit, e.g.
Live On Coliru
std::string result;
std::vector<std::string> v;
if( test::parse(input, number[px::bind(print1, qi::_1)] >> *qi::as_string[qi::char_("+-") >> number[print2]] >> qi::eoi, result, v) ) {
std::cout<<"Match! result = "<<result<<std::endl;
for (auto s : v)
std::cout << s << "\n";
} else {
Prints
n1 = 1
n2 = +2
Match! result = 1
+2
Now I don't know what you want to achieve, but this could be close:
if (test::parse(input, qi::raw [number[print_("n1",_1)] > *(qi::char_("+-") >> number[print_("n2",_1)]) ] >> qi::eoi, result)) {
Live On Coliru
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace test {
template< typename Rng,typename Expr,typename... Attr >
bool parse(Rng const& rng,Expr const& expr,Attr&... attr) noexcept {
auto itBegin = boost::begin(rng);
auto itEnd = boost::end(rng);
try {
return qi::parse(itBegin,itEnd,expr,attr...);
} catch(qi::expectation_failure<decltype(itBegin)> const&) {
return false;
}
}
}
void printn(std::string const& label, std::string const& s) {
std::cout << label << " = " << s << std::endl;
}
BOOST_PHOENIX_ADAPT_FUNCTION(void, print_, printn, 2)
int main() {
qi::rule<std::string::const_iterator, std::string()> number = +qi::digit;
std::string input = "1+2";
std::string result;
using qi::_1;
if (test::parse(input, qi::raw [number[print_("n1",_1)] > *(qi::char_("+-") >> number[print_("n2",_1)]) ] >> qi::eoi, result)) {
std::cout<<"Match! result = "<<result<<std::endl;
} else {
std::cout<<"Not match!"<<std::endl;
}
return 0;
}
Prints
n1 = 1
n2 = 2
Match! result = 1+2
BONUS
A little more separation of concern:
Live On Coliru
void printn(int n, std::string const& s) {
std::cout << "n" << n << " = " << s << std::endl;
}
BOOST_PHOENIX_ADAPT_FUNCTION(void, print_, printn, 2)
int main() {
qi::rule<std::string::const_iterator, std::string()> number;
int n = 1;
number %= qi::as_string[+qi::digit] [print_(px::ref(n)++, qi::_1)];
std::string input = "1+2";
std::string result;
if (test::parse(input, qi::raw [number > *(qi::char_("+-") >> number) ] >> qi::eoi, result)) {
std::cout << "Match! result = " << result << std::endl;
} else {
std::cout << "Not match!" << std::endl;
}
return 0;
}

Boost Spirit X3 and std::unordered_map

I want to parse into a std::unordered_map.
Example Code:
struct Base
{
int item1;
int item2;
};
BOOST_FUSION_ADAPT_STRUCT(Base, item1, item2)
namespace grammar
{
using namespace boost::spirit::x3;
auto base_ = rule<struct base_, Base>{"base"}
= repeat(2)[ int_ ];
auto start = rule<struct start, std::unordered_map<std::int32_t, Base>>{"start"}
= (id_ >> base_) % eol;
}
With the following main:
namespace ios = boost::iostreams;
namespace fs = boost::filesystem;
namespace x3 = boost::spirit::x3;
int main()
{
std::unordered_map<std::int32_t, Base> bases;
ios::mapped_file mmf("example.dat");
auto beg = std::begin(mmf);
auto end = std::end(mmf);
auto ret = x3::phrase_parse(beg, end, grammar::start, x3::char_(','), bases);
if (ret && beg == end)
{
std::cout << "Parse ok\n";
}
mmf.close();
return 0;
}
and one example file:
1,2,3
2,3,4
3,4,5
The compiler error message is:
.... ‘class std::unordered_map<int, Base>’ has no member named ‘push_back’
What to do next, is there a way da adapt std::unordered_map?
Likee jv_ supposed, updating my boost version fixed the problem.

boost qi attribute is coming up as qi::unused_type

Cannot figure out why this rule unary_msg doesnt work, it says the attribute type is qi::unused_type but this makes no sense to me. Why does boost torment me like this?
template<class It, class Skip= boost::spirit::ascii::space_type>
struct g3: qi::grammar<It, ast::expr(), Skip>
{
template<typename...Args>
using R = qi::rule<It, Args...>;
R<ast::expr(), Skip> start, expr_, term_, unary_term;
R<ast::intlit()> int_;
R<std::string()> selector_;
R<boost::fusion::vector<ast::expr, std::vector<std::string>>, Skip> unary_msg;
g3(): g3::base_type(start)
{
namespace ph = boost::phoenix;
using namespace boost::spirit::qi;
int_ = qi::int_;
selector_ = lexeme[+qi::alnum];
term_ = int_;
unary_msg = term_ >> *selector_;
unary_term = unary_msg[ qi::_val = ph::bind(&collect_unary, qi::_1) ];
expr_ = unary_term;
start = expr_;
}
};
full code: http://coliru.stacked-crooked.com/a/e9afef4585ce76c3
Like cv_and_he mentions, add the parens.
Working example with many cleanup suggestions:
Live On Coliru
Notes
don't use using namespace at toplevel
don't use conflicting namespaces (using std and boost are very likely to lead to surprises or conflicts)
don't use internal attribute types like fusion::vector
use modern style BOOST_FUSION_ADAPT_STRUCT
some minor style issues
For example the following function
ast::expr collect_unary (const boost::fusion::vector<ast::expr, std::vector<std::string>>& parts)
//ast::expr collect_unary (const ast::expr& a, const std::vector<std::string>& msgs)
{
ast::expr res = boost::fusion::at_c<0>(parts);//a;
const auto& msgs = boost::fusion::at_c<1>(parts);
for(const auto& m: msgs)
{
ast::message msg;
msg.name = m;
msg.args.push_back(res);
res = msg;
}
return res;
}
was changed into:
ast::expr collect_unary(ast::expr accum, const std::vector<std::string>& msgs) {
for (const auto &m : msgs)
accum = ast::message { m, { accum } };
return accum;
}
Full Listing And Output
Live On Coliru
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace ast {
struct intlit {
int value;
intlit(int i = 0) : value(i) { }
intlit(intlit const&other) = default;
};
struct nil {};
struct message;
using expr = boost::make_recursive_variant<nil, intlit, message>::type;
struct message {
std::string name;
std::vector<ast::expr> args;
};
}
#include <boost/fusion/include/adapt_struct.hpp>
BOOST_FUSION_ADAPT_STRUCT(ast::intlit, value)
BOOST_FUSION_ADAPT_STRUCT(ast::message, name, args)
struct ast_print {
void operator()(ast::nil &) const { std::cout << "nil"; }
void operator()(ast::intlit &i) const { std::cout << i.value; }
void operator()(ast::message &m) const {
std::cout << "(" << m.name;
for (auto &it : m.args) {
std::cout << " ";
boost::apply_visitor(ast_print(), it);
}
std::cout << ")" << std::endl;
}
};
ast::expr collect_unary(ast::expr accum, const std::vector<std::string>& msgs)
{
for (const auto &m : msgs)
accum = ast::message { m, { accum } };
return accum;
}
template <class It, class Skip = boost::spirit::ascii::space_type> struct g3 : qi::grammar<It, ast::expr(), Skip> {
g3() : g3::base_type(start) {
using namespace boost::spirit::qi;
namespace ph = boost::phoenix;
int_ = qi::int_;
selector_ = +qi::alnum;
term_ = int_;
unary_msg = (term_ >> *selector_) [ _val = ph::bind(collect_unary, _1, _2) ];
unary_term = unary_msg;
expr_ = unary_term;
start = expr_;
}
private:
template <typename Attr, typename... Args> using R = qi::rule<It, Attr(), Args...>;
R<ast::expr, Skip> start, expr_, term_, unary_term, unary_msg;
R<ast::intlit> int_;
R<std::string> selector_;
};
template <class Parser, typename Result> bool test(const std::string &input, const Parser &parser, Result &result) {
auto first = input.begin(), last = input.end();
return qi::phrase_parse(first, last, parser, boost::spirit::ascii::space, result);
}
int main() {
std::string const input = "42 x y";
g3<std::string::const_iterator> p;
ast::expr res;
if (test(input, p, res)) {
std::cout << "parse ok " << std::endl;
boost::apply_visitor(ast_print(), res);
}
}
Prints
parse ok
(y (x 42)
)