提升精神语义行动参数 [英] boost spirit semantic action parameters

查看:110
本文介绍了提升精神语义行动参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在此关于提升精神语义行为的文章中提到


实际上还有另外两个参数
被传递:解析器上下文和
对布尔值hit
参数的引用。仅当语义动作
附加到规则的右侧
手的某处时,解析器上下文才是有意义的。我们稍后会看到更多关于此的
信息。
布尔值可以设置为false
在语义操作内使无效的
匹配在回溯,使
解析器失败。

There are actually 2 more arguments being passed: the parser context and a reference to a boolean ‘hit’ parameter. The parser context is meaningful only if the semantic action is attached somewhere to the right hand side of a rule. We will see more information about this shortly. The boolean value can be set to false inside the semantic action invalidates the match in retrospective, making the parser fail.

很好,但我一直在试图找到一个例子传递一个函数对象作为语义动作,使用其他参数(解析器上下文和命中布尔),但我还没有找到任何。我很想看到一个使用常规函数或函数对象的例子,因为我几乎不能挖掘凤凰巫师

All fine, but i've been trying to find an example passing a function object as semantic action that uses the other parameters (parser context and hit boolean) but i haven't found any. I would love to see an example using regular functions or function objects, as i barely can grok the phoenix voodoo

推荐答案

好的问题(也是一个蠕虫病毒),因为它在qi和凤凰的界面。我还没有看到一个例子,所以我会在这个方向稍微延长文章。

This a really good question (and also a can of worms) because it gets at the interface of qi and phoenix. I haven't seen an example either, so I'll extend the article a little in this direction.

正如你所说,语义操作最多可以包含三个参数

As you say, functions for semantic actions can take up to three parameters


  1. 匹配属性 -

  2. 上下文 - 包含qi-phoenix接口

  3. 匹配标记 - 操作匹配状态

匹配标志

Match flag

正如文章所述,第二个参数没有意义,除非表达式是规则的一部分,所以让我们从第三个开始。仍然需要第二个参数的占位符,为此使用 boost :: fusion :: unused_type 。因此,从文章修改的函数使用第三个参数是:

As the article states, the second parameter is not meaningful unless the expression is part of a rule, so lets start with the third. A placeholder for the second parameter is still needed though and for this use boost::fusion::unused_type. So a modified function from the article to use the third parameter is:

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

void f(int attribute, const boost::fusion::unused_type& it, bool& mFlag){
    //output parameters
    std::cout << "matched integer: '" << attribute << "'" << std::endl
              << "match flag: " << mFlag << std::endl;

    //fiddle with match flag
    mFlag = false;
}

namespace qi = boost::spirit::qi;

int main(void){
   std::string input("1234 6543");
   std::string::const_iterator begin = input.begin(), end = input.end();

   bool returnVal = qi::phrase_parse(begin, end, qi::int_[f], qi::space);

   std::cout << "return: " << returnVal << std::endl;
   return 0;
}

其输出:


matched integer: '1234'
match flag: 1
return: 0

所有这个例子都是将匹配切换到不匹配,这反映在解析器输出中。根据hkaiser,在boost 1.44和向上设置匹配标志为false将导致匹配以正常方式失败。如果定义了替代项,解析器将回溯并尝试匹配它们,如预期的那样。然而,在boost <= 1.43中,精灵错误防止回溯,这导致奇怪的行为。要看到这一点,请添加phoenix include boost / spirit / include / phoenix.hpp 并将表达式更改为

All this example does is switch the match to a non-match, which is reflected in the parser output. According to hkaiser, in boost 1.44 and up setting the match flag to false will cause the match to fail in the normal way. If alternatives are defined, the parser will backtrack and attempt to match them as one would expect. However, in boost<=1.43 a Spirit bug prevents backtracking, which causes strange behavior. To see this, add phoenix include boost/spirit/include/phoenix.hpp and change the expression to

qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]

你会期望当qi :: int解析器失败时,替换的qi ::数字匹配输入的开头在1,但输出是:

You'd expect that, when the qi::int parser fails, the alternative qi::digit to match the beginning of the input at "1", but the output is:


matched integer: '1234'
match flag: 1
6
return: 1

6 是输入中第二个int的第一个数字,表示使用船长和没有回溯进行替代。还要注意,匹配被视为成功,基于替代。

