提升精神语义动作参数
在这篇关于助推精神语义行为的文章中提到了这一点
实际上还有两个参数被传递:parsing器上下文和一个布尔型“命中”参数的引用。 parsing器上下文只有在语义动作附加到规则的右手边时才有意义。 我们将很快看到更多的信息。 布尔值可以在语义操作中设置为false,使回溯中的匹配无效,从而使parsing器失败。
一切正常,但我一直在试图find一个例子传递一个函数对象作为语义行动,使用其他参数(parsing器上下文和命中布尔),但我还没有find任何。 我很希望看到一个使用常规函数或函数对象的例子,因为我几乎可以挖掘凤凰巫术
这是一个非常好的问题(也是一个蠕虫),因为它是在气和凤凰的接口。 我也没有看到一个例子,所以我会在这个方向稍微扩展一下这个文章。
正如你所说, 语义动作的function可能需要三个参数
- 匹配的属性 – 在文章中讨论
- 上下文 – 包含qi-phoenix接口
- 匹配标志 – 操纵匹配状态
匹配标志
正如文章所述,第二个参数是没有意义的,除非expression式是规则的一部分,所以让我们从第三个开始。 第二个参数的占位符仍然需要,为此使用boost::fusion::unused_type
。 所以从文章中修改的函数使用第三个参数是:
#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; }
其输出:
匹配整数:“1234” 比赛标志:1 返回:0
所有这个例子都是把匹配切换到一个不匹配,这反映在parsing器输出中。 根据hkaiser的说法,在boost 1.44中设置匹配标志为false将导致匹配以正常的方式失败。 如果定义了替代scheme,parsing器将会回溯并尝试按照预期匹配它们。 然而,在boost <= 1.43的时候,一个灵魂的bug会阻止回溯,这会导致奇怪的行为。 要看到这个,添加phoenix包括boost/spirit/include/phoenix.hpp
并将expression式改为
qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]
你会希望,当qi :: intparsing器失败时,替代的qi :: digit匹配input的起始位置“1”,但输出是:
匹配整数:“1234” 比赛标志:1 6 返回:1
6
是input中第二个int的第一个数字,表示替代方法是使用船长并且没有回溯。 还要注意,基于替代scheme,匹配被认为是成功的。
一旦提升1.44结束,匹配标志对于应用在parsing器序列中可能难以expression的匹配标准将是有用的。 请注意,可以使用_pass
占位符在phoenixexpression式中操作匹配标志。
上下文参数
第二个比较有意思的参数是第二个,它包含了qi-phoenix接口,或者是用语义来描述语义动作的上下文。 为了说明这一点,首先检查一个规则:
rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>
context参数包含了包装在boost :: spirit :: context模板types中的Attribute,Arg1,… ArgN和qi :: locals模板参数。 这个属性不同于函数参数:函数参数属性是parsing的值,而这个属性是规则本身的值。 语义行为必须将前者映射到后者。 下面是一个可能的上下文types的例子(表示等效的凤凰expression式):
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) > >
请注意,返回属性和参数列表采用lisp样式列表( 缺点列表 )的forms。 要在函数中访问这些variables,请使用fusion :: at <>()来访问context
结构模板的attribute
或locals
成员。 例如,对于上下文variablescon
//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调用:
... 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; ....
这是一个非常简单的例子,只是将parsing的值映射到输出属性值,但扩展应该是相当明显的。 只要使上下文结构模板参数匹配规则输出,input和本地types即可。 请注意,parsingtypes/值与输出types/值之间的这种types的直接匹配可以使用自动规则自动完成,定义规则时使用%=
而不是a =
:
qi::rule<std::string::const_iterator,int(void),ascii::space_type> intRule %= qi::int_;
恕我直言,为每个动作写一个函数将是相当繁琐,相比,简短和可读的凤凰expression等值。 我同情伏都教的观点,但是一旦你和凤凰一起工作,语义和语法就不是非常困难了。
编辑:访问规则上下文W /凤凰
上下文variables只在parsing器是规则的一部分时才被定义。 将分析器看作是消耗input的任何expression式,其中规则将parsing器值(qi :: _ 1)转换为规则值(qi :: _ val)。 差别通常是不平凡的,例如当qi :: val有一个Classtypes需要从PODparsing值构造时。 下面是一个简单的例子。
假设我们input的一部分是三个CSV整数( x1, x2, x3
)的序列,我们只关心这三个整数(f = x0 +(x1 + x2)* x3)的算术函数,其中x0是在别处获得的价值。 一个选项是读入整数并计算函数,或者使用phoenix来完成这两个操作。
对于这个例子,使用一个带有输出属性(函数值)的规则,input(x0)和一个本地(在单个parsing器之间用规则传递信息)。 这是完整的例子。
#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; }
或者,所有的整数可以被parsing为一个vector,并且用单个语义操作来评估该函数(下面的%
是列表运算符,并且该vector的元素用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 ]; ....
对于上述情况,如果input是不正确的(两个ints而不是三个),则在运行时可能会发生错误,所以最好是明确指定parsing值的数量,因此对于错误的input,parsing将失败。 以下使用_1
, _2
和_3
来引用第一个,第二个和第三个匹配值:
(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_) [ qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1 ];
这是一个人为的例子,但应该给你的想法。 我发现凤凰的语义动作真的有助于直接从input构build复杂的对象; 这是可能的,因为你可以在语义动作中调用构造函数和成员函数。