Spirit X3,如何获取属性类型以匹配规则类型? [英] Spirit X3, How to get attribute type to match rule type?

查看:71
本文介绍了Spirit X3,如何获取属性类型以匹配规则类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了开发Spirit X3解析器,我想使用语义动作(脚注1).对我来说,控制如何将属性存储到STL容器中很重要.

For the development of Spirit X3 parser I want to use semantic actions(footnote 1). It is important for me to be in control of how to store attributes into STL containers.

这个问题是关于如何控制解析器属性_attr(ctx)与规则类型:_val(ctx)匹配的,以便可以对其进行正确分配.也许这个问题归结为如何应用无证件 transform_attribute 功能.但是,请与我一起阅读,看看在示例代码中这是否真的可以解决我的问题.

This question is about how to control that the parser attribute: _attr( ctx ) match the rule type: _val( ctx ) so that it can be assigned properly. Maybe this question boils down to how to apply the undocumented transform_attribute feature. But please read with me to see if that is actually the thing that solves it for me in the example code.

打印对象/变量的类型

当我尝试不同的语法表达式时,我发现非常有用的是能够以语义动作打印_attr(ctx)和_val(ctx)类型的功能.

What I found very useful is the ability to print the type of _attr( ctx ) and _val( ctx ) in an semantic action, when I am experimenting with different grammar expressions.

因此,根据 Howard的回答Hinnant ,我编写了一个实用程序头文件来根据自己的喜好提供类似的功能.

So based on the answer of Howard Hinnant, I wrote a utility header file to provide facilities like this according to my preferences.

code将被放置在一个名为Utility.h的文件中.

#include <string>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>

namespace utility
{

template<typename T>
std::string type2string()
{
  std::string r;
  typedef typename std::remove_reference<T>::type TR;

  std::string space = "";
  if ( std::is_const<TR>::value )
    { r = "const"; space = " "; }
  if ( std::is_volatile<TR>::value )
    { r += space + " volatile"; space = " "; }

  int status;
  char* demangled =
    abi::__cxa_demangle( typeid(TR).name(), nullptr, nullptr, &status );
  switch ( status )
  {
    case  0: { goto proceed; }
    case -1: { r = "type2string failed: malloc failure"; goto fail; }
    case -2: { r = "type2string failed: " + std::string(typeid(TR).name()) +
      " nonvalid C++ ABI name"; goto fail; }
    case -3: { r = "type2string failed: invalid argument(s)"; goto fail; }
    default: { r = "type2string failed: unknown status " +
      status; goto fail; }
  }
  proceed:
  r += space + demangled;
  free( demangled );

  /* references are without a space */
  if ( std::is_lvalue_reference<T>::value ) { r += '&'; }
  if ( std::is_rvalue_reference<T>::value ) { r += "&&"; }

  fail:
  return r;
}

}

现在是实际的工作示例代码:

Now the actual working example code:

#include <cstddef>
#include <cstdio>
#include <cstdint>

#define BOOST_SPIRIT_X3_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>

#include <string>
#include <vector>
#include <utility> // this is for std::move
#include "utility.h" // to print types

namespace client
{
  namespace x3 = boost::spirit::x3;
  namespace ascii = boost::spirit::x3::ascii;

  namespace semantic_actions
  {
    using x3::_val;  // assign to _val( ctx )
    using x3::_attr; // from _attr( ctx )    

    struct move_assign
    {  
      template <typename Context>
      void operator()(const Context& ctx) const
      {
        printf( "move_assign\n" );
        _val( ctx ) = std::move( _attr( ctx ) );
      }
    };

    struct print_type
    {
      template <typename Context>
      void operator()(const Context& ctx) const
      {
        printf( "print_type\n" );

        std::string str;
        str = utility::type2string< decltype( _attr( ctx ) ) >();
        printf( "_attr type: %s\n", str.c_str() );

        // reuse str
        str = utility::type2string< decltype( _val( ctx ) ) >();
        printf( "_val type: %s\n", str.c_str() );
      }
    };
  }

  namespace parser
  {
    using x3::char_;
    using x3::lit;
    using namespace semantic_actions;

    x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";

    const auto main_rule__def = (*( !lit(';') >> char_) >> lit(';'))[print_type()][move_assign()];

    BOOST_SPIRIT_DEFINE( main_rule_ )

    const auto entry_point = x3::skip(x3::space)[ main_rule_ ];
  }
}