The 6 is the first digit of the second int in the input which indicates the alternative is taken using the skipper and without backtracking. Notice also that the match is considered succesful, based on the alternative.

一旦boost 1.44结束,匹配标志将有助于应用匹配标准,否则可能难以在解析器序列中表达。注意,可以使用 _pass 占位符在phoenix表达式中操作匹配标志。

Once boost 1.44 is out, the match flag will be useful for applying match criteria that might be otherwise difficult to express in a parser sequence. Note that the match flag can be manipulated in phoenix expressions using the _pass placeholder.

上下文参数

更有趣的参数是第二个参数,它包含qi-phoenix接口,或者qi parlance,语义动作的上下文。为了说明这一点,首先检查规则:

The more interesting parameter is the second one, which contains the qi-phoenix interface, or in qi parlance, the context of the semantic action. To illustrate this, first examine a rule:

rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>

上下文参数包含Attribute,Arg1,... ArgN和qi :: locals模板参数,包装在boost :: spirit :: context模板类型中。此属性与函数参数不同:函数参数属性是解析的值,而此属性是规则本身的值。语义动作必须将前者映射到后者。下面是一个可能的上下文类型的例子(表示等价的phoenix表达式):

The context parameter embodies the Attribute, Arg1, ... ArgN, and qi::locals template paramters, wrapped in a boost::spirit::context template type. This attribute differs from the function parameter: the function parameter attribute is the parsed value, while this attribute is the value of the rule itself. A semantic action must map the former to the latter. Here's an example of a possible context type (phoenix expression equivalents indicated):

using namespace boost;
spirit::context<              //context template
    fusion::cons<             
        int&,                 //return int attribute (phoenix: _val)
        fusion::cons<
            char&,            //char argument1       (phoenix: _r1)
            fusion::cons<
                float&,       //float argument2      (phoenix: _r2) 
                fusion::nil   //end of cons list
            >,
        >,
    >,
    fusion::vector2<          //locals container
        char,                 //char local           (phoenix: _a)
        unsigned int          //unsigned int local   (phoenix: _b)
    > 
>

注意return属性和参数列表采用lisp风格列表的形式=http://en.wikipedia.org/wiki/Cons#Lists> cons列表)。要访问函数中的这些变量,请访问上下文的属性 locals / code> struct template with fusion :: at<>()。例如,对于上下文变量 con

Note the return attribute and argument list take the form of a lisp-style list (a cons list). To access these variables within a function, access the attribute or locals members of the context struct template with fusion::at<>(). For example, for a context variable con

//assign return attribute
fusion::at_c<0>(con.attributes) = 1;

//get the second rule argument
float arg2 = fusion::at_c<2>(con.attributes);

//assign the first local
fusion::at_c<1>(con.locals) = 42;

要修改文章示例以使用第二个参数,请更改函数定义和phrase_parse调用:

To modify the article example to use the second argument, change the function definition and phrase_parse calls:

...
typedef 
    boost::spirit::context<
        boost::fusion::cons<int&, boost::fusion::nil>, 
        boost::fusion::vector0<> 
    > f_context;
void f(int attribute, const f_context& con, bool& mFlag){
   std::cout << "matched integer: '" << attribute << "'" << std::endl
             << "match flag: " << mFlag << std::endl;

   //assign output attribute from parsed value    
   boost::fusion::at_c<0>(con.attributes) = attribute;
}
...
int matchedInt;
qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule = qi::int_[f];
qi::phrase_parse(begin, end, intRule, ascii::space, matchedInt);
std::cout << "matched: " << matchedInt << std::endl;
....

这是一个非常简单的示例,只是将解析的值映射到输出属性值,但扩展应该是相当明显的。只需使上下文结构模板参数匹配规则输出,输入和本地类型。请注意,这种类型的解析类型/值与输出类型/值之间的直接匹配可以使用自动规则自动完成,使用%= 而不是 = 定义规则时:

This is a very simple example that just maps the parsed value to the output attribute value, but extensions should be fairly apparent. Just make the context struct template parameters match the rule output, input, and local types. Note that this type of a direct match between parsed type/value to output type/value can be done automatically using auto rules, with a %= instead of a = when defining the rule:

qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule %= qi::int_;

