使用单独的规则定义和实例化时,升压精神X3 AST与语义动作工作 [英] Boost Spirit X3 AST not working with semantic actions when using separate rule definition and instantiation

查看:132
本文介绍了使用单独的规则定义和实例化时,升压精神X3 AST与语义动作工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用升压精神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 4

I 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 the x3::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 a reference_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

Live On Coliru

#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屋!

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