I have a Qi grammar definition that I use to parse an input. Later I have a Karma generator to output in a way that should be similar to the input.
Is this possible at all? It seem that a parser grammar can be transformed into a generator grammar automatically (??).
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <iostream>
int main(){
//test input
std::string s = "Xx 1.233 pseudo";
//input variables
std::string element;
double mass;
std::string pseudo;
auto GRAMMAR =
boost::spirit::qi::lexeme[+(boost::spirit::qi::char_ - ' ' - '\n')]
>> boost::spirit::qi::double_
>> boost::spirit::qi::lexeme[+(boost::spirit::qi::char_ - ' ' - '\n')];
bool r = boost::spirit::qi::phrase_parse(
s.begin(), s.end(),
GRAMMAR,
boost::spirit::qi::space, element, mass, pseudo
);
std::cout << boost::spirit::karma::format(
GRAMMAR ??? is it possible?
,
element,
mass,
pseudo
);
}
Sadly it's not possible to achieve what you want in a general way (or at least I don't know how), but if you are willing to just use a limited subset of Spirit.Qi the approach below could work.
The first thing to know is that when you use something like:
You just have a Boost.Proto expression that describes several terminals and how they are related. That expression by itself doesn't "know" anything about how to parse one int and then one double. Whenever you use
parse
/phrase_parse
or assign one of these Proto expressions to arule
Spirit "compiles" that expression for a domain (Qi or Karma) and creates the parsers/generators that do the actual work.Here you can see a small example that shows the exact types of the Proto and compiled Qi expressions:
As long as you have access to the original expression you can use Proto transforms/grammars to convert it to a suitable Karma expression.
In the example below I have used the following transformations:
In order to achieve this transformations you can use
boost::proto::or_
getting something similar to:I'll try to explain how this works.
MatcherN
in the example below can be:proto::terminal<boost::spirit::tag::omit>
: matches only that specific terminal.proto::terminal<proto::_>
: matches any terminal not specifically matched before.proto::subscript<proto::terminal<boost::spirit::tag::omit>,proto::_>
: matchesomit[expr]
whereexpr
can be anything.proto::shift_right<ToKarma,ToKarma>
: matchesexpr1 >> expr2
whereexpr1
andexpr2
must recursively conform to theToKarma
grammar.proto::nary_expr<proto::_,proto::vararg<ToKarma> >
: matches any n-ary (unary, binary or actually n-ary like a function calla(b,c,d,e)
) where each one of the elements of the expression conforms to the ToKarma grammar.All the
TransformN
in this example are expression builders, here are some explanations:_make_terminal(boost::spirit::tag::lexeme())
: builds aproto::terminal<boost::spirit::tag::lexeme>
(note that it is necessary to add()
after the tag, you'll get an awful error if you forget them)._make_subscript(_make_terminal(tag::no_delimit()), _make_terminal(tag::eps()))
: builds aproto::subscript<proto::terminal<tag::no_delimit>, proto::terminal<tag::eps> >
, or the equivalent tono_delimit[eps]
._make_shift_left(ToKarma(proto::_left), ToKarma(proto::_right))
:proto::_left
means take the lhs of the original expression.ToKarma(proto::_left)
means recursively apply the ToKarma grammar/transform to the lhs of the original expression. The whole_make_shift_left
basically buildstransformed_lhs << transformed_rhs
.A
MatcherN
by itself (not insideproto::when
) is a shorthand for build an expression of the same type using as elements the result of recursively applying the transform to the original elements.Full Sample (Running on WandBox)
PS:
Things that definitely won't work:
qi::rule
qi::grammar
qi::symbols
Things that don't exist in Karma:
qi::attr
qi::matches
qi::hold
^
||
Things that have different semantics in Karma:
qi::skip
&
!