我正在尝试将Boost Spirit X3与语义 Action 一起使用,同时将结构解析为AST。如果我使用的规则没有单独的定义和实例化,那么它可以正常工作,例如:

#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;
}

运行上面的代码(与标志-std = c++ 14编译)输出预期结果:
n: 3, 3 elements: 1 2 3
n: 4, 4 elements: 1 2 3 4

现在,我试图让我的Spirit X3解析器的组织方式与Boost Boost X3的calc 9 example大致相同,但是它不起作用:
  • ast.hxx:定义抽象语法树。
  • grammar.hxx:公开解析器方法的用户界面。
  • grammar.cxx:实例化规则。
  • grammar_def.hxx:解析器语法定义。
  • config.hxx:解析器配置。
  • main.cxx:解析器用法示例。

  • 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;
    }
    

    编译main.cxx和grammar.cxx(标志:-std = c++ 14)并运行上面的代码将输出:
    Parsing [3 1 2 3]
    0 elements:
    Parsing [4 1 2 3 4]
    0 elements:
    

    我为冗长的源代码表示歉意,并尝试将其尽量缩小。

    请注意,我对无符号n全局变量有一些用法,它将与自定义的repeat指令一起使用(请参阅question hereone of the solutions here)。为了使问题集中注意力,我从该问题中删除了重复部分,因此即使在此示例中我可以删除语义 Action ,也不是可行的解决方案。

    我将不胜感激,可以帮助您发现此问题,但尚不清楚为什么上面的代码无法正常工作。先感谢您。

    最佳答案

    我必须承认,实际上重建您的样本对我来说有点麻烦(称我为懒人...)。
    但是,我知道答案和使您的生活更简单的窍门。
    答案
    规则定义上的语义 Action 会禁止自动属性传播。从Qi docs(X3也是如此,但我总是会丢失指向文档的链接):

    绝招
    您可以使用n指令注入(inject)状态(在这种情况下为x3::with<>引用)。这样,您就没有全局 namespace (n),并且可以使解析器可重入,线程安全等。
    这是我的“ list ”,将它们放在一个文件中:

    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)
    }
    

    请注意,get<state_tag>(ctx)返回reference_wrapper<unsigned>只是因为我们使用解析器,如下所示:
    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 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");
    }
    
    打印
    n: 3, 3 elements: 1 2 3
    n: 4, 4 elements: 1 2 3 4
    

    10-07 12:30