int main()
{
  printf( "Give me a string to test rule.\n" );
  printf( "Type [q or Q] to quit.\n" );

  std::string input_str;
  std::string output_str;

  while (getline(std::cin, input_str))
  {
    if ( input_str.empty() || input_str[0] == 'q' || input_str[0] == 'Q')
    { break; }

    auto first = input_str.begin(), last = input_str.end();

    if ( parse( first, last, client::parser::entry_point, output_str) )
    {
      printf( "Parsing succeeded\n" );
      printf( "input:  \"%s\"\n", input_str.c_str() );
      printf( "output: \"%s\"\n", output_str.c_str() );
    }
    else
    {
      printf( "Parsing failed\n" );
    }
  }

  return 0;
}

输入始终为: abcd;

输出:

Give me a string to test rule.
Type [q or Q] to quit.
<main_rule>
  <try>abcd;</try>
print_type
_attr type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
move_assign
  <success></success>
  <attributes>[a, b, c, d]</attributes>
</main_rule>
Parsing succeeded
input:  "abcd;"
output: "abcd"

好的,到目前为止一切都很好,但是假设我想在分析结果中包括分号.我将语法行更改为:

Ok, so far all fine but assume I would like to include the semicolon in the parsed result. I change the grammar line to:

const auto main_rule__def = (*( !lit(';') >> char_) >> char_(";"))[print_type()];

注意:我删除了语义动作[move_assign()],因为它由于_attr和_val类型不兼容而无法编译.现在的输出是:

Note: I removed the semantic action [move_assign()] because it fails to compile due to incompatible _attr and _val types. Now the output is:

Give me a string to test rule.
Type [q or Q] to quit.
<main_rule>
  <try>abcd;</try>
print_type
_attr type: boost::fusion::deque<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
  <success></success>
  <attributes>[]</attributes>
</main_rule>
Parsing succeeded
input:  "abcd;"
output: ""

现在boost :: fusion :: deque<>的_attr类型不是我想要的,我就是它要成为std :: string的东西.我不明白为什么如果我在语义动作括号内的语法赋值有完整的右边,那么_attr仍然不是_val类型.X3功能transform_attribute在这里有帮助吗?我应该如何应用呢?或者,又有什么好方法可以解决此问题,而无需使用Boost Fusion类接口或其他实现细节.

Now the _attr type of boost::fusion::deque<> is not what I want, I just what it to be std::string. I don’t understand why if I have the complete right side of the grammar assignment within semantic action parentheses _attr is still not of the _val type. Would the X3 feature transform_attribute help here? And how should I apply that? Or what is another good way to solve this, without having to work with boost fusion class interfaces or other implementation details.

当前解决方法

对我来说,当前的解决方法是定义另一个规则,该规则仅通过语义动作从第一个规则中分配._attr只有std :: string类型.

The current workaround for me is to define another rule just to be assigned from the first rule with a semantic action. Only there the _attr is of std::string type.

  namespace parser
  {
    using x3::char_;
    using x3::lit;
    using namespace semantic_actions;

    x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";
    x3::rule<struct main_rule2_class, std::string> main_rule2_ = "main_rule2";

    const auto main_rule__def = *( !lit(';') >> char_) >> char_(";");
    const auto main_rule2__def = main_rule_[print_type()][move_assign()];

    BOOST_SPIRIT_DEFINE( main_rule_, main_rule2_ )

    const auto entry_point = x3::skip(x3::space)[ main_rule2_ ];
  }

输出:

Give me a string to test rule.
Type [q or Q] to quit.
<main_rule2>
  <try>abcd;</try>
  <main_rule>
    <try>abcd;</try>
    <success></success>
    <attributes>[a, b, c, d, ;]</attributes>
  </main_rule>
print_type
_attr type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
move_assign
  <success></success>
  <attributes>[a, b, c, d, ;]</attributes>
</main_rule2>
Parsing succeeded
input:  "abcd;"
output: "abcd;"

我希望有一种方法不必为了使_attr的类型与_val相匹配而制定另一条规则.

I hope there is a way without having to make another rule just to get the type of _attr to match _val.

(1)我不欣赏作者在这个库中隐藏的聪明之处.因为只有一项无辜的改变会破坏应用程序.而更明确和详尽的方法将更清楚地传达正在发生的事情.我只需要把它拿走就可以了.

(1) I don’t appreciate the hidden cleverness the authors put into this library. As just one innocent looking change can break the application. Whereas a more explicit and elaborate approach will communicate much clearer what is going on. I just have to get this off my chest.

推荐答案

直接回答

尚未为X3记录

