Boost Spirit链式成员访问的on_success处理器触发问题
解决Boost Spirit链式调用节点的逐次注释问题
你的问题核心在于:当前postfix规则是匹配整个链式表达式(比如a.b.c.d是一个完整的postfix匹配),所以绑定在postfix上的on_success只会在整个表达式解析完成时触发一次,仅处理最终生成的顶层节点(a.b.c.d),而中间生成的a.b、a.b.c这些节点没有机会触发注释逻辑。
要让每个链式步骤都触发annotateNode,我们需要把成员访问/函数调用的操作拆分为独立的子规则,并给这些子规则绑定on_success,这样每完成一次链式调用的解析,就会立即处理对应的中间节点。
具体实现步骤
1. 定义带参数的子规则
我们需要定义接受当前节点作为参数的子规则,用来处理单次成员访问或函数调用,生成新的节点:
// 子规则:接受当前节点(_r1),返回新的成员访问节点 qi::rule<Iterator, common_node(common_node), Skipper> member_access; // 子规则:接受当前节点(_r1),返回新的函数调用节点 qi::rule<Iterator, common_node(common_node), Skipper> function_call;
2. 实现子规则的解析逻辑与语义动作
为每个子规则编写解析逻辑,同时在语义动作中基于传入的父节点生成新的AST节点:
// 处理成员访问(比如 .b) member_access = (lit('.') > name_pure) [ _val = construct<common_node>( type_cmd_dot_call, key_this, construct<common_node>(_r1), // 传入的父节点作为this key_propname, construct<std::wstring>(_1) // 成员名 ) ]; // 处理函数调用(比如 () 或 (param1, param2)) function_call = (lit('(') > paralistopt > lit(')')) [ _val = construct<common_node>( type_cmd_fnc_call, key_this, construct<common_node>(_r1), // 传入的父节点作为this key_parameter, construct<std::vector<common_node>>(_1) // 参数列表 ) ];
3. 给子规则绑定on_success处理器
现在给每个子规则绑定annotateNode,这样每次子规则匹配完成(即生成一个新的链式节点),就会触发注释:
on_success(member_access, phoenix::bind( &annotateNode, phoenix::ref(parserData), _1, _3, // 匹配的起始/结束迭代器 _val // 新生成的链式节点(比如a.b) )); on_success(function_call, phoenix::bind( &annotateNode, phoenix::ref(parserData), _1, _3, _val ));
4. 修改原postfix规则调用子规则
最后,把原postfix规则修改为调用这些子规则,每次迭代都更新当前的节点值:
postfix = primary [_val=_1] >> *( member_access(_val)[_val=_1] // 传入当前节点,用返回的新节点更新_val | function_call(_val)[_val=_1] // 同理处理函数调用 );
效果说明
现在解析a.b.c.d时,流程会变成这样:
- 解析
primary得到节点a,如果primary也需要注释,记得给它单独绑定on_success - 匹配
.b,调用member_access(a)生成a.b节点,触发member_access的on_success注释该节点,然后把_val更新为a.b - 匹配
.c,调用member_access(a.b)生成a.b.c节点,触发注释,更新_val - 匹配
.d,调用member_access(a.b.c)生成a.b.c.d节点,触发注释,更新_val - 整个
postfix规则完成(如果不需要重复注释最终节点,可以移除原postfix的on_success)
这样每个链式步骤生成的节点都会被单独注释,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Markus
相关产品推荐
相关产品推荐

