首页 > 解决方案 > 解析不带引号的字符串时应用 to_upper

问题描述

foo_def.hppBoost.Spirit X3 项目的文件中,我有解析器:

auto const identifier_component_unrestricted =
    lexeme[(alpha | '_') >> *(alnum | '_')];

auto const identifier_component_def =
    ((identifier_component_unrestricted - reserved_words) |
    lexeme['"' >> identifier_component_unrestricted >> '"']);

identifier_component解析为 avariant但随后折叠为单个std::string.

如何identifier_component在未引用时自动将解析的内容转换为全大写(变体中的第一种类型),但在引用时保持大小写不变(变体中的第二种类型)?

我尝试过使用语义操作,但没有成功获得可以工作/编译的东西。


编辑:感谢rmawatson提供以下解决方案。

添加文件to_upper.hpp

#pragma once

#include <boost/algorithm/string.hpp>

namespace parser {

using namespace boost::spirit::x3;

template <typename Subject>
struct ToUpperDirective : unary_parser<Subject, ToUpperDirective<Subject>> {
  using base_type = unary_parser<Subject, ToUpperDirective<Subject>>;
  using attribute_type = typename extension::as_parser<Subject>::value_type;
  static bool const has_attribute = true;
  using subject_type = Subject;

  ToUpperDirective(Subject const& subject) : base_type(subject) {}

  template <typename Iterator, typename Context, typename RContext,
            typename Attribute>
  bool parse(Iterator& first,
             Iterator const& last,
             Context const& context,
             RContext& rcontext,
             Attribute& attr) const {
    auto result = this->subject.parse(first, last, context, rcontext, attr);
    boost::to_upper(attr);
    return result;
  }
};

struct ToUpper {
  template <typename Subject>
  ToUpperDirective<typename extension::as_parser<Subject>::value_type>
      operator[](Subject const& subject) const {
    return {as_parser(subject)};
  }
};

ToUpper const to_upper;

}  // namespace parser

在原来的foo_def.hpp只是添加#include "to_upper.hpp"和:

// Convert unquoted identifier_components to upper case; keep quoted unchanged.
auto const identifier_component_def =
    to_upper[identifier_component_unrestricted - reserved_words] |
    lexeme['"' >> identifier_component_unrestricted >> '"'];

标签: c++17boost-spirit-x3

解决方案


这两个都可以只有一个std::string属性,而不需要变体。

我认为最简单的方法可能是创建自己的all_caps指令并将引用的替代方案包装在其中。

就像是..

template <typename Subject>
struct all_caps_directive : x3::unary_parser<Subject, all_caps_directive<Subject>>
{
    using base_type = x3::unary_parser<Subject, all_caps_directive<Subject> >;
    using attribute_type = typename x3::extension::as_parser<Subject>::value_type;
    static bool const has_attribute = true;
    using subject_type = Subject;

    all_caps_directive(Subject const& subject)
        : base_type(subject) {}

    template <typename Iterator, typename Context, typename RContext,typename Attribute>
    bool parse(Iterator& first, Iterator const& last
        , Context const& context, RContext& rcontext, Attribute& attr) const
    {
        auto result = this->subject.parse(first, last, context, rcontext, attr);
        boost::to_upper(attr);
        return result;
    }
};

struct all_caps_gen
{
    template <typename Subject>
    all_caps_directive<typename x3::extension::as_parser<Subject>::value_type>
        operator[](Subject const& subject) const
    {
        return { as_parser(subject) };
    }
};

auto const all_caps = all_caps_gen{};

然后像这样使用它

auto const identifier_component_def =
    (identifier_component_unrestricted |
    all_caps[lexeme['"' >> identifier_component_unrestricted >> '"']]);

演示

为了回应您对更简单的评论,这里是一个语义动作版本。我认为这不太清楚,我自己也不太好。

 auto all_caps = []( auto &ctx )
        {
            boost::to_upper( x3::_attr(ctx));
            x3::_val(ctx) = x3::_attr(ctx);
        };

并使用像..

auto const identifier_component_def =
    (identifier_component_unrestricted |
    lexeme['"' >> identifier_component_unrestricted >> '"'][all_caps]);

演示


推荐阅读