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 added1
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:
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
because with<_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 << "
Parsing " << 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
1 lend your support/voice at the [spirit-general] mailing list :)
2 can't find a suitable documentation link, but it's used in some of the samples