I was toying with Boost.Spirit X3 calculator example when I encountered an error I couldn\'t get my head around.
I minimized the program to reduce complexity still
Yup. This looks like another limitation with automatic propagation of attributes when single-element sequences are involved.
I'd probably bite the bullet and change the rule definition from what it is (and what you'd expect to work) to:
x3::rule<class program_, std::vector<std::string> >
That removes the root of the confusion.
Other notes:
you had char_
which also eats ';'
so the grammar would never succeed because no ';'
would follow a "statement".
your statements aren't lexeme, so whitespace is discarded (is this what you meant? See Boost spirit skipper issues)
your statement could be empty, which meant parsing would ALWAYS fail at the end of input (where it would always read an empty state, and then discover that the expected ';'
was missing). Fix it by requiring at least 1 character before accepting a statement.
With some simplifications/style changes:
Live On Coliru
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <list>
namespace x3 = boost::spirit::x3;
namespace ast {
using statement = std::string;
struct program {
std::list<statement> stmts;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::program, stmts)
namespace grammar {
auto statement
= x3::rule<class statement_, ast::statement> {"statement"}
= +~x3::char_(';');
auto program
= x3::rule<class program_, std::list<ast::statement> > {"program"}
= *(statement >> ';');
}
#include <iostream>
#include <iomanip>
int main() {
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using It = std::string::const_iterator;
for (std::string str; std::getline(std::cin, str);) {
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
auto &parser = grammar::program;
ast::program program; // Our program (AST)
It iter = str.begin(), end = str.end();
if (phrase_parse(iter, end, parser, x3::space, program)) {
std::cout << "Parsing succeeded\n";
for (auto& s : program.stmts) {
std::cout << "Statement: " << std::quoted(s, '\'') << "\n";
}
}
else
std::cout << "Parsing failed\n";
if (iter != end)
std::cout << "Remaining unparsed: " << std::quoted(std::string(iter, end), '\'') << "\n";
}
}
Which for input "a;b;c;d;" prints:
Parsing succeeded
Statement: 'a'
Statement: 'b'
Statement: 'c'
Statement: 'd'