使用单独的规则定义和实例化时,升压精神X3 AST与语义动作工作 [英] Boost Spirit X3 AST not working with semantic actions when using separate rule definition and instantiation
问题描述
我想使用升压精神X3与语义动作在解析结构的AST。如果我使用规则没有单独的定义和实例只是正常工作,例如:
的#include<矢量>
#包括LT&;串GT;
#包括LT&;&iostream的GT;
#包括LT&;升压/融合/有/ adapt_struct.hpp>
#包括LT&;升压/精神/家庭/ x3.hpp>命名空间AST
{结构ast_struct
{
INT编号;
的std ::矢量<&INT GT;数;
};}BOOST_FUSION_ADAPT_STRUCT(
AST :: ast_struct,
(INT,号码)
(性病::矢量< INT>中号)
)命名空间X3 =的boost ::精神:: X3;
使用命名空间std;无效解析(常量标准::字符串&安培;数据)
{
字符串::为const_iterator开始= data.begin();
字符串::为const_iterator结束= data.end(); 无符号的N(0); 汽车F = [&放大器; N](自动&安培; CTX)
{
N = X3 :: _ ATTR(CTX);
}; AST :: ast_struct AST;
BOOL R = X3 ::解析(开始,结束,
X3的:: int_并[f]≥> +(X3 ::省略[+ X3 ::空白>> X3 :: int_),AST); 如果(R&安培;&安培;开始==完)
{
COUT<< N:<< N'LT;< ,;
性病::复制(ast.numbers.begin(),ast.numbers.end()
的std :: ostream_iterator< INT>(STD ::法院LT&;< ast.numbers.size()<<元素,));
COUT<< ENDL;
}
其他
COUT<< 解析失败<< ENDL;
}诠释的main()
{
解析(3 1 2 3);
解析(4 1 2 3 4);
返回0;
}
运行code以上(含标志编译-std = C ++ 14)输出预期的结果:
N:3,3个要素:1 2 3
N:4,4要素:1 2 3 4
现在我想有我精X3分析器组织或多或少的方式作为的钙9例如从升压精神X3 的,但它不工作:
- ast.hxx:定义抽象语法树 。
- grammar.hxx:用户界面露出解析器方法
- grammar.cxx:实例化的规则
- grammar_def.hxx:解析器语法定义
- config.hxx:解析器配置
- main.cxx:解析器使用例子
ast.hxx:
的#ifndef AST_HXX
#定义AST_HXX#包括LT&;矢量>
#包括LT&;升压/融合/有/ adapt_struct.hpp>命名空间AST
{结构ast_struct
{
INT编号;
的std ::矢量<&INT GT;数;
};}BOOST_FUSION_ADAPT_STRUCT(
AST :: ast_struct,
(INT,号码)
(性病::矢量< INT>中号)
)#万一
grammar.hxx:
的#ifndef GRAMMAR_HXX
#定义GRAMMAR_HXX#包括ast.hxx
#包括LT&;升压/精神/家庭/ x3.hpp>命名空间的解析器
{命名空间X3 =的boost ::精神:: X3;使用my_rule_type = X3 ::规则<类my_rule_class,AST :: ast_struct取代;BOOST_SPIRIT_DECLARE(my_rule_type);常量my_rule_type&放大器; get_my_rule();}#万一
grammar.cxx:
的#includegrammar_def.hxx
#包括config.hxx命名空间的解析器
{BOOST_SPIRIT_INSTANTIATE(my_rule_type,iterator_type,context_type)}
grammar_def.hxx:
的#ifndef GRAMMAR_DEF_HXX
#定义GRAMMAR_DEF_HXX#包括LT&;&iostream的GT;
#包括LT&;升压/精神/家庭/ x3.hpp>
#包括grammar.hxx
#包括ast.hxx命名空间的解析器
{
命名空间X3 =的boost ::精神:: X3;常量my_rule_type my_rule(my_rule);无符号N;汽车F = [](自动&安培; CTX)
{
N = X3 :: _ ATTR(CTX);
};自动my_rule_def = X3 :: int_并[f]≥> +(X3 ::省略[+ X3 ::空白>> X3 :: int_);BOOST_SPIRIT_DEFINE(my_rule)常量my_rule_type&放大器; get_my_rule()
{
返回my_rule;
}}#万一
config.hxx:
的#ifndef CONFIG_HXX
#定义CONFIG_HXX#包括LT&;串GT;
#包括LT&;升压/精神/家庭/ x3.hpp>命名空间的解析器
{命名空间X3 =的boost ::精神:: X3;使用iterator_type =标准::字符串::为const_iterator;
使用context_type = X3 :: unused_type;}#万一
main.cxx:
的#includeast.hxx
#包括grammar.hxx
#包括config.hxx
#包括LT&;&iostream的GT;
#包括LT&;升压/精神/家庭/ x3.hpp>
#包括LT&;串GT;命名空间X3 =的boost ::精神:: X3;
使用命名空间std;无效解析(常量标准::字符串&安培;数据)
{
解析器:: iterator_type开始= data.begin();
解析器:: iterator_type结束= data.end(); AST :: ast_struct AST;
COUT<< 解析[<<字符串(开始,结束)LT;< ]<< ENDL; BOOL R = X3 ::解析(开始,结束,解析器:: get_my_rule(),AST); 如果(R&安培;&安培;开始==完)
{
性病::复制(ast.numbers.begin(),ast.numbers.end()
的std :: ostream_iterator< INT>(STD ::法院LT&;< ast.numbers.size()<<元素,));
COUT<< ENDL;
}
其他
COUT<< 解析失败<< ENDL;
}诠释的main()
{
解析(3 1 2 3);
解析(4 1 2 3 4);
返回0;
}
编译main.cxx和grammar.cxx(标志:-std = C ++ 14),并运行code以上打印:
解析[3 1 2 3]
0个元素:
解析[4 1 2 3 4]
0个元素:
我为长源$ C $ C道歉,我试图使它尽可能小。
请注意我有一个无符号■全球变量的一些使用,它会与自定义指令重复使用(请参阅question这里和的解决方案之一,这里)。为了使问题集中我删除了重复的部分来自这个问题,所以尽管我可以删除本例中的语义动作,它不是一个可行的解决方案。
我想AP preciate一些帮助得到这个问题的发现,为什么code以上不工作也不清楚我。谢谢你在前进。
我必须承认,其实重建你的样品是有点,我(叫我懒...)。
太多的工作不过,我知道答案,并一招,让你的生活更加简单。
答案
在规则定义语义动作抑制自动属性传播。从<一个href=\"http://www.boost.org/doc/libs/1_59_0/libs/spirit/doc/html/spirit/qi/reference/nonterminal/rule.html#spirit.qi.reference.nonterminal.rule.ex$p$pssion_semantics\"相对=nofollow>齐文档(同样也适用于X3,但我总是输的链接文档):
R =磷;
规则定义
这与R(%)= P(见下文),如果有P中的任何地方连接没有语义动作。
版本r%= P;自动规则定义
P的属性应与r的综合属性兼容。当p是成功的,它的属性被自动传播于r的综合属性。
块引用>的伎俩
您可以使用
注入状态(你的
指令。这样,你不具备全球命名空间(N
引用,在这种情况下)X3 ::以&lt;&GT;N
),并可以使解析器折返,线程等。下面是我的simplist承担的东西,在一个单一的文件:
命名空间解析{
X3 ::规则&LT;结构解析,AST :: ast_struct&GT;解析器{分析器}; 结构state_tag {}; 汽车record_number = [](自动&安培; CTX){
无符号&安培; N = X3 ::获得&LT; state_tag&GT;(CTX);
N = X3 :: _ ATTR(CTX);
}; 汽车parser_def = X3 ::规则&LT;结构parser_def,AST :: ast_struct&GT; {}
%= X3 :: int_ [record_number&GT;&GT; +(X3 ::省略[+ X3 ::空白&GT;&GT; X3 :: int_); BOOST_SPIRIT_DEFINE(分析器)
}
提示的:运行演示的
=
而不是% =
看行为的差异!的
块引用>注意
获取LT; state_tag&GT;(CTX)
返回的reference_wrapper&LT;无符号&GT;
只是因为我们使用的解析器如下:无效解析(常量标准::字符串&安培;数据){
使用命名空间std; AST :: ast_struct AST;
无符号N;
自动分析器= X3 ::以&lt;解析:: state_tag&GT;(参考文献(n))的[解析::解析器]≥&GT; X3 ::意向书; 如果(×3 ::解析(data.begin(),data.end(),语法分析器,AST)){
COUT&LT;&LT; N:&LT;&LT; N'LT;&LT; ,;
副本(ast.numbers.begin(),ast.numbers.end(),ostream_iterator&LT; INT&GT;(COUT&LT;&LT; ast.numbers.size()&LT;&LT;元素,));
COUT&LT;&LT; \\ n;
}其他
COUT&LT;&LT; 解析失败\\ n;
}现场演示
<大骨节病> 住在Coliru 骨节病>
的#include&LT;升压/融合/有/ adapt_struct.hpp&GT;
#包括LT&;升压/精神/家庭/ x3.hpp&GT;
#包括LT&;&iostream的GT;命名空间AST {
结构ast_struct {
INT编号;
的std ::矢量&lt;&INT GT;数;
};
}BOOST_FUSION_ADAPT_STRUCT(AST :: ast_struct,数,数字)命名空间X3 =的boost ::精神:: X3;命名空间解析{
X3 ::规则&LT;结构解析,AST :: ast_struct&GT;解析器{分析器}; 结构state_tag {}; 汽车record_number = [](自动&安培; CTX){
无符号&安培; N = X3 ::获得&LT; state_tag&GT;(CTX); //注:返回的reference_wrapper&LT; T&GT;
N = X3 :: _ ATTR(CTX);
}; 汽车parser_def = X3 ::规则&LT;结构parser_def,AST :: ast_struct&GT; {}
%= X3 :: int_ [record_number&GT;&GT; +(X3 ::省略[+ X3 ::空白&GT;&GT; X3 :: int_); BOOST_SPIRIT_DEFINE(分析器)
}无效解析(常量标准::字符串&安培;数据){
使用命名空间std; AST :: ast_struct AST;
无符号的N = 0;
自动分析器= X3 ::以&lt;解析:: state_tag&GT;(参考文献(n))的[解析::解析器]≥&GT; X3 ::意向书; 如果(×3 ::解析(data.begin(),data.end(),语法分析器,AST)){
COUT&LT;&LT; N:&LT;&LT; N'LT;&LT; ,;
副本(ast.numbers.begin(),ast.numbers.end(),ostream_iterator&LT; INT&GT;(COUT&LT;&LT; ast.numbers.size()&LT;&LT;元素,));
COUT&LT;&LT; \\ n;
}其他
COUT&LT;&LT; 解析失败\\ n;
}诠释主(){
解析(3 1 2 3);
解析(4 1 2 3 4);
}打印
N:3,3个要素:1 2 3
N:4,4要素:1 2 3 4I am trying to use Boost Spirit X3 with semantic actions while parsing the structure to an AST. If I use a rule without separate definition and instantiation it works just fine, for example:
#include <vector> #include <string> #include <iostream> #include <boost/fusion/include/adapt_struct.hpp> #include <boost/spirit/home/x3.hpp> namespace ast { struct ast_struct { int number; std::vector<int> numbers; }; } BOOST_FUSION_ADAPT_STRUCT( ast::ast_struct, (int, number) (std::vector<int>, numbers) ) namespace x3 = boost::spirit::x3; using namespace std; void parse( const std::string &data ) { string::const_iterator begin = data.begin(); string::const_iterator end = data.end(); unsigned n(0); auto f = [&n]( auto &ctx ) { n = x3::_attr(ctx); }; ast::ast_struct ast; bool r = x3::parse( begin, end, x3::int_[f] >> +( x3::omit[+x3::blank] >> x3::int_ ), ast ); if ( r && begin == end ) { cout << "n: " << n << ", "; std::copy(ast.numbers.begin(), ast.numbers.end(), std::ostream_iterator<int>(std::cout << ast.numbers.size() << " elements: ", " ")); cout << endl; } else cout << "Parse failed" << endl; } int main() { parse( "3 1 2 3" ); parse( "4 1 2 3 4" ); return 0; }
Running the code above (compiled with flags -std=c++14) outputs the expected result:
n: 3, 3 elements: 1 2 3 n: 4, 4 elements: 1 2 3 4
Now I am trying to have my Spirit X3 parser organized more or less the same way as the calc 9 example from Boost Spirit X3, but it does not work:
- ast.hxx: defines the abstract syntax tree.
- grammar.hxx: user interface exposing the parser methods.
- grammar.cxx: instantiates the rules.
- grammar_def.hxx: parser grammar definition.
- config.hxx: parser configuration.
- main.cxx: parser usage example.
ast.hxx:
#ifndef AST_HXX #define AST_HXX #include <vector> #include <boost/fusion/include/adapt_struct.hpp> namespace ast { struct ast_struct { int number; std::vector<int> numbers; }; } BOOST_FUSION_ADAPT_STRUCT( ast::ast_struct, (int, number) (std::vector<int>, numbers) ) #endif
grammar.hxx:
#ifndef GRAMMAR_HXX #define GRAMMAR_HXX #include "ast.hxx" #include <boost/spirit/home/x3.hpp> namespace parser { namespace x3 = boost::spirit::x3; using my_rule_type = x3::rule<class my_rule_class, ast::ast_struct>; BOOST_SPIRIT_DECLARE( my_rule_type ); const my_rule_type &get_my_rule(); } #endif
grammar.cxx:
#include "grammar_def.hxx" #include "config.hxx" namespace parser { BOOST_SPIRIT_INSTANTIATE( my_rule_type, iterator_type, context_type ) }
grammar_def.hxx:
#ifndef GRAMMAR_DEF_HXX #define GRAMMAR_DEF_HXX #include <iostream> #include <boost/spirit/home/x3.hpp> #include "grammar.hxx" #include "ast.hxx" namespace parser { namespace x3 = boost::spirit::x3; const my_rule_type my_rule( "my_rule" ); unsigned n; auto f = []( auto &ctx ) { n = x3::_attr(ctx); }; auto my_rule_def = x3::int_[f] >> +( x3::omit[+x3::blank] >> x3::int_ ); BOOST_SPIRIT_DEFINE( my_rule ) const my_rule_type &get_my_rule() { return my_rule; } } #endif
config.hxx:
#ifndef CONFIG_HXX #define CONFIG_HXX #include <string> #include <boost/spirit/home/x3.hpp> namespace parser { namespace x3 = boost::spirit::x3; using iterator_type = std::string::const_iterator; using context_type = x3::unused_type; } #endif
main.cxx:
#include "ast.hxx" #include "grammar.hxx" #include "config.hxx" #include <iostream> #include <boost/spirit/home/x3.hpp> #include <string> namespace x3 = boost::spirit::x3; using namespace std; void parse( const std::string &data ) { parser::iterator_type begin = data.begin(); parser::iterator_type end = data.end(); ast::ast_struct ast; cout << "Parsing [" << string(begin,end) << "]" << endl; bool r = x3::parse( begin, end, parser::get_my_rule(), ast ); if ( r && begin == end ) { std::copy(ast.numbers.begin(), ast.numbers.end(), std::ostream_iterator<int>(std::cout << ast.numbers.size() << " elements: ", " ")); cout << endl; } else cout << "Parse failed" << endl; } int main() { parse( "3 1 2 3" ); parse( "4 1 2 3 4" ); return 0; }
Compiling main.cxx and grammar.cxx (flags: -std=c++14) and running the code above prints:
Parsing [3 1 2 3] 0 elements: Parsing [4 1 2 3 4] 0 elements:
I apologize for the long source code, I tried to make it as small as possible.
Please notice I have some usage for the unsigned n global variable, it will be used with a custom repeat directive (see question here and one of the solutions here). In order to keep the question focused I removed the repeat part from this question, so even though I could remove the semantic action in this example, it is not a possible solution.
I would appreciate some help to get this issue uncovered, it is not clear to me why the code above does not work. Thank you in advance.
解决方案I must admit actually reconstructing your sample was a bit too much work for me (call me lazy...).
However, I know the answer and a trick to make your life simpler.
The Answer
Semantic actions on a rule definition inhibit automatic attribute propagation. From the Qi docs (the same goes for X3, but I always lose the link to the docs):
r = p;
Rule definition
This is equivalent to r %= p (see below) if there are no semantic actions attached anywhere in p.r %= p; Auto-rule definition
The attribute of p should be compatible with the synthesized attribute of r. When p is successful, its attribute is automatically propagated to r's synthesized attribute.
The Trick
You can inject state (your
n
reference, in this case) using thex3::with<>
directive. That way you don't have the namespace global (n
) and can make the parser reentrant, threadsafe etc.Here's my "simplist" take on things, in a single file:
namespace parsing { x3::rule<struct parser, ast::ast_struct> parser {"parser"}; struct state_tag { }; auto record_number = [](auto &ctx) { unsigned& n = x3::get<state_tag>(ctx); n = x3::_attr(ctx); }; auto parser_def = x3::rule<struct parser_def, ast::ast_struct> {} %= x3::int_[record_number] >> +(x3::omit[+x3::blank] >> x3::int_); BOOST_SPIRIT_DEFINE(parser) }
Tip: run the demo with
=
instead of the%=
to see the difference in behaviour!Note that
get<state_tag>(ctx)
returns areference_wrapper<unsigned>
just because we use the parser as follows:void parse(const std::string &data) { using namespace std; ast::ast_struct ast; unsigned n; auto parser = x3::with<parsing::state_tag>(ref(n)) [parsing::parser] >> x3::eoi; if (x3::parse(data.begin(), data.end(), parser, ast)) { cout << "n: " << n << ", "; copy(ast.numbers.begin(), ast.numbers.end(), ostream_iterator<int>(cout << ast.numbers.size() << " elements: ", " ")); cout << "\n"; } else cout << "Parse failed\n"; }
Live Demo
#include <boost/fusion/include/adapt_struct.hpp> #include <boost/spirit/home/x3.hpp> #include <iostream> namespace ast { struct ast_struct { int number; std::vector<int> numbers; }; } BOOST_FUSION_ADAPT_STRUCT(ast::ast_struct, number, numbers) namespace x3 = boost::spirit::x3; namespace parsing { x3::rule<struct parser, ast::ast_struct> parser {"parser"}; struct state_tag { }; auto record_number = [](auto &ctx) { unsigned& n = x3::get<state_tag>(ctx); // note: returns reference_wrapper<T> n = x3::_attr(ctx); }; auto parser_def = x3::rule<struct parser_def, ast::ast_struct> {} %= x3::int_[record_number] >> +(x3::omit[+x3::blank] >> x3::int_); BOOST_SPIRIT_DEFINE(parser) } void parse(const std::string &data) { using namespace std; ast::ast_struct ast; unsigned n = 0; auto parser = x3::with<parsing::state_tag>(ref(n)) [parsing::parser] >> x3::eoi; if (x3::parse(data.begin(), data.end(), parser, ast)) { cout << "n: " << n << ", "; copy(ast.numbers.begin(), ast.numbers.end(), ostream_iterator<int>(cout << ast.numbers.size() << " elements: ", " ")); cout << "\n"; } else cout << "Parse failed\n"; } int main() { parse("3 1 2 3"); parse("4 1 2 3 4"); }
Prints
n: 3, 3 elements: 1 2 3 n: 4, 4 elements: 1 2 3 4
这篇关于使用单独的规则定义和实例化时,升压精神X3 AST与语义动作工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!