Add support for octal numbers (#6909)

This implements the leads decision made in #6821, proposal #6910. The
proposal is pending, but I figured it's relatively safe to just do given
the decision.

Assisted-by: Google Antigravity with Gemini

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
Jon Ross-Perkins
2026-03-16 21:28:37 +00:00
committed by GitHub
co-authored by Chandler Carruth
parent c5761d2d16
commit 613a139bef
6 changed files with 153 additions and 87 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ CARBON_DIAGNOSTIC_KIND(ErrorReadingFile)
// Lexer diagnostics
// ============================================================================
CARBON_DIAGNOSTIC_KIND(BinaryRealLiteral)
CARBON_DIAGNOSTIC_KIND(InvalidRealLiteralRadix)
CARBON_DIAGNOSTIC_KIND(ContentBeforeStringTerminator)
CARBON_DIAGNOSTIC_KIND(DecimalEscapeSequence)
CARBON_DIAGNOSTIC_KIND(DumpSemIRRangeMissingEnd)
+43 -35
View File
@@ -159,6 +159,8 @@ NumericLiteral::Parser::Parser(Diagnostics::Emitter<const char*>& emitter,
radix_ = Radix::Hexadecimal;
} else if (int_part_.consume_front("0b")) {
radix_ = Radix::Binary;
} else if (int_part_.consume_front("0o")) {
radix_ = Radix::Octal;
}
fract_part_ = literal.text_.substr(
@@ -190,16 +192,28 @@ static auto ParseBinary(llvm::StringRef digits) -> llvm::APInt {
return value;
}
// Parses a hexadecimal integer literal.
static auto ParseHexadecimal(llvm::StringRef digits) -> llvm::APInt {
llvm::APInt value(std::max<int>(IntStore::MinAPWidth, digits.size() * 4), 0);
int cursor = digits.size() * 4 - 1;
// Parses an octal or hexadecimal integer literal.
template <NumericLiteral::Radix Radix>
requires(Radix == NumericLiteral::Radix::Hexadecimal ||
Radix == NumericLiteral::Radix::Octal)
static auto ParseOctalOrHexadecimal(llvm::StringRef digits) -> llvm::APInt {
constexpr int BitsPerDigit =
Radix == NumericLiteral::Radix::Hexadecimal ? 4 : 3;
llvm::APInt value(
std::max<int>(IntStore::MinAPWidth, digits.size() * BitsPerDigit), 0);
int cursor = digits.size() * BitsPerDigit - 1;
for (char c : digits) {
uint8_t digit = c <= '9' ? (c - '0') : (c - 'A' + 10);
if (digit & 0x8) {
value.setBit(cursor);
uint8_t digit;
if constexpr (Radix == NumericLiteral::Radix::Octal) {
digit = c - '0';
} else {
digit = c <= '9' ? (c - '0') : (c - 'A' + 10);
if (digit & 0x8) {
value.setBit(cursor);
}
--cursor;
}
--cursor;
if (digit & 0x4) {
value.setBit(cursor);
}
@@ -289,10 +303,13 @@ static auto ParseInt(llvm::StringRef digits, NumericLiteral::Radix radix,
switch (radix) {
case NumericLiteral::Radix::Binary:
return ParseBinary(digits);
case NumericLiteral::Radix::Octal:
return ParseOctalOrHexadecimal<NumericLiteral::Radix::Octal>(digits);
case NumericLiteral::Radix::Decimal:
return ParseDecimal(digits);
case NumericLiteral::Radix::Hexadecimal:
return ParseHexadecimal(digits);
return ParseOctalOrHexadecimal<NumericLiteral::Radix::Hexadecimal>(
digits);
}
}
@@ -326,6 +343,8 @@ auto NumericLiteral::Parser::GetExponent() -> llvm::APInt {
int excess_exponent = fract_part_.size();
if (radix_ == Radix::Hexadecimal) {
excess_exponent *= 4;
} else if (radix_ == Radix::Octal) {
excess_exponent *= 3;
}
exponent -= excess_exponent;
if (exponent_is_negative_ && !exponent.isNegative()) {
@@ -347,22 +366,9 @@ auto NumericLiteral::Parser::CheckDigitSequence(llvm::StringRef text,
bool allow_digit_separators)
-> CheckDigitSequenceResult {
std::bitset<256> valid_digits;
switch (radix) {
case Radix::Binary:
for (char c : "01") {
valid_digits[static_cast<unsigned char>(c)] = true;
}
break;
case Radix::Decimal:
for (char c : "0123456789") {
valid_digits[static_cast<unsigned char>(c)] = true;
}
break;
case Radix::Hexadecimal:
for (char c : "0123456789ABCDEF") {
valid_digits[static_cast<unsigned char>(c)] = true;
}
break;
static constexpr llvm::StringLiteral Digits = "0123456789ABCDEF";
for (char c : Digits.take_front(static_cast<int>(radix))) {
valid_digits[static_cast<unsigned char>(c)] = true;
}
int num_digit_separators = 0;
@@ -386,11 +392,11 @@ auto NumericLiteral::Parser::CheckDigitSequence(llvm::StringRef text,
continue;
}
CARBON_DIAGNOSTIC(
InvalidDigit, Error,
"invalid digit '{0}' in {1:=2:binary|=10:decimal|=16:hexadecimal} "
"numeric literal",
char, Diagnostics::IntAsSelect);
CARBON_DIAGNOSTIC(InvalidDigit, Error,
"invalid digit '{0}' in "
"{1:=2:binary|=8:octal|=10:decimal|=16:hexadecimal} "
"numeric literal",
char, Diagnostics::IntAsSelect);
emitter_.Emit(text.begin() + i, InvalidDigit, c, static_cast<int>(radix));
return {.ok = false};
}
@@ -435,12 +441,14 @@ auto NumericLiteral::Parser::CheckFractionalPart() -> bool {
return true;
}
if (radix_ == Radix::Binary) {
CARBON_DIAGNOSTIC(BinaryRealLiteral, Error,
"binary real number literals are not supported");
if (radix_ == Radix::Binary || radix_ == Radix::Octal) {
CARBON_DIAGNOSTIC(
InvalidRealLiteralRadix, Error,
"{0:=2:binary|=8:octal} real number literals are not supported",
Diagnostics::IntAsSelect);
emitter_.Emit(literal_.text_.begin() + literal_.radix_point_,
BinaryRealLiteral);
// Carry on and parse the binary real literal anyway.
InvalidRealLiteralRadix, static_cast<int>(radix_));
// Carry on and parse the real literal anyway.
}
// We need to remove a '.' from the mantissa.
+6 -1
View File
@@ -17,7 +17,12 @@ namespace Carbon::Lex {
// A numeric literal token that has been extracted from a source buffer.
class NumericLiteral {
public:
enum class Radix : int8_t { Binary = 2, Decimal = 10, Hexadecimal = 16 };
enum class Radix : int8_t {
Binary = 2,
Octal = 8,
Decimal = 10,
Hexadecimal = 16
};
// Value of an integer literal.
struct IntValue {
+22 -8
View File
@@ -15,14 +15,17 @@ namespace {
// Returns an integer literal string with `prefix` followed by `num_digits`
// entries from `digits` (repeating `digits` as necessary).
static auto MakeIntString(llvm::StringLiteral prefix,
llvm::StringLiteral digits, size_t num_digits)
static auto MakeIntString(llvm::StringLiteral prefix, int radix, int num_digits)
-> std::string {
// Digits are reversed so that we can take `radix` digits from the end, and
// never have 0 be the first digit.
static constexpr llvm::StringLiteral Digits = "FEDCBA9876543210";
std::string s;
s.reserve(prefix.size() + num_digits);
s.append(prefix);
for (size_t i = 0; i < num_digits; i += digits.size()) {
s.append(digits.take_front(std::min(digits.size(), num_digits - i)));
for (int i = 0; i < num_digits; i += radix) {
s.append(Digits.take_back(std::min(radix, num_digits - i)));
}
return s;
}
@@ -40,7 +43,7 @@ static void BM_Lex_Int(benchmark::State& state) {
}
static void BM_Lex_IntDecimalN(benchmark::State& state) {
std::string s = MakeIntString("", "1234567890", state.range(0));
std::string s = MakeIntString("", 10, state.range(0));
for (auto _ : state) {
CARBON_CHECK(NumericLiteral::Lex(s, true));
}
@@ -65,7 +68,7 @@ static void BM_ComputeValue_Int(benchmark::State& state) {
}
static void BM_ComputeValue_IntDecimalN(benchmark::State& state) {
std::string s = MakeIntString("", "1234567890", state.range(0));
std::string s = MakeIntString("", 10, state.range(0));
auto val = NumericLiteral::Lex(s, true);
auto& emitter = Diagnostics::NullEmitter<const char*>();
CARBON_CHECK(val);
@@ -75,7 +78,17 @@ static void BM_ComputeValue_IntDecimalN(benchmark::State& state) {
}
static void BM_ComputeValue_IntBinaryN(benchmark::State& state) {
std::string s = MakeIntString("0b", "10", state.range(0));
std::string s = MakeIntString("0b", 2, state.range(0));
auto val = NumericLiteral::Lex(s, true);
auto& emitter = Diagnostics::NullEmitter<const char*>();
CARBON_CHECK(val);
for (auto _ : state) {
val->ComputeValue(emitter);
}
}
static void BM_ComputeValue_IntOctalN(benchmark::State& state) {
std::string s = MakeIntString("0o", 8, state.range(0));
auto val = NumericLiteral::Lex(s, true);
auto& emitter = Diagnostics::NullEmitter<const char*>();
CARBON_CHECK(val);
@@ -86,7 +99,7 @@ static void BM_ComputeValue_IntBinaryN(benchmark::State& state) {
static void BM_ComputeValue_IntHexN(benchmark::State& state) {
// 0 is in the middle so that it isn't truncated in parse.
std::string s = MakeIntString("0x", "1234567890ABCDEF", state.range(0));
std::string s = MakeIntString("0x", 16, state.range(0));
auto val = NumericLiteral::Lex(s, true);
auto& emitter = Diagnostics::NullEmitter<const char*>();
CARBON_CHECK(val);
@@ -102,6 +115,7 @@ BENCHMARK(BM_ComputeValue_Float);
BENCHMARK(BM_ComputeValue_Int);
BENCHMARK(BM_ComputeValue_IntDecimalN)->RangeMultiplier(10)->Range(1, 10000);
BENCHMARK(BM_ComputeValue_IntBinaryN)->RangeMultiplier(10)->Range(1, 10000);
BENCHMARK(BM_ComputeValue_IntOctalN)->RangeMultiplier(10)->Range(1, 10000);
BENCHMARK(BM_ComputeValue_IntHexN)->RangeMultiplier(10)->Range(1, 10000);
} // namespace
+29 -5
View File
@@ -95,6 +95,7 @@ TEST_F(NumericLiteralTest, HandlesIntLiteral) {
{.token = "12", .value = 12, .radix = 10},
{.token = "0x12_3ABC", .value = 0x12'3ABC, .radix = 16},
{.token = "0b10_10_11", .value = 0b10'10'11, .radix = 2},
{.token = "0o12_345", .value = 012345, .radix = 8},
{.token = "1_234_567", .value = 1'234'567, .radix = 10},
};
for (bool can_form_real_literal : {false, true}) {
@@ -122,6 +123,10 @@ TEST_F(NumericLiteralTest, ValidatesBaseSpecifier) {
// Binary integer literals.
"0b10110100101001010",
"0b0000000",
// Octal integer literals.
"0o01234567",
"0o0000000",
};
for (llvm::StringLiteral literal : valid) {
error_tracker.Reset();
@@ -130,10 +135,9 @@ TEST_F(NumericLiteralTest, ValidatesBaseSpecifier) {
}
llvm::StringLiteral invalid[] = {
"00", "0X123", "0o123", "0B1",
"007", "123L", "123456789A", "0x",
"0b", "0x123abc", "0b011101201001", "0b10A",
"0x_", "0b_",
"00", "0X123", "0O123", "0B1", "007", "123L",
"123456789A", "0x", "0b", "0o", "0x123abc", "0b011101201001",
"0b10A", "0x_", "0b_", "0o_", "0o1238", "0o12A",
};
for (llvm::StringLiteral literal : invalid) {
error_tracker.Reset();
@@ -162,6 +166,10 @@ TEST_F(NumericLiteralTest, ValidatesIntDigitSeparators) {
// Binary literals.
"0b1_0_1_0_1_0",
"0b111_0000",
// Octal literals.
"0o1_234",
"0o12_34",
};
for (llvm::StringLiteral literal : valid) {
error_tracker.Reset();
@@ -184,6 +192,11 @@ TEST_F(NumericLiteralTest, ValidatesIntDigitSeparators) {
"0b1__01",
"0b1011_",
"0b1_01_01_",
// Octal literals.
"0o_1234",
"0o123_",
"0o1_2__34",
};
for (llvm::StringLiteral literal : invalid) {
error_tracker.Reset();
@@ -275,6 +288,14 @@ TEST_F(NumericLiteralTest, HandlesRealLiteral) {
.exponent = -2,
.radix = 2,
.int_value = 0b101101},
// Octal real literals. These are invalid, but we accept them for error
// recovery.
{.token = "0o12_34.56",
.mantissa = 0123456,
.exponent = -6,
.radix = 8,
.int_value = 01234},
};
// Check we get the right real value.
for (Testcase testcase : testcases) {
@@ -284,7 +305,8 @@ TEST_F(NumericLiteralTest, HandlesRealLiteral) {
.mantissa = IsUnsignedInt(testcase.mantissa),
.exponent = IsSignedInt(testcase.exponent)}))
<< testcase.token;
EXPECT_EQ(error_tracker.seen_error(), testcase.radix == 2)
EXPECT_EQ(error_tracker.seen_error(),
testcase.radix == 2 || testcase.radix == 8)
<< testcase.token;
}
// If we are required to stop at the `.` character, check we get the right int
@@ -319,6 +341,8 @@ TEST_F(NumericLiteralTest, ValidatesRealLiterals) {
"0b.0",
"0x_.0",
"0b_.0",
"0o.0",
"0o_.0",
// No digits in fractional part.
"0.e",
+52 -37
View File
@@ -18,7 +18,7 @@ fn F() {
// CHECK:STDOUT: - { index: 2, kind: "Identifier", line: {{ *}}[[@LINE-2]], column: 4, indent: 1, spelling: "F", identifier: 0, has_leading_space: true }
// CHECK:STDOUT: - { index: 3, kind: "OpenParen", line: {{ *}}[[@LINE-3]], column: 5, indent: 1, spelling: "(", closing_token: 4 }
// CHECK:STDOUT: - { index: 4, kind: "CloseParen", line: {{ *}}[[@LINE-4]], column: 6, indent: 1, spelling: ")", opening_token: 3 }
// CHECK:STDOUT: - { index: 5, kind: "OpenCurlyBrace", line: {{ *}}[[@LINE-5]], column: 8, indent: 1, spelling: "{", closing_token: 58, has_leading_space: true }
// CHECK:STDOUT: - { index: 5, kind: "OpenCurlyBrace", line: {{ *}}[[@LINE-5]], column: 8, indent: 1, spelling: "{", closing_token: 60, has_leading_space: true }
// 8 and 9 trigger special behavior in APInt when mishandling signed versus
// unsigned, so we pay extra attention to those.
var ints: array(i32, 6) = (
@@ -32,7 +32,7 @@ fn F() {
// CHECK:STDOUT: - { index: 13, kind: "IntLiteral", line: {{ *}}[[@LINE-8]], column: 24, indent: 3, spelling: "6", value: "6", has_leading_space: true }
// CHECK:STDOUT: - { index: 14, kind: "CloseParen", line: {{ *}}[[@LINE-9]], column: 25, indent: 3, spelling: ")", opening_token: 10 }
// CHECK:STDOUT: - { index: 15, kind: "Equal", line: {{ *}}[[@LINE-10]], column: 27, indent: 3, spelling: "=", has_leading_space: true }
// CHECK:STDOUT: - { index: 16, kind: "OpenParen", line: {{ *}}[[@LINE-11]], column: 29, indent: 3, spelling: "(", closing_token: 29, has_leading_space: true }
// CHECK:STDOUT: - { index: 16, kind: "OpenParen", line: {{ *}}[[@LINE-11]], column: 29, indent: 3, spelling: "(", closing_token: 31, has_leading_space: true }
8,
// CHECK:STDOUT: - { index: 17, kind: "IntLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "8", value: "8", has_leading_space: true }
// CHECK:STDOUT: - { index: 18, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 6, indent: 5, spelling: "," }
@@ -45,66 +45,81 @@ fn F() {
0b1000,
// CHECK:STDOUT: - { index: 23, kind: "IntLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "0b1000", value: "8", has_leading_space: true }
// CHECK:STDOUT: - { index: 24, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 11, indent: 5, spelling: "," }
0o10,
// CHECK:STDOUT: - { index: 25, kind: "IntLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "0o10", value: "8", has_leading_space: true }
// CHECK:STDOUT: - { index: 26, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 9, indent: 5, spelling: "," }
39999999999999999993,
// CHECK:STDOUT: - { index: 25, kind: "IntLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "39999999999999999993", value: "39999999999999999993", has_leading_space: true }
// CHECK:STDOUT: - { index: 26, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 25, indent: 5, spelling: "," }
// CHECK:STDOUT: - { index: 27, kind: "IntLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "39999999999999999993", value: "39999999999999999993", has_leading_space: true }
// CHECK:STDOUT: - { index: 28, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 25, indent: 5, spelling: "," }
12345678901234567890123456789012345678901234567890123456789,
// CHECK:STDOUT: - { index: 27, kind: "IntLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "12345678901234567890123456789012345678901234567890123456789", value: "12345678901234567890123456789012345678901234567890123456789", has_leading_space: true }
// CHECK:STDOUT: - { index: 28, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 64, indent: 5, spelling: "," }
// CHECK:STDOUT: - { index: 29, kind: "IntLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "12345678901234567890123456789012345678901234567890123456789", value: "12345678901234567890123456789012345678901234567890123456789", has_leading_space: true }
// CHECK:STDOUT: - { index: 30, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 64, indent: 5, spelling: "," }
);
// CHECK:STDOUT: - { index: 29, kind: "CloseParen", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: ")", opening_token: 16, has_leading_space: true }
// CHECK:STDOUT: - { index: 30, kind: "Semi", line: {{ *}}[[@LINE-2]], column: 4, indent: 3, spelling: ";" }
// CHECK:STDOUT: - { index: 31, kind: "CloseParen", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: ")", opening_token: 16, has_leading_space: true }
// CHECK:STDOUT: - { index: 32, kind: "Semi", line: {{ *}}[[@LINE-2]], column: 4, indent: 3, spelling: ";" }
var floats: array(f64, 7) = (
// CHECK:STDOUT: - { index: 31, kind: "Var", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: "var", has_leading_space: true }
// CHECK:STDOUT: - { index: 32, kind: "Identifier", line: {{ *}}[[@LINE-2]], column: 7, indent: 3, spelling: "floats", identifier: 2, has_leading_space: true }
// CHECK:STDOUT: - { index: 33, kind: "Colon", line: {{ *}}[[@LINE-3]], column: 13, indent: 3, spelling: ":" }
// CHECK:STDOUT: - { index: 34, kind: "Array", line: {{ *}}[[@LINE-4]], column: 15, indent: 3, spelling: "array", has_leading_space: true }
// CHECK:STDOUT: - { index: 35, kind: "OpenParen", line: {{ *}}[[@LINE-5]], column: 20, indent: 3, spelling: "(", closing_token: 39 }
// CHECK:STDOUT: - { index: 36, kind: "FloatTypeLiteral", line: {{ *}}[[@LINE-6]], column: 21, indent: 3, spelling: "f64" }
// CHECK:STDOUT: - { index: 37, kind: "Comma", line: {{ *}}[[@LINE-7]], column: 24, indent: 3, spelling: "," }
// CHECK:STDOUT: - { index: 38, kind: "IntLiteral", line: {{ *}}[[@LINE-8]], column: 26, indent: 3, spelling: "7", value: "7", has_leading_space: true }
// CHECK:STDOUT: - { index: 39, kind: "CloseParen", line: {{ *}}[[@LINE-9]], column: 27, indent: 3, spelling: ")", opening_token: 35 }
// CHECK:STDOUT: - { index: 40, kind: "Equal", line: {{ *}}[[@LINE-10]], column: 29, indent: 3, spelling: "=", has_leading_space: true }
// CHECK:STDOUT: - { index: 41, kind: "OpenParen", line: {{ *}}[[@LINE-11]], column: 31, indent: 3, spelling: "(", closing_token: 56, has_leading_space: true }
// CHECK:STDOUT: - { index: 33, kind: "Var", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: "var", has_leading_space: true }
// CHECK:STDOUT: - { index: 34, kind: "Identifier", line: {{ *}}[[@LINE-2]], column: 7, indent: 3, spelling: "floats", identifier: 2, has_leading_space: true }
// CHECK:STDOUT: - { index: 35, kind: "Colon", line: {{ *}}[[@LINE-3]], column: 13, indent: 3, spelling: ":" }
// CHECK:STDOUT: - { index: 36, kind: "Array", line: {{ *}}[[@LINE-4]], column: 15, indent: 3, spelling: "array", has_leading_space: true }
// CHECK:STDOUT: - { index: 37, kind: "OpenParen", line: {{ *}}[[@LINE-5]], column: 20, indent: 3, spelling: "(", closing_token: 41 }
// CHECK:STDOUT: - { index: 38, kind: "FloatTypeLiteral", line: {{ *}}[[@LINE-6]], column: 21, indent: 3, spelling: "f64" }
// CHECK:STDOUT: - { index: 39, kind: "Comma", line: {{ *}}[[@LINE-7]], column: 24, indent: 3, spelling: "," }
// CHECK:STDOUT: - { index: 40, kind: "IntLiteral", line: {{ *}}[[@LINE-8]], column: 26, indent: 3, spelling: "7", value: "7", has_leading_space: true }
// CHECK:STDOUT: - { index: 41, kind: "CloseParen", line: {{ *}}[[@LINE-9]], column: 27, indent: 3, spelling: ")", opening_token: 37 }
// CHECK:STDOUT: - { index: 42, kind: "Equal", line: {{ *}}[[@LINE-10]], column: 29, indent: 3, spelling: "=", has_leading_space: true }
// CHECK:STDOUT: - { index: 43, kind: "OpenParen", line: {{ *}}[[@LINE-11]], column: 31, indent: 3, spelling: "(", closing_token: 58, has_leading_space: true }
0.9,
// CHECK:STDOUT: - { index: 42, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "0.9", value: "9*10^-1", has_leading_space: true }
// CHECK:STDOUT: - { index: 43, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 8, indent: 5, spelling: "," }
8.0,
// CHECK:STDOUT: - { index: 44, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "8.0", value: "80*10^-1", has_leading_space: true }
// CHECK:STDOUT: - { index: 44, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "0.9", value: "9*10^-1", has_leading_space: true }
// CHECK:STDOUT: - { index: 45, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 8, indent: 5, spelling: "," }
8.0,
// CHECK:STDOUT: - { index: 46, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "8.0", value: "80*10^-1", has_leading_space: true }
// CHECK:STDOUT: - { index: 47, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 8, indent: 5, spelling: "," }
80.0,
// CHECK:STDOUT: - { index: 46, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "80.0", value: "800*10^-1", has_leading_space: true }
// CHECK:STDOUT: - { index: 47, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 9, indent: 5, spelling: "," }
// CHECK:STDOUT: - { index: 48, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "80.0", value: "800*10^-1", has_leading_space: true }
// CHECK:STDOUT: - { index: 49, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 9, indent: 5, spelling: "," }
1.0e7,
// CHECK:STDOUT: - { index: 48, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "1.0e7", value: "10*10^6", has_leading_space: true }
// CHECK:STDOUT: - { index: 49, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 10, indent: 5, spelling: "," }
1.0e8,
// CHECK:STDOUT: - { index: 50, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "1.0e8", value: "10*10^7", has_leading_space: true }
// CHECK:STDOUT: - { index: 50, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "1.0e7", value: "10*10^6", has_leading_space: true }
// CHECK:STDOUT: - { index: 51, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 10, indent: 5, spelling: "," }
1.0e8,
// CHECK:STDOUT: - { index: 52, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "1.0e8", value: "10*10^7", has_leading_space: true }
// CHECK:STDOUT: - { index: 53, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 10, indent: 5, spelling: "," }
1.0e-8,
// CHECK:STDOUT: - { index: 52, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "1.0e-8", value: "10*10^-9", has_leading_space: true }
// CHECK:STDOUT: - { index: 53, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 11, indent: 5, spelling: "," }
// CHECK:STDOUT: - { index: 54, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "1.0e-8", value: "10*10^-9", has_leading_space: true }
// CHECK:STDOUT: - { index: 55, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 11, indent: 5, spelling: "," }
39999999999999999993.0e39999999999999999993,
// CHECK:STDOUT: - { index: 54, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "39999999999999999993.0e39999999999999999993", value: "399999999999999999930*10^39999999999999999992", has_leading_space: true }
// CHECK:STDOUT: - { index: 55, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 48, indent: 5, spelling: "," }
// CHECK:STDOUT: - { index: 56, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 5, indent: 5, spelling: "39999999999999999993.0e39999999999999999993", value: "399999999999999999930*10^39999999999999999992", has_leading_space: true }
// CHECK:STDOUT: - { index: 57, kind: "Comma", line: {{ *}}[[@LINE-2]], column: 48, indent: 5, spelling: "," }
);
// CHECK:STDOUT: - { index: 56, kind: "CloseParen", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: ")", opening_token: 41, has_leading_space: true }
// CHECK:STDOUT: - { index: 57, kind: "Semi", line: {{ *}}[[@LINE-2]], column: 4, indent: 3, spelling: ";" }
// CHECK:STDOUT: - { index: 58, kind: "CloseParen", line: {{ *}}[[@LINE-1]], column: 3, indent: 3, spelling: ")", opening_token: 43, has_leading_space: true }
// CHECK:STDOUT: - { index: 59, kind: "Semi", line: {{ *}}[[@LINE-2]], column: 4, indent: 3, spelling: ";" }
}
// CHECK:STDOUT: - { index: 58, kind: "CloseCurlyBrace", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "}", opening_token: 5, has_leading_space: true }
// CHECK:STDOUT: - { index: 60, kind: "CloseCurlyBrace", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "}", opening_token: 5, has_leading_space: true }
// --- fail_binary_real.carbon
// CHECK:STDOUT: - filename: fail_binary_real.carbon
// CHECK:STDOUT: tokens:
// CHECK:STDERR: fail_binary_real.carbon:[[@LINE+4]]:4: error: binary real number literals are not supported [BinaryRealLiteral]
// CHECK:STDERR: fail_binary_real.carbon:[[@LINE+4]]:4: error: binary real number literals are not supported [InvalidRealLiteralRadix]
// CHECK:STDERR: 0b1.0
// CHECK:STDERR: ^
// CHECK:STDERR:
0b1.0
// CHECK:STDOUT: - { index: 1, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "0b1.0", value: "2*2^-1", has_leading_space: true }
// --- fail_octal_real.carbon
// CHECK:STDOUT: - filename: fail_octal_real.carbon
// CHECK:STDOUT: tokens:
// CHECK:STDERR: fail_octal_real.carbon:[[@LINE+4]]:4: error: octal real number literals are not supported [InvalidRealLiteralRadix]
// CHECK:STDERR: 0o1.0
// CHECK:STDERR: ^
// CHECK:STDERR:
0o1.0
// CHECK:STDOUT: - { index: 1, kind: "RealLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "0o1.0", value: "8*2^-3", has_leading_space: true }
// --- fail_wrong_real_exponent.carbon
// CHECK:STDOUT: - filename: fail_wrong_real_exponent.carbon
// CHECK:STDOUT: tokens: