悲しいことに、あなたが望むものを一般的な方法で達成することはできません (または、少なくとも方法はわかりません) が、Spirit.Qi の限られたサブセットのみを使用したい場合は、以下のアプローチでうまくいく可能性があります。
最初に知っておくべきことは、次のようなものを使用する場合です。
int_ >> double_
いくつかの端末とそれらがどのように関連しているかを説明する Boost.Proto 式があります。その式自体は、1 つの int と 1 つの double を解析する方法について何も「認識」していません。これらの Proto 式の 1 つをスピリットに使用parse
またはphrase_parse
割り当てるrule
と、ドメイン (Qi または Karma) の式が「コンパイル」され、実際の作業を行うパーサー/ジェネレーターが作成されます。
ここでは、Proto 式とコンパイルされた Qi 式の正確な型を示す小さな例を見ることができます。
Raw proto type:
boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::spirit::terminal<boost::spirit::tag::int_> const&, boost::spirit::terminal<boost::spirit::tag::double_> const&>, 2l>
"Pretty" proto type:
shift_right(
terminal(boost::spirit::tag::int_)
, terminal(boost::spirit::tag::double_)
)
Compiled Qi type:
boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::any_int_parser<int, 10u, 1u, -1>, boost::fusion::cons<boost::spirit::qi::any_real_parser<double, boost::spirit::qi::real_policies<double> >, boost::fusion::nil_> > >
元の式にアクセスできる限り、Proto 変換/文法を使用して適切な Karma 式に変換できます。
以下の例では、次の変換を使用しています。
Qi |Karma |Reason
------------|---------------|------
lexeme[expr]|verbatim[expr] | lexeme does not exist in Karma
omit[expr] |no_delimit[eps]| omit consumes an attribute in Karma
a >> b |a << b |
a > b |a << b | < does not exist in Karma
a - b |a | - does not exist in Karma
この変換を実現するために、次のboost::proto::or_
ようなものを取得できます。
struct Grammar : proto::or_<
proto::when<Matcher1,Transform1>,
proto::when<Matcher2,Transform2>,
Matcher3,
Matcher4
>{};
これがどのように機能するかを説明しようと思います。
MatcherN
以下の例では、次のようになります。
proto::terminal<boost::spirit::tag::omit>
: その特定の端末のみに一致します。
proto::terminal<proto::_>
: 以前に明確に一致しなかった任意の端末に一致します。
proto::subscript<proto::terminal<boost::spirit::tag::omit>,proto::_>
omit[expr]
: 任意の場所に一致しexpr
ます。
proto::shift_right<ToKarma,ToKarma>
: expr1 >> expr2
whereexpr1
に一致し、文法expr2
に再帰的に準拠する必要があります。ToKarma
proto::nary_expr<proto::_,proto::vararg<ToKarma> >
a(b,c,d,e)
:式の各要素が ToKarma 文法に準拠している任意の n 項 (単項、バイナリ、または実際には関数呼び出しのような n 項) に一致します。
この例のすべてTransformN
は式ビルダーです。いくつかの説明を次に示します。
_make_terminal(boost::spirit::tag::lexeme())
: をビルドしますproto::terminal<boost::spirit::tag::lexeme>
(タグの後に追加する必要があることに注意して()
ください。忘れるとひどいエラーが発生します)。
_make_subscript(_make_terminal(tag::no_delimit()), _make_terminal(tag::eps()))
proto::subscript<proto::terminal<tag::no_delimit>, proto::terminal<tag::eps> >
: 、または と同等のものを構築しno_delimit[eps]
ます。
_make_shift_left(ToKarma(proto::_left), ToKarma(proto::_right))
:proto::_left
元の式の左辺を取ることを意味します。ToKarma(proto::_left)
元の式の左辺に ToKarma 文法/変換を再帰的に適用することを意味します。全体が_make_shift_left
基本的に構築されtransformed_lhs << transformed_rhs
ます。
AMatcherN
自体 ( 内ではないproto::when
) は、変換を元の要素に再帰的に適用した結果を要素として使用して、同じ型の式を構築するための省略形です。
完全なサンプル(WandBox で実行)
#include <iostream>
#include <string>
#include <tuple>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/std_tuple.hpp>
namespace proto= boost::proto;
struct ToKarma: proto::or_<
//translation of directives
proto::when<proto::terminal<boost::spirit::tag::lexeme>, proto::_make_terminal(boost::spirit::tag::verbatim())>, //lexeme -> verbatim
proto::when<
proto::subscript<proto::terminal<boost::spirit::tag::omit>,proto::_>, //omit[expr] -> no_delimit[eps]
proto::_make_subscript(proto::_make_terminal(boost::spirit::tag::no_delimit()),proto::_make_terminal(boost::spirit::tag::eps()))
>,
proto::terminal<proto::_>, //if the expression is any other terminal leave it as is
//translation of operators
proto::when<proto::shift_right<ToKarma,ToKarma>, proto::_make_shift_left(ToKarma(proto::_left),ToKarma(proto::_right)) >, //changes '>>' into '<<'
proto::when<proto::greater<ToKarma,ToKarma>, proto::_make_shift_left(ToKarma(proto::_left),ToKarma(proto::_right)) >, //changes '>' into '<<'
proto::when<proto::minus<ToKarma,ToKarma>, ToKarma(proto::_left)>, //changes 'expr-whatever' into 'expr'
proto::nary_expr<proto::_,proto::vararg<ToKarma> > //if it's anything else leave it unchanged and recurse into the expression tree
>{};
template <typename ... Attr, typename Parser>
void test(const std::string& input, const Parser& parser)
{
std::cout << "Original: \"" << input << "\"\n";
std::tuple<Attr...> attr;
std::string::const_iterator iter = input.begin(), end = input.end();
bool result = boost::spirit::qi::phrase_parse(iter,end,parser,boost::spirit::qi::space,attr);
if(result && iter==end)
{
ToKarma to_karma;
std::cout << "Generated: \"" << boost::spirit::karma::format_delimited(to_karma(parser), boost::spirit::karma::space, attr) << '"' << std::endl;
}
else
{
std::cout << "Parsing failed. Unparsed: ->" << std::string(iter,end) << "<-" << std::endl;
}
}
int main(){
using namespace boost::spirit::qi;
test<std::string,double,std::string >("Xx 1.233 pseudo", lexeme[+(char_-' '-'\n')] >> double_ >> lexeme[+(char_-' '-'\n')]);
test<int,double>("foo 1 2.5", omit[lexeme[+alpha]] > int_ > double_);
}
PS:
絶対にうまくいかないこと:
qi::rule
qi::grammar
qi::symbols
Karma に存在しないもの:
qi::attr
qi::matches
qi::hold
- 順列パーサー
^
- Sequential Or パーサー
||
Karma ではセマンティクスが異なるもの:
qi::skip
- アンド述語パーサー
&
- 非述語パーサー
!