是否有可能重用Spirit Qi语法作为Spirit Karma语法?

时间:2016-09-16 00:56:24

标签: c++ parsing boost-spirit boost-spirit-qi boost-spirit-karma

我有一个用于解析输入的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
    );
}

1 个答案:

答案 0 :(得分:4)

可悲的是,不可能以一般的方式实现你想要的东西(或者至少我不知道如何),但是如果你愿意只使用Spirit.Qi的一个有限的子集,下面的方法可以起作用。< / p>

首先要知道的是,当你使用类似的东西时:

int_ >> double_

您只有一个Boost.Proto表达式,它描述了几个终端以及它们之间的关系。该表达式本身并不“知道”有关如何解析一个int然后一个double的任何内容。每当您使用parse / phrase_parse或将其中一个Proto表达式分配给rule Spirit时,“编译”该域的表达式(Qi或Karma)并创建执行的解析器/生成器实际工作。

Here你可以看到一个小例子,它显示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,其中expr1expr2必须递归地符合ToKarma语法。
  • proto::nary_expr<proto::_,proto::vararg<ToKarma> >:匹配任何n-ary(一元,二元或实际n-ary,如函数调用a(b,c,d,e)),其中表达式的每个元素都符合ToKarma语法。

此示例中的所有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表示获取原始表达式的lhs。 ToKarma(proto::_left)表示递归地将ToKarma语法/变换应用于原始表达式的lhs。整个_make_shift_left基本上构建transformed_lhs << transformed_rhs

MatcherN本身(不在proto::when内)是使用as元素构建相同类型的表达式的简写,将元素递归地应用于原始元素。

完整示例(Running on 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
  • 排列解析器^
  • 顺序或解析器||

Karma中具有不同语义的东西:

  • qi::skip
  • And-predicate parser &
  • 非谓词解析器!