transform_attribute ( =")( https://www.boost.org/doc/libs/1_70_0/libs/spirit/doc/html/spirit/advanced/customize/transform.html .

Direct Answer

transform_attribute is not yet documented for X3 (https://www.boost.org/doc/libs/1_70_0/libs/spirit/doc/x3/html/index.html) but you can find its Qi counterpart here: https://www.boost.org/doc/libs/1_70_0/libs/spirit/doc/html/spirit/advanced/customize/transform.html.

X3功能transform_attribute在这里有帮助吗?我应该如何应用呢?

Would the X3 feature transform_attribute help here? And how should I apply that?

无论如何,这是一个实现细节,您可以使用规则轻松访问.我喜欢使用匿名规则来解决此问题:

Regardless, it's an implementation detail that you can easily access by using rules. I like to use anonymous rules to help with this:

template <typename T>
    struct as_type {
        template <typename E>
        constexpr auto operator[](E e) const { return x3::rule<struct _, T> {} = e; }
    };

template <typename T>
    static inline constexpr as_type<T> as;

现在您可以写

const auto main_rule__def = as<std::string> [ (*(char_ - ';') >> char_(';')) ];

在Coliru上直播

#include <iostream>
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <iomanip> // std::quoted

namespace client {
    namespace x3 = boost::spirit::x3;
    namespace ascii = boost::spirit::x3::ascii;

    namespace parser {
        using x3::char_;
        using x3::lit;

        x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";

        template <typename T>
            struct as_type {
                template <typename E>
                constexpr auto operator[](E e) const { return x3::rule<struct _, T> {} = e; }
            };

        template <typename T>
            static inline constexpr as_type<T> as;

        const auto main_rule__def = as<std::string> [ (*(char_ - ';') >> char_(';')) ];

        BOOST_SPIRIT_DEFINE(main_rule_)

        const auto entry_point = x3::skip(x3::space)[main_rule_];
    } // namespace parser
} // namespace client

int main() {
    std::string output_str;
    for(std::string const input_str : { "abcd;" }) {
        auto first = input_str.begin(), last = input_str.end();

        if (parse(first, last, client::parser::entry_point, output_str)) {
            std::cout << "Parsing succeeded\n";
            std::cout << "input:  " << std::quoted(input_str) << "\n";
            std::cout << "output:  " << std::quoted(output_str) << "\n";
        } else {
            std::cout << "Parsing failed\n";
        }
    }
}

打印

Parsing succeeded
input:  "abcd;"
output:  "abcd;"

理论上可能会有性能开销,但是我强烈怀疑所有编译器都会在这里内联所有内容,因为没有任何外部链接或vtable,而且所有内容都是const/constexpr.

In theory there might be performance overhead, but I strongly suspect all compilers will inline everything here since nothing has external linkage or vtables, and everything is const/constexpr.

在这种情况下,您可以使用现有指令获得所需的行为:

In this case you could have gotten the behaviour you want using an existing directive: x3::raw

在Coliru上直播

const auto main_rule__def = x3::raw [ *(char_ - ';') >> ';' ];

不要总是使用 rule<>

仅当您具有递归规则或需要规则的外部链接(在单独的翻译单元中定义它们)时才需要.整个程序缩小到...

Don't use rule<> always

Only required if you have recursive rules or need external linkage on rules (define them in separate translation units). The whole program shrinks to ...

在Coliru上直播

#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <iomanip> // std::quoted

namespace x3 = boost::spirit::x3;
namespace client::parser {
    auto const entry_point = x3::raw [ *(x3::char_ - ';') >> ';' ];
}

int main() {
    for(std::string const input : { "abcd;" }) {
        std::string output;
        if (parse(input.begin(), input.end(), client::parser::entry_point, output)) {
            std::cout << "Parsing succeeded\n";
            std::cout << "input:  " << std::quoted(input) << "\n";
            std::cout << "output: " << std::quoted(output) << "\n";
        } else {
            std::cout << "Parsing failed\n";
        }
    }
}

最后-关于跳过

我不认为您想要 char_-';'(或更复杂的拼写方式:!lit(';')>> char _ ).使用船长,它将在空白中进行解析("ab c \ nd;" ->"abcd;"`).

Finally - About skipping

I don't think you want char_ - ';' (or the more elaborate way you spelled it: !lit(';') >> char_). With the skipper it will parse across whitespace ("ab c\nd ;" -> "abcd;"`).

您可能希望使规则更具限制性(例如 lexeme [+(graph-';')] ,甚至只是 raw [lexeme [+(alnum |'__)] lexeme [+ char _("a-zA-Z0-9 _")] ).

You would probably want to make the rule more restrictive (like lexeme [+(graph - ';')] or even simply raw[lexeme[+(alnum|'_')] or lexeme[+char_("a-zA-Z0-9_")]).

请参见 Boost Spirit队长问题

这篇关于Spirit X3,如何获取属性类型以匹配规则类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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