这是一个非常好的问题(也是一个蠕虫的罐头),因为它得到了气和凤凰的接口。我还没有看到一个例子,所以我会在这个方向上稍微扩展一下这篇文章。
正如你所说,为semantic actions功能可能需要多达三个参数
- 匹配的属性 - 覆盖的文章
- 语境中 - 包含齐凤接口
- 匹配标志 - 操纵比赛状态
匹配标志
正如文章所述,除非表达式是规则的一部分,否则第二个参数是没有意义的,所以我们从第三个开始。第二个参数的占位符仍然需要,但为此使用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;
}
,其输出:
matched integer: '1234'
match flag: 1
return: 0
所有这些例子并是匹配切换到不匹配,这反映在解析器输出。根据hkaiser的说法,在boost 1.44中,如果将match flag设置为false,会导致匹配以正常方式失败。如果定义了替代方案,解析器会回溯并尝试按照预期匹配它们。然而,在提升< = 1.43时,灵魂bug阻止回溯,这会导致奇怪的行为。看到这一点,加凤包括boost/spirit/include/phoenix.hpp
,改变表达
qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]
你会想到的是,当齐:: INT解析器失败,替代气::数字来匹配输入在“开始1" ,但输出的是:
matched integer: '1234'
match flag: 1
6
return: 1
的6
是在指示替代的输入和第二INT的第一位数使用船长和无回溯取。另请注意,基于替代方案,该匹配被认为是成功的。
一旦提升1.44结束,匹配标志将有助于应用在解析器序列中可能难以表达的匹配标准。请注意,可以使用_pass
占位符在phoenix表达式中操作匹配标志。
上下文参数
更有趣的参数是第二个,其中包含补气凤凰接口,或在气的说法,语义动作的上下文。为了说明这一点,首先检查规则:
rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>
上下文参数体现了属性,ARG1,... ARGN,补气::当地人模板paramters,包裹在一个boost ::精神::背景模板类型。此属性与函数参数不同:函数参数属性是解析值,而此属性是规则本身的值。语义行为必须将前者映射到后者。这里的一个可能的上下文类型的例子(凤表达当量表示):
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风格列表的形式(一个cons list)。要访问函数中的这些变量,请使用fusion :: at <>()访问context
结构模板的attribute
或locals
成员。例如,对于变量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电话:
...
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;
....
这是一个非常简单的例子,只是映射解析值为输出属性值,但扩展名应该相当明显。只要使上下文结构模板参数匹配规则输出,输入和本地类型即可。需要注意的是这种类型的输出类型/值可以自动使用自动规则定义规则时进行,用%=
代替=
解析的类型/值之间的直接匹配的:
qi::rule<std::string::const_iterator,int(void),ascii::space_type>
intRule %= qi::int_;
恕我直言,写一个函数对于每一个动作来说,相对于简短易读的凤凰表情等价物而言,相当乏味。我同情伏都教的观点,但是一旦你和凤凰一起工作了一段时间,语义和语法就不是非常困难了。
编辑:访问规则上下文瓦特/菲尼克斯
当解析器是规则的一部分的上下文变量限定。将分析器看作是消耗输入的任何表达式,其中规则将解析器值(qi :: _ 1)转换为规则值(qi :: _ val)。差别通常是非平凡的,例如,当qi :: val具有需要从POD解析值构建的Class类型时。下面是一个简单的例子。假设我们输入的一部分是一个由三个CSV整数组成的序列(x1, x2, x3
),我们只关心这三个整数的算术函数(f = x0 +(x1 + x2)* x3),其中x0是在别处获得的价值。一种选择是读入整数并计算函数,或者使用phoenix来执行这两个操作。
对于此示例,使用一个带有输出属性(函数值)和输入(x0)的规则,以及一个本地(用于在单个解析器与规则之间传递信息)的规则。这是完整的例子。
#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;
}
可替换地,所有的整数可以被解析为一个矢量,并函数用单个语义动作评价(以下%
是列表操作者和所述向量的元素与凤访问::在):
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
引用第一,第二和第三匹配值:
(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];
这是一个人为的例子,但应该给你的想法。我发现凤凰语义行为真正有助于直接从输入构建复杂对象;这是可能的,因为你可以在语义动作中调用构造函数和成员函数。
感谢您的出色解释。你介意我'偷'这个,以便在Spirit网站上重新发布(当然是给予应有的学分)? 你提到的回溯问题是Spirit中的一个错误。第一种替代方案失败后的正确行为应该是第二种替代方法在第一种替代方案的输入中的同一点处重新开始。我会看看我能做些什么来解决这个问题。 此外,您不应该在语义操作中使用凤凰占位符。请始终使用相应的Spirit占位符,即qi :: _ 1。 – hkaiser 2010-06-18 21:34:14
好吧,回溯问题现在已经修复,在下一个版本(升压版本V1.44)中可以正常使用。 – hkaiser 2010-06-18 21:52:53
@hkaiser很高兴你喜欢它,如果你愿意,请重复使用它。我会在邮件列表中询问有关回溯的问题,谢谢您的关心。一个关于占位符的问题:phoenix :: _ 1和qi :: _ 1被定义为const phoenix :: actor>,这个主题是否会改变? –
academicRobot
2010-06-18 23:32:36