C++ 使用Boost spirit解析任意精度的数字

C++ 使用Boost spirit解析任意精度的数字,c++,boost,llvm,boost-spirit,boost-spirit-qi,C++,Boost,Llvm,Boost Spirit,Boost Spirit Qi,我想编写一个Boost Spirit Qi解析器,它可以解析任意C整数文本(例如1234或0x1234ULL),并将它们转换为任意精度llvm::APInt值 我想,要做到这一点,我需要为十进制、十六进制等文本组合单独的解析器 以后者为例,解析器需要识别标记0xNN…NS,其中N是十六进制数字,S是有效的文字后缀 构造这样一个解析器很容易,但我让它“丢弃”前缀和后缀,并返回转换为llvm::APInt值的剩余数字,与返回无符号整数的方式相同,例如qi::uint\ucode>返回无符号整数 我知

我想编写一个Boost Spirit Qi解析器,它可以解析任意C整数文本(例如
1234
0x1234ULL
),并将它们转换为任意精度
llvm::APInt

我想,要做到这一点,我需要为十进制、十六进制等文本组合单独的解析器

以后者为例,解析器需要识别标记
0xNN…NS
,其中
N
是十六进制数字,
S
是有效的文字后缀

构造这样一个解析器很容易,但我让它“丢弃”前缀和后缀,并返回转换为
llvm::APInt
值的剩余数字,与返回无符号整数的方式相同,例如
qi::uint\ucode>返回无符号整数


我知道有
qi::uint\u解析器
,但该类似乎非常有限,因为它似乎是从整数而不是字符串构建结果的。这是其他解析器生成器的一个主要功能,因此我很惊讶文档会掩盖这一点。

我认为是解析器生成器的主要功能,实际上是解析为任意类型的整数

你所追求的是更多:你想要解析成一种表示任意类型的整数,并根据语法中的决定添加语义信息

这些决定不能烘焙到解析器生成器中,因为这会将其与特定类型的语法联系起来

当然,你也可以这样做。让我一步一步走过去

1.主食 正如你们所注意到的,勇气就是这样做的。让我们来演示一下基础知识

松散地

3.应用于LLVM-APInt 机制是一样的:

struct converter_f {
    template <typename T> static auto as(uint64_t raw) {
        return llvm::APInt(raw, CHAR_BIT * sizeof(T), std::is_signed_v<T>);
    }
    llvm::APInt operator()(uintmax_t raw, Suffix sfx) const {
        switch (sfx) {
        case Suffix::signed_:   return as<signed>(raw);
        case Suffix::unsigned_: return as<unsigned>(raw);
        case Suffix::long_:     return as<long>(raw);
        case Suffix::longlong_: return as<long long>(raw);
        case Suffix::ul_:       return as<unsigned long>(raw);
        case Suffix::ull_:      return as<unsigned long long>(raw);
        }
        throw std::invalid_argument("sfx");
    }
};
剩余未解决的问题
  • 当然,对于语义操作,实际上可以使用
    fromString
    factory方法解析字符串表示

  • 我不知道如何准确地询问它是否有签名。我怀疑我应该解析成一个
    变量,因为它没有被指定。而且它似乎并不是任何“主食”功能的一部分


由于八进制分支,正确解析“0”的边缘情况,并简化了第一个示例。此解决方案的最大问题是它将解析器与主机平台类型宽度联系起来。@NikitaKniazev(a)不正确(只是我为阐述所做的选择)(b)似乎是既定目标(“我想编写一个Boost Spirit Qi解析器,它可以解析任意C整数文本(例如1234或0x1234ULL)”只需考虑使用Boost <代码> CppyIt并从其中抛出<代码> IMXMAXT 。如果您想要的话,它甚至还可以做位字段。Dang,我希望我可以投票两次,这是比我预期的更广泛的答案。Sype应该能够解析出框中无界的自定义整数(如<代码> uTunpPalSeS/<代码>)。,如果没有,请提交一份错误报告。@NikitaKniazev我认为您没有查看该类型或其文档。它不是一个通用的可替换整数类型。我几乎在发布我的答案的同时发布了您的comment@sehe是的,它说APInt是普通无符号整数的函数替换类型…APInt提供多种算术运算符和方法来操作任意位宽的整数值。它支持典型的整数算术和比较运算以及按位操作。
@NikitaKniazev是的,这就像营销宣传。注意,他们没有说明支持opera的接口选项。我喜欢在阅读接口时想象自己在使用一个类型:@sehe它并没有说该类型支持隐式转换。我还没有让Spirit支持此类类型。此外,还有一个解析构造函数,不管出于什么原因,这里没有人提到它。
template <typename Integer> void test() {
    std::cout << " ---- " << __PRETTY_FUNCTION__ << "\n";
    using It = std::string::const_iterator;
    IntLiteral<It, Integer> const parser {};

    for (std::string const input : {
             "1234",
             "1234u",
             "0x12f34ULL",
             "033ULL",
             "0b101011l",
             "33lu"
         }) {
        Integer value;
        if (parse(input.begin(), input.end(), parser >> qi::eoi, value)) {
            std::cout << "Parsed " << std::quoted(input) << " -> " << value << "\n";
        } else {
            std::cout << "Failed to parse " << std::quoted(input) << "\n";
        }
    }
}

int main() {
    test<std::uintmax_t>();
    test<boost::multiprecision::checked_int1024_t>();
}
 ---- void test() [with Integer = long unsigned int]
Parsed "1234" -> 1234
Parsed "1234u" -> 1234
Parsed "0x12f34ULL" -> 77620
Parsed "033ULL" -> 27
Parsed "0b101011l" -> 43
Parsed "33lu" -> 33
 ---- void test() [with Integer = boost::multiprecision::number<boost::multiprecision::backend
s::cpp_int_backend<1024, 1024, boost::multiprecision::signed_magnitude, boost::multiprecision:
:checked, void> >]
Parsed "1234" -> 1234
Parsed "1234u" -> 1234
Parsed "0x12f34ULL" -> 77620
Parsed "033ULL" -> 27
Parsed "0b101011l" -> 43
Parsed "33lu" -> 33
using CxxInteger = boost::variant<
    signed, unsigned, 
    signed long, unsigned long,
    signed long long, unsigned long long>;
using Raw = std::uintmax_t;

_start = no_case [ // case insensitive
    ("0x"       >> uint_parser<Raw, 16>{} |
     "0b"       >> uint_parser<Raw,  2>{} |
     &lit('0')  >> uint_parser<Raw,  8>{} |
                   uint_parser<Raw, 10>{})
    // ignored for now:
    >> _optsuffix
] [ _val = coerce_type(_1, _2) ];

_optsuffix = no_case[_suffix] | attr(Suffix::signed_);
struct converter_f {
    CxxInteger operator()(uintmax_t raw, Suffix sfx) const {
        switch (sfx) {
          case Suffix::signed_:   return static_cast<signed>(raw);
          case Suffix::unsigned_: return static_cast<unsigned>(raw);
          case Suffix::long_:     return static_cast<long>(raw);
          case Suffix::longlong_: return static_cast<long long>(raw);
          case Suffix::ul_:       return static_cast<unsigned long>(raw);
          case Suffix::ull_:      return static_cast<unsigned long long>(raw);
        }
        throw std::invalid_argument("sfx");
    }
};
boost::phoenix::function<converter_f> coerce_type;
std::cout << "Parsed " << std::quoted(input) << " -> " << value
          << " (type #" << value.which() << " "
          << boost::core::demangle(value.type().name()) << ")\n";
 ---- void test()
Parsed "1234" -> 1234 (type #0 int)
Parsed "1234u" -> 1234 (type #1 unsigned int)
Parsed "0x12f34ULL" -> 77620 (type #5 unsigned long long)
Parsed "033ULL" -> 27 (type #5 unsigned long long)
Parsed "0b101011l" -> 43 (type #2 long)
Parsed "33lu" -> 33 (type #3 unsigned long)
struct converter_f {
    template <typename T> static auto as(uint64_t raw) {
        return llvm::APInt(raw, CHAR_BIT * sizeof(T), std::is_signed_v<T>);
    }
    llvm::APInt operator()(uintmax_t raw, Suffix sfx) const {
        switch (sfx) {
        case Suffix::signed_:   return as<signed>(raw);
        case Suffix::unsigned_: return as<unsigned>(raw);
        case Suffix::long_:     return as<long>(raw);
        case Suffix::longlong_: return as<long long>(raw);
        case Suffix::ul_:       return as<unsigned long>(raw);
        case Suffix::ull_:      return as<unsigned long long>(raw);
        }
        throw std::invalid_argument("sfx");
    }
};
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
#include <llvm/ADT/APInt.h>
namespace qi = boost::spirit::qi;

template <typename It>
struct IntLiteral : qi::grammar<It, llvm::APInt()> {
    IntLiteral() : IntLiteral::base_type(_start) {
        using namespace qi;
        using Raw = std::uint64_t;

        _start = no_case [ // case insensitive
            ("0x"      >> uint_parser<Raw, 16>{} |
            "0b"      >> uint_parser<Raw,  2>{} |
            &lit('0') >> uint_parser<Raw,  8>{} |
                        uint_parser<Raw, 10>{})
            // ignored for now:
            >> _optsuffix
        ] [ _val = coerce_type(_1, _2) ];

        _optsuffix = no_case[_suffix] | attr(Suffix::signed_);
    }

private:
    enum class Suffix {
        signed_   = 0,
        unsigned_ = 1,
        long_     = 2,
        longlong_ = 4,

        l_   = long_,
        ll_  = longlong_,
        ul_  = unsigned_ | l_,
        ull_ = unsigned_ | ll_,
    };

    struct suffix_sym : qi::symbols<char, Suffix> {
        suffix_sym() {
            this->add
                ("u",   Suffix::unsigned_)
                ("l",   Suffix::l_)
                ("ll",  Suffix::ll_)
                ("ul",  Suffix::ul_)  ("lu",  Suffix::ul_)
                ("ull", Suffix::ull_) ("llu", Suffix::ull_)
            ;
        }
    } _suffix;

    struct converter_f {
        template <typename T> static auto as(uint64_t raw) {
            return llvm::APInt(CHAR_BIT * sizeof(T), raw, std::is_signed_v<T>);
        }
        llvm::APInt operator()(uint64_t raw, Suffix sfx) const {
            switch (sfx) {
            case Suffix::signed_:   return as<signed>(raw);
            case Suffix::unsigned_: return as<unsigned>(raw);
            case Suffix::long_:     return as<long>(raw);
            case Suffix::longlong_: return as<long long>(raw);
            case Suffix::ul_:       return as<unsigned long>(raw);
            case Suffix::ull_:      return as<unsigned long long>(raw);
            }
            throw std::invalid_argument("sfx");
        }
    };
    boost::phoenix::function<converter_f> coerce_type;

    qi::rule<It, llvm::APInt()> _start;
    qi::rule<It, Suffix()>      _optsuffix;
};

void test() {
    std::cout << " ---- " << __PRETTY_FUNCTION__ << "\n";
    using It = std::string::const_iterator;
    IntLiteral<It> const parser {};

    for (std::string const input : {
            "1234",
            "1234u",
            "0x12f34ULL",
            "033ULL",
            "0b101011l",
            "33lu"
        }) {
        llvm::APInt value;
        if (parse(input.begin(), input.end(), parser >> qi::eoi, value)) {
            std::cout << "Parsed " << std::quoted(input) << " -> "
                    << value.toString(10, false) // TODO signed?
                    << " bits:" << value.getBitWidth() << "\n";
        } else {
            std::cout << "Failed to parse " << std::quoted(input) << "\n";
        }
    }
}

int main() {
    test();
}
 ---- void test()
Parsed "1234" -> 1234 bits:32
Parsed "1234u" -> 1234 bits:32
Parsed "0x12f34ULL" -> 77620 bits:64
Parsed "033ULL" -> 27 bits:64
Parsed "0b101011l" -> 43 bits:64
Parsed "33lu" -> 33 bits:64