2

入力を解析するために使用する Qi 文法定義があります。後で、入力と同様の方法で出力する Karma ジェネレーターを用意しました。

これはまったく可能ですか?パーサー文法は自動的にジェネレーター文法に変換できるようです (??)。

#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
    );
}
4

1 に答える 1

5

悲しいことに、あなたが望むものを一般的な方法で達成することはできません (または、少なくとも方法はわかりません) が、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 >> expr2whereexpr1に一致し、文法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
  • アンド述語パーサー&
  • 非述語パーサー!
于 2016-09-16T17:17:04.937 に答える