Boost Spirit 中的名称表达式没有分配给规则

Name expressions in Boost Spirit without assignment to a rule

是否可以在 Boost Spirit 中命名表达式而不分配给规则?

我知道您可以通过分配规则来命名它,例如:

using boost::spirit::standard::char_;
boost::spirit::qi::rule<> number = char_("0") | (char_("1-9") >> *char_("0-9"));
number.name("number");

这使得语法错误的调试更加容易,因为您已经可以以正确的方式命名特定部分。

但是有可能以这种方式内联吗?

using boost::spirit::standard::char_;
boost::spirit::qi::rule<> twoDigits = char_("0-9") > name(char_("0-9"), "digit");

因此,如果它得到像“3a”这样的输入,那么异常会说它期望在位置 2 有一个 "digit"(位置 2 在这里并不重要)。

另一种表达方式是:

using boost::spirit::standard::char_;
boost::spirit::qi::rule<> digit = char_("0-9");
digit.name("digit");
boost::spirit::qi::rule<> twoDigits = digit > digit;

我已经检查了源代码并发现该表达式有一个名为 what() 的函数,它 returns 一个 boost::spirit::info 可以从中检索字符串表示形式的对象。但我无法覆盖它,因为我不熟悉 Boost Proto 和 Boost Spirit 的内部结构。

您可以使用 auto 分组。但是调试名称只能附加到非终端:qi::rule<>qi::grammar<>(其他 "groupings" 无论如何都不会被跟踪,甚至 attr_cast<> 也不会被跟踪,这在语义上是接近的rule<>).

的属性传播语义

关于使用 auto 有一个很大的警告:

Proto表达式树需要深拷贝。最近的齐定义qi::copy()来做到这一点。

这是一种使用自定义指令的方法。你可以看到一个很好的解释如何做类似的事情(它是一个解析器,而不是一个指令)here

创建自定义的过程 parser/directive 可以分为四个部分:

  1. Defining/creating 在 Spirit 表达式中使用的终端。 通常你会使用 BOOST_SPIRIT_TERMINAL 除非你需要你的 parser/directive parser(whatever)/directive(whatever)[subject] 的形式。在这种情况下,您需要使用 BOOST_SPIRIT_TERMINAL_EX。这种情况特别奇怪,因为您需要与终端关联的状态,因此您需要定义一个从 terminal<tag::stateful_tag,...> 派生的结构。这通常不是必需的。我决定将所有这些都放在命名空间 custom_directive 中,但您也可以将其放在 boost::spirit::qi 中。
  2. 启用您的 parser/directive. 这里您需要在命名空间内专门化 use_terminal(对于解析器)或 use_directive(对于指令) boost::spirit.
  3. 创建实际的 parser/directive. 这个 parser/directive 需要三件事:一个 attribute<Context,Iterator>::type 关联的元函数,它说明了解析器的属性是(在这种情况下,我只是通过了主题解析器的属性);一个 parse 具有适当签名的成员函数进行真正的解析(我再次推迟到主题解析器),以及一个 what 成员函数,我们真正有兴趣修改 returns 与建设中的终端相关的任何内容。我再次决定使用命名空间 custom_directive 但你也可以将它放在 boost::spirit::qi.
  4. 将终端与您的实际 parser/directive 连接。 这需要在 boost::spirit::qi 内并且需要您专门化 make_directivemake_primitive 与您的终端标签并实例化您的实际 parser/directive。

Live on coliru

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>


//START OF expression_renamer.hpp

namespace custom_directive
{
    BOOST_SPIRIT_TERMINAL(rename_expression);

    struct expression_renamer: boost::spirit::terminal<boost::spirit::tag::stateful_tag<std::string, tag::rename_expression> >
    {
        typedef boost::spirit::tag::stateful_tag<std::string, tag::rename_expression> tag_type;

        expression_renamer(std::string const& p) : boost::spirit::terminal<tag_type>(p) {}
    };
}

namespace boost { namespace spirit
{
    template <>
    struct use_directive<qi::domain, boost::spirit::tag::stateful_tag<std::string, custom_directive::tag::rename_expression> >   // enables expression_renamer[p]
      : mpl::true_ {};
}}

namespace custom_directive
{
    template <typename Subject, typename Data>
    struct rename_directive : boost::spirit::qi::unary_parser<rename_directive<Subject,Data> >
    {
        typedef Subject subject_type;
        rename_directive(Subject const& subject_, Data const& data_)
          : subject(subject_),data(data_) {}

        template <typename Context, typename Iterator>
        struct attribute
        {
            typedef typename
                boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type
            type;
        };

        template <typename Iterator, typename Context
          , typename Skipper, typename Attribute>
        bool parse(Iterator& first, Iterator const& last, Context& context, Skipper const& skipper, Attribute& attr_) const
        {
            return subject.parse(first, last, context, skipper, attr_);
        }


        template <typename Context>
        boost::spirit::info what(Context& context) const
        {
            return boost::spirit::info(data);

        }

        Subject subject;
        Data data;
    };
}


// instantiation of the parser
namespace boost { namespace spirit { namespace qi 
{
      template<typename Data, typename Subject,typename Modifiers>
      struct make_directive<tag::stateful_tag<Data, custom_directive::tag::rename_expression>, Subject, Modifiers>
      {
        typedef custom_directive::rename_directive<Subject,Data> result_type;

        template<typename Terminal>
        result_type operator()(Terminal& term, Subject const& subject, unused_type) const 
        {
          typedef tag::stateful_tag<Data,
              custom_directive::tag::rename_expression> tag_type;
          using spirit::detail::get_stateful_data;
          return result_type(subject,get_stateful_data<tag_type>::call(term));
        }
      };
}}}


//END OF expression_renamer.hpp

template <typename Parser>
void parse(std::string const& str, Parser const& parser)
{
    std::cout << "Parsing: \"" << str << "\"" << " with `digit` `point` `digit`" << std::endl;
    std::string::const_iterator iter=str.begin(),end=str.end();

    boost::spirit::qi::parse(iter,end,parser);
}

int main()
{
    custom_directive::expression_renamer point("point");
    custom_directive::expression_renamer digit("digit");
    boost::spirit::qi::char_type char_;
    boost::spirit::qi::rule<std::string::const_iterator> twoDigitsWithPoint = char_("0-9") > point[char_('.')] > digit[char_("0-9")];
    boost::spirit::qi::on_error<boost::spirit::qi::fail>
        (
            twoDigitsWithPoint
          , std::cout
                << boost::phoenix::val("Error! Expecting ")
                << boost::spirit::qi::_4                               // what failed?
                << std::endl
        );
    parse("33",twoDigitsWithPoint);
    parse("3.a",twoDigitsWithPoint);

}