IMHO,为每个动作编写一个函数相当冗长乏味,等价物。我同情巫术的观点,但一旦你与凤凰工作一段时间,语义和语法不是很难。

IMHO, writing a function for each action would be rather tedious, compared to the brief and readable phoenix expression equivalents. I sympathize with the voodoo viewpoint, but once you work with phoenix for a little while, the semantics and syntax aren't terribly difficult.

编辑:访问规则上下文/ Phoenix

上下文变量在解析器是规则的一部分时定义。将解析器视为任何使用输入的表达式,其中规则将解析器值(qi :: _ 1)转换为规则值(qi :: _ val)。差异通常是不平凡的,例如,当qi :: val具有需要从POD解析值构造的类类型。下面是一个简单的例子。

The context variable is only defined when the parser is part of a rule. Think of a parser as being any expression that consumes input, where a rule translates the parser values (qi::_1) into a rule value (qi::_val). The difference is often non-trivial, for example when qi::val has a Class type that needs to be constructed from POD parsed values. Below is a simple example.

假设我们的输入是一个由三个CSV整数组成的序列( x1,x2,x3 ),我们只关心这三个整数(f = x0 +(x1 + x2)* x3)的算术函数,其中x0是在别处获得的值。一个选项是读取整数并计算函数,或者使用phoenix来做这两个操作。

Let's say part of our input is a sequence of three CSV integers (x1, x2, x3), and we only care out an arithmetic function of these three integers (f = x0 + (x1+x2)*x3 ), where x0 is a value obtained elsewhere. One option is to read in the integers and calculate the function, or alternatively use phoenix to do both.

对于这个例子,使用一个带输出属性的规则值)和输入(x0),以及一个局部(在规则之间传递各个解析器之间的信息)。以下是完整的示例。

For this example, use one rule with an output attribute (the function value), and input (x0), and a local (to pass information between individual parsers with the rule). Here's the full example.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

int main(void){
   std::string input("1234, 6543, 42");
   std::string::const_iterator begin = input.begin(), end = input.end();

   qi::rule<
      std::string::const_iterator,
      int(int),                    //output (_val) and input (_r1)
      qi::locals<int>,             //local int (_a)
      ascii::space_type
   >
      intRule =
            qi::int_[qi::_a = qi::_1]             //local = x1
         >> ","
         >> qi::int_[qi::_a += qi::_1]            //local = x1 + x2
         >> ","
         >> qi::int_
            [
               qi::_val = qi::_a*qi::_1 + qi::_r1 //output = local*x3 + x0
            ];

   int ruleValue, x0 = 10;
   qi::phrase_parse(begin, end, intRule(x0), ascii::space, ruleValue);
   std::cout << "rule value: " << ruleValue << std::endl;
   return 0;
}

或者,所有的int可以被解析为一个向量, (是列表运算符,向量的元素可以通过phoenix :: at访问):

Alternatively, all the ints could be parsed as a vector, and the function evaluated with a single semantic action (the % below is the list operator and elements of the vector are accessed with phoenix::at):

namespace ph = boost::phoenix;
...
    qi::rule<
        std::string::const_iterator,
        int(int),
        ascii::space_type
    >
    intRule =
        (qi::int_ % ",")
        [
            qi::_val = (ph::at(qi::_1,0) + ph::at(qi::_1,1))
                      * ph::at(qi::_1,2) + qi::_r1
        ];
....

对于上述,如果输入不正确的三个),坏的事情可能会在运行时发生,因此最好明确指定解析的值的数量,因此解析将失败的一个坏的输入。下面使用 _1 _2 _3 第一,第二和第三匹配值:

For the above, if the input is incorrect (two ints instead of three), bad thing could happen at run time, so it would be better to specify the number of parsed values explicitly, so parsing will fail for a bad input. The below uses _1, _2, and _3 to reference the first, second, and third match value:

(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
    qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];

这是一个假设的例子,但应该给你的想法。我发现phoenix语义动作真正有助于直接从输入构建复杂对象;这是可能的,因为你可以在语义动作中调用构造函数和成员函数。

This is a contrived example, but should give you the idea. I've found phoenix semantic actions really helpful in constructing complex objects directly from input; this is possible because you can call constructors and member functions within semantic actions.

这篇关于提升精神语义行动参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