diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index 07a1e7b2dacd..2e74aa6730b9 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -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) diff --git a/toolchain/lex/numeric_literal.cpp b/toolchain/lex/numeric_literal.cpp index df3d44eb8477..09fd080f8208 100644 --- a/toolchain/lex/numeric_literal.cpp +++ b/toolchain/lex/numeric_literal.cpp @@ -159,6 +159,8 @@ NumericLiteral::Parser::Parser(Diagnostics::Emitter& 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(IntStore::MinAPWidth, digits.size() * 4), 0); - int cursor = digits.size() * 4 - 1; +// Parses an octal or hexadecimal integer literal. +template + 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(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(digits); case NumericLiteral::Radix::Decimal: return ParseDecimal(digits); case NumericLiteral::Radix::Hexadecimal: - return ParseHexadecimal(digits); + return ParseOctalOrHexadecimal( + 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(c)] = true; - } - break; - case Radix::Decimal: - for (char c : "0123456789") { - valid_digits[static_cast(c)] = true; - } - break; - case Radix::Hexadecimal: - for (char c : "0123456789ABCDEF") { - valid_digits[static_cast(c)] = true; - } - break; + static constexpr llvm::StringLiteral Digits = "0123456789ABCDEF"; + for (char c : Digits.take_front(static_cast(radix))) { + valid_digits[static_cast(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(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(radix_)); + // Carry on and parse the real literal anyway. } // We need to remove a '.' from the mantissa. diff --git a/toolchain/lex/numeric_literal.h b/toolchain/lex/numeric_literal.h index 90af4316bec9..be0896627edf 100644 --- a/toolchain/lex/numeric_literal.h +++ b/toolchain/lex/numeric_literal.h @@ -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 { diff --git a/toolchain/lex/numeric_literal_benchmark.cpp b/toolchain/lex/numeric_literal_benchmark.cpp index 427d39ad5bd2..2f06756e8b4c 100644 --- a/toolchain/lex/numeric_literal_benchmark.cpp +++ b/toolchain/lex/numeric_literal_benchmark.cpp @@ -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(); 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(); + 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(); 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(); 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 diff --git a/toolchain/lex/numeric_literal_test.cpp b/toolchain/lex/numeric_literal_test.cpp index c0ccab572087..92ecc9ef5989 100644 --- a/toolchain/lex/numeric_literal_test.cpp +++ b/toolchain/lex/numeric_literal_test.cpp @@ -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", diff --git a/toolchain/lex/testdata/numeric_literals.carbon b/toolchain/lex/testdata/numeric_literals.carbon index 95e59569acb7..e18e44a7fe1f 100644 --- a/toolchain/lex/testdata/numeric_literals.carbon +++ b/toolchain/lex/testdata/numeric_literals.carbon @@ -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: