C++ 使用Boost spirit解析任意精度的数字
我想编写一个Boost Spirit Qi解析器,它可以解析任意C整数文本(例如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>返回无符号整数 我知
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