Spirit X3:解析单个字符并生成字符串的规则

Spirit X3: Rule that parses a single character and generates a string

是否可以在 Spirit X3 中创建一个解析单个字符并生成字符串的规则?

我想在版本号解析器的上下文中使用它,其中每个数字标识符可以是单个数字,也可以是非零数字后跟一个或多个数字:

auto const positive_digit = char_(L"123456789");
auto const digit = char_(L"0123456789");
auto const digits = x3::rule<class digits, std::wstring>{"digits"} = +digit;
auto const numeric_identifier = (positive_digit >> digits) | digit;

我看到的问题是 numeric_identifier 合成的类型与字符串不兼容(参见完整示例 here)。

为了解决这个问题,我需要创建一个匹配数字并合成字符串的规则。我能想到的唯一解决方案是使用语义操作,但是当在需要回溯的情况下使用规则时,这会导致错误(参见完整示例 here)。

这很棘手。我不知道是否有更好的方法来匹配字符并获取字符串作为解析输出,但一种方法是利用序列运算符 >> 将字符序列传播为字符串这一事实.

在您的规则中,由于 numeric_identifier 是一串数字或单个数字,您可以使用一个事实,即单个数字后跟输入结束符来制作一个序列,将其转换成一个字符串:

#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <string>

namespace x3 = boost::spirit::x3;

auto const positive_digit = x3::char_("123456789");
auto const digit = x3::char_("0123456789");
auto const numeric_identifier = (digit >> x3::eoi) | (positive_digit >> (+digit)) ;


int main() {
    std::string test = "1";
    std::string numeric_identifier_str;
    bool success = x3::parse(test.begin(), test.end(), numeric_identifier, numeric_identifier_str);
    std::cout << (success ? "success" : "failure") << "   " << numeric_identifier_str << "\n";

    test = "4545631";
    numeric_identifier_str = "";
    success = x3::parse(test.begin(), test.end(), numeric_identifier, numeric_identifier_str);
    std::cout << (success ? "success" : "failure") << "   " << numeric_identifier_str;
}

产量

success   1
success   44545631

我不太清楚你要做什么。如果目标是验证字符串的格式但解析完全匹配输入字符串,为什么不使用 x3::raw?

例如

auto num_id  = x3::uint_;
auto version = x3::raw[num_id % '.'];

现在可以直接将典型的版本字符串解析成字符串:

Live On Coliru

int main() {
    for (sv input : {"0", "1", "1.4", "1.6.77.0.1234",}) {
        std::string parsed;

        std::cout << "Parsing " << std::quoted(input);
         
        auto f = begin(input), l = end(input);

        if (parse(f, l, version, parsed)) {
            std::cout << " -> " << std::quoted(parsed) << "\n";
        } else {
            std::cout << " -- FAILED\n";
        }

        if (f != l) {
            std::cout << "Remaining unparsed: " << std::quoted(sv{f, l}) << "\n";
        }
    }
}

版画

Parsing "0" -> "0"
Parsing "1" -> "1"
Parsing "1.4" -> "1.4"
Parsing "1.6.77.0.1234" -> "1.6.77.0.1234"

添加 ID 号码不以 0 开头的限制,除非它们是字面上的 zero:

auto num_id  = x3::char_('0') | x3::uint_;

当然你可以不那么聪明,也可以更直率:

auto num_id
    = !x3::lit('0') >> x3::uint_
    | x3::uint_parser<unsigned, 10, 1, 1>{};

效果是一样的。我比较喜欢第一个。