Boost Spirit X3 cannot compile repeat directive with variable factor
From what I gather, reading the source and the mailing list, Phoenix is not integrated into X3 at all: the reason being that c++14 makes most of it obsolete.
I agree that this leaves a few spots where Qi used to have elegant solutions, e.g. eps(DEFERRED_CONDITION)
, lazy(*RULE_PTR)
(the Nabialek trick), and indeed, this case.
Spirit X3 is still in development, so we might see this added¹
For now, Spirit X3 has one generalized facility for stateful context. This essentially replaces locals<>
, in some cases inherited arguments, and can be /made to/ validate the number of elements in this particular case as well:
-
x3::with
²
Here's how you could use it:
with<_n>(std::ref(n))
[ omit[uint_[number] ] >>
*(eps [more] >> int_) >> eps [done] ]
Here, _n
is a tag type that identifies the context element for retrieval with get<_n>(cxtx)
.
Note, currently we have to use a reference-wrapper to an lvalue
n
becausewith<_n>(0u)
would result in constant element inside the context. I suppose this, too, is a QoI that may be lifted as X# matures
Now, for the semantic actions:
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
This stores the parsed unsigned number into the context. (In fact, due to the ref(n)
binding it's not actually part of the context for now, as mentioned)
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
Here we check that we're actually not "full" - i.e. more integers are allowed
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
Here we check that we're "full" - i.e. no more integers are allowed.
Putting it all together:
Live On Coliru
#include <string>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
int main() {
for (std::string const input : {
"3 1 2 3", // correct
"4 1 2 3", // too few
"2 1 2 3", // too many
//
" 3 1 2 3 ",
})
{
std::cout << "\nParsing " << std::left << std::setw(20) << ("'" + input + "':");
std::vector<int> v;
bool ok;
{
using namespace boost::spirit::x3;
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
auto r = rule<struct _r, std::vector<int> > {}
%= with<_n>(std::ref(n))
[ omit[uint_[number] ] >> *(eps [more] >> int_) >> eps [done] ];
ok = phrase_parse(input.begin(), input.end(), r >> eoi, space, v);
}
if (ok) {
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout << v.size() << " elements: ", " "));
} else {
std::cout << "Parse failed";
}
}
}
Which prints:
Parsing '3 1 2 3': 3 elements: 1 2 3
Parsing '4 1 2 3': Parse failed
Parsing '2 1 2 3': Parse failed
Parsing ' 3 1 2 3 ': 3 elements: 1 2 3
¹ lend your support/voice at the [spirit-general] mailing list :)
² can't find a suitable documentation link, but it's used in some of the samples