From b9222808f61e00833f8c11cd196cafb50ec9e1b9 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Fri, 3 Aug 2018 16:13:52 +0200 Subject: [PATCH] Cleanup & polish numbers-with-underscores parsing, also improving tests. --- docs/types.rst | 4 +- libsolidity/analysis/SyntaxChecker.cpp | 49 +++-- libsolidity/ast/Types.cpp | 21 ++- libsolidity/parsing/Scanner.cpp | 60 +++--- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- test/libsolidity/SolidityScanner.cpp | 178 ++++-------------- ...lexer_numbers_with_underscores_decimal.sol | 13 ++ ..._numbers_with_underscores_decimal_fail.sol | 13 ++ .../lexer_numbers_with_underscores_fixed.sol | 9 + ...er_numbers_with_underscores_fixed_fail.sol | 17 ++ .../lexer_numbers_with_underscores_hex.sol | 13 ++ ...exer_numbers_with_underscores_hex_fail.sol | 7 + 12 files changed, 183 insertions(+), 203 deletions(-) create mode 100644 test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal.sol create mode 100644 test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal_fail.sol create mode 100644 test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed.sol create mode 100644 test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed_fail.sol create mode 100644 test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex.sol create mode 100644 test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex_fail.sol diff --git a/docs/types.rst b/docs/types.rst index 4bade37c8..5c4a30ca8 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -285,7 +285,9 @@ Scientific notation is also supported, where the base can have fractions, while Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``. Underscores can be used to separate the digits of a numeric literal to aid readability. -For example, ``123_000``, ``0x2eff_abde``, ``1233_e348_9a`` are all valid. Underscores are only allowed between two digits. For hex literals, underscores are only allowed to separate groups of 4 hex digits. +For example, decimal ``123_000``, hexadecimal ``0x2eff_abde``, scientific decimal notation ``1_2e345_678`` are all valid. +Underscores are only allowed between two digits and only one consecutive underscore is allowed. +There is no additional semantic meaning added to a number literal containing underscores. Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by using them together with a non-literal expression). diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index dc2e35e50..ac4fa72b0 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -188,39 +188,38 @@ bool SyntaxChecker::visit(Throw const& _throwStatement) bool SyntaxChecker::visit(Literal const& _literal) { - if (!_literal.isHexNumber()) + if (_literal.token() != Token::Number) return true; - // We have a hex literal. Do underscore validation - solAssert(_literal.value().substr(0, 2) == "0x", ""); - ASTString value = _literal.value().substr(2); // Skip the 0x - vector parts; - boost::split(parts, value, boost::is_any_of("_")); - if (parts.size() == 1) // no underscores - return true; - // Everything except first and last part must be 4 chars in length - for (size_t i = 1; i + 1 < parts.size(); ++i) + ASTString const& value = _literal.value(); + solAssert(!value.empty(), ""); + + // Generic checks no matter what base this number literal is of: + if (value.back() == '_') { - if (parts[i].size() != 4) - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in hex literal. Found inner part with " + to_string(parts[i].size()) + " digits (has to be 4 digits)."); + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed."); + return true; } - // Validate rightmost block - if (parts.back().size() == 4) // If ends with 4 digits, then no need to validate first block + if (value.find("__") != ASTString::npos) + { + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed."); return true; - - // Validate leftmost block - // If first part is 4 digits then last part's length has to be even to avoid ambiguity over zero padding - if (parts.front().size() == 4) - { - if (parts.back().size() % 2 == 0) - return true; - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in hex literal. If the first part has 4 digits, it is assumed to be a byte sequence instead of a number and thus the last part should have an even number of digits."); } - else + + if (!_literal.isHexNumber()) // decimal literal { - // Both first and last part is invalid - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in hex literal. First or last part must have 4 digits."); + if (value.find("._") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); + + if (value.find("_.") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); + + if (value.find("_e") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed."); + + if (value.find("e_") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed."); } return true; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 73137ba98..0094c5ed2 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -773,21 +773,22 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal rational value; try { - auto expPoint = find(_literal.value().begin(), _literal.value().end(), 'e'); - if (expPoint == _literal.value().end()) - expPoint = find(_literal.value().begin(), _literal.value().end(), 'E'); + ASTString valueString = _literal.value(); + boost::erase_all(valueString, "_");// Remove underscore separators - if (boost::starts_with(_literal.value(), "0x")) + auto expPoint = find(valueString.begin(), valueString.end(), 'e'); + if (expPoint == valueString.end()) + expPoint = find(valueString.begin(), valueString.end(), 'E'); + + if (boost::starts_with(valueString, "0x")) { // process as hex - ASTString valueString = _literal.value(); - boost::erase_all(valueString, "_");// Remove underscore separators value = bigint(valueString); } - else if (expPoint != _literal.value().end()) + else if (expPoint != valueString.end()) { // Parse mantissa and exponent. Checks numeric limit. - tuple mantissa = parseRational(string(_literal.value().begin(), expPoint)); + tuple mantissa = parseRational(string(valueString.begin(), expPoint)); if (!get<0>(mantissa)) return make_tuple(false, rational(0)); @@ -797,7 +798,7 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal if (value == 0) return make_tuple(true, rational(0)); - bigint exp = bigint(string(expPoint + 1, _literal.value().end())); + bigint exp = bigint(string(expPoint + 1, valueString.end())); if (exp > numeric_limits::max() || exp < numeric_limits::min()) return make_tuple(false, rational(0)); @@ -826,7 +827,7 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal else { // parse as rational number - tuple tmp = parseRational(_literal.value()); + tuple tmp = parseRational(valueString); if (!get<0>(tmp)) return tmp; value = get<1>(tmp); diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index c223779e1..30fdf21d0 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -724,28 +724,18 @@ Token::Value Scanner::scanHexString() return Token::StringLiteral; } +// Parse for regex [:digit:]+(_[:digit:]+)* void Scanner::scanDecimalDigits() { - // Parse for regex [:digit:]+(_[:digit:]+)* + // MUST begin with a decimal digit. + if (!isDecimalDigit(m_char)) + return; - do - { - if (!isDecimalDigit(m_char)) - return; - while (isDecimalDigit(m_char)) - addLiteralCharAndAdvance(); + // May continue with decimal digit or underscore for grouping. + do addLiteralCharAndAdvance(); + while (!m_source.isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); - if (m_char == '_') - { - advance(); - if (!isDecimalDigit(m_char)) // Trailing underscore. Rollback and allow next step to flag it as illegal - { - rollback(1); - return; - } - } - } - while (isDecimalDigit(m_char)); + // Defer further validation of underscore to SyntaxChecker. } Token::Value Scanner::scanNumber(char _charSeen) @@ -756,6 +746,8 @@ Token::Value Scanner::scanNumber(char _charSeen) { // we have already seen a decimal point of the float addLiteralChar('.'); + if (m_char == '_') + return Token::Illegal; scanDecimalDigits(); // we know we have at least one digit } else @@ -773,17 +765,9 @@ Token::Value Scanner::scanNumber(char _charSeen) addLiteralCharAndAdvance(); if (!isHexDigit(m_char)) return Token::Illegal; // we must have at least one hex digit after 'x'/'X' - char last = m_char; - while (isHexDigit(m_char) || m_char == '_') // Unlike decimal digits, we keep the underscores for later validation - { - if (m_char == '_' && last == '_') - return Token::Illegal; // Double underscore - last = m_char; + while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation addLiteralCharAndAdvance(); - } - if (last == '_') - return Token::Illegal; // Trailing underscore } else if (isDecimalDigit(m_char)) // We do not allow octal numbers @@ -795,9 +779,17 @@ Token::Value Scanner::scanNumber(char _charSeen) scanDecimalDigits(); // optional if (m_char == '.') { - // A '.' has to be followed by a number. + if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + { + // Assume the input may be a floating point number with leading '_' in fraction part. + // Recover by consuming it all but returning `Illegal` right away. + addLiteralCharAndAdvance(); // '.' + addLiteralCharAndAdvance(); // '_' + scanDecimalDigits(); + } if (m_source.isPastEndOfInput() || !isDecimalDigit(m_source.get(1))) { + // A '.' has to be followed by a number. literal.complete(); return Token::Number; } @@ -812,8 +804,18 @@ Token::Value Scanner::scanNumber(char _charSeen) solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) return Token::Illegal; + else if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + { + // Recover from wrongly placed underscore as delimiter in literal with scientific + // notation by consuming until the end. + addLiteralCharAndAdvance(); // 'e' + addLiteralCharAndAdvance(); // '_' + scanDecimalDigits(); + literal.complete(); + return Token::Number; + } // scan exponent - addLiteralCharAndAdvance(); + addLiteralCharAndAdvance(); // 'e' | 'E' if (m_char == '+' || m_char == '-') addLiteralCharAndAdvance(); if (!isDecimalDigit(m_char)) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 12da1fa1d..3fd610a08 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -12840,7 +12840,7 @@ BOOST_AUTO_TEST_CASE(test_underscore_in_hex) { char const* sourceCode = R"( contract test { - function f(bool cond) returns (uint) { + function f(bool cond) public pure returns (uint) { uint32 x = 0x1234_ab; uint y = 0x1234_abcd_1234; return cond ? x : y; diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 9ad738ae1..f2e756bb5 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -155,170 +155,74 @@ BOOST_AUTO_TEST_CASE(trailing_dot) BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } -BOOST_AUTO_TEST_CASE(underscores_in_integer) -{ - Scanner scanner(CharStream("var x = 1_23_4;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Number); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1234"); - BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); -} - -BOOST_AUTO_TEST_CASE(underscores_in_scientific_notation) -{ - Scanner scanner(CharStream("var x = 1_2e10;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Number); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "12e10"); - BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); -} - -BOOST_AUTO_TEST_CASE(underscores_in_scientific_notation_in_exp_part) -{ - Scanner scanner(CharStream("var x = 12e1_0;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Number); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "12e10"); - BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); -} - - -BOOST_AUTO_TEST_CASE(underscores_in_hex) -{ - Scanner scanner(CharStream("var x = 0xab_19cf;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Number); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0xab_19cf"); - BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); -} - -BOOST_AUTO_TEST_CASE(leading_underscore_integer_is_identifier) -{ - Scanner scanner(CharStream("var x = _12;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); -} - BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier) { - Scanner scanner(CharStream("var x = _1.2;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("_1.2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(leading_underscore_decimal_after_dot_illegal) { - Scanner scanner(CharStream("var x = 1._2;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("1._2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + + scanner.reset(CharStream("1._"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(leading_underscore_exp_are_identifier) { - Scanner scanner(CharStream("var x = _1e2;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("_1e2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal) { - Scanner scanner(CharStream("var x = 1e_2;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("1e_2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1e_2"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal) { - Scanner scanner(CharStream("var x = 0x_abc;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); + Scanner scanner(CharStream("0x_abc")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } -BOOST_AUTO_TEST_CASE(trailing_underscore_integer_illegal) +BOOST_AUTO_TEST_CASE(fixed_number_invalid_underscore_front) { - Scanner scanner(CharStream("var x = 12_;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("12._1234_1234")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } -BOOST_AUTO_TEST_CASE(leading_underscore_after_decimal_illegal) +BOOST_AUTO_TEST_CASE(number_literals_with_trailing_underscore_at_eos) { - Scanner scanner(CharStream("var x = 1.2_;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); -} + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("0x123_")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); -BOOST_AUTO_TEST_CASE(leading_underscore_before_decimal_illegal) -{ - Scanner scanner(CharStream("var x = 1_.2;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); -} + scanner.reset(CharStream("123_"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); -BOOST_AUTO_TEST_CASE(trailing_underscore_exp_illegal) -{ - Scanner scanner(CharStream("var x = 1e2_;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); -} - -BOOST_AUTO_TEST_CASE(trailing_underscore_exp_before_e_illegal) -{ - Scanner scanner(CharStream("var x = 1_e2;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); -} - -BOOST_AUTO_TEST_CASE(trailing_underscore_hex_illegal) -{ - Scanner scanner(CharStream("var x = 0xabc_;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); -} - -BOOST_AUTO_TEST_CASE(double_underscore_illegal) -{ - Scanner scanner(CharStream("var x = 1__2;")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Var); - BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); - BOOST_CHECK_EQUAL(scanner.next(), Token::Assign); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + scanner.reset(CharStream("12.34_"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(negative_numbers) diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal.sol new file mode 100644 index 000000000..d4d3299f8 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + uint d1 = 654_321; + uint d2 = 54_321; + uint d3 = 4_321; + uint d4 = 5_43_21; + uint d5 = 1_2e10; + uint d6 = 12e1_0; + + d1; d2; d3; d4; d5; d6; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal_fail.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal_fail.sol new file mode 100644 index 000000000..8978996dc --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal_fail.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + uint D1 = 1234_; + uint D2 = 12__34; + uint D3 = 12_e34; + uint D4 = 12e_34; + } +} +// ---- +// SyntaxError: (56-61): Invalid use of underscores in number literal. No trailing underscores allowed. +// SyntaxError: (77-83): Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed. +// SyntaxError: (99-105): Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed. +// SyntaxError: (121-127): Invalid use of underscores in number literal. No underscore in front of exponent allowed. diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed.sol new file mode 100644 index 000000000..4910ee829 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + fixed f1 = 3.14_15; + fixed f2 = 3_1.4_15; + + f1; f2; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed_fail.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed_fail.sol new file mode 100644 index 000000000..3b91895d5 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed_fail.sol @@ -0,0 +1,17 @@ +contract C { + function f() public pure { + fixed F1 = 3.1415_; + fixed F2 = 3__1.4__15; + fixed F3 = 1_.2; + fixed F4 = 1._2; + fixed F5 = 1.2e_12; + fixed F6 = 1._; + } +} +// ---- +// SyntaxError: (57-64): Invalid use of underscores in number literal. No trailing underscores allowed. +// SyntaxError: (81-91): Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed. +// SyntaxError: (108-112): Invalid use of underscores in number literal. No underscores in front of the fraction part allowed. +// SyntaxError: (129-133): Invalid use of underscores in number literal. No underscores in front of the fraction part allowed. +// SyntaxError: (150-157): Invalid use of underscores in number literal. No underscore in front of exponent allowed. +// SyntaxError: (174-177): Invalid use of underscores in number literal. No trailing underscores allowed. diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex.sol new file mode 100644 index 000000000..999a4634d --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + uint x1 = 0x8765_4321; + uint x2 = 0x765_4321; + uint x3 = 0x65_4321; + uint x4 = 0x5_4321; + uint x5 = 0x123_1234_1234_1234; + uint x6 = 0x123456_1234_1234; + + x1; x2; x3; x4; x5; x6; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex_fail.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex_fail.sol new file mode 100644 index 000000000..a8a488c14 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex_fail.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + uint X1 = 0x1234__1234__1234__123; + } +} +// ---- +// SyntaxError: (56-79): Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.