diff options
author | chriseth <chris@ethereum.org> | 2018-08-09 03:36:57 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-09 03:36:57 +0800 |
commit | d634d20b5b0dac3e5caf1741073fa123fdd56ab9 (patch) | |
tree | e88008bc8a8e665d32d8ac8c9125595dd90e8a22 /libsolidity | |
parent | 551343ae3eb1b3f1575d91a4f7021c0f9529d5bd (diff) | |
parent | b9222808f61e00833f8c11cd196cafb50ec9e1b9 (diff) | |
download | dexon-solidity-d634d20b5b0dac3e5caf1741073fa123fdd56ab9.tar.gz dexon-solidity-d634d20b5b0dac3e5caf1741073fa123fdd56ab9.tar.zst dexon-solidity-d634d20b5b0dac3e5caf1741073fa123fdd56ab9.zip |
Merge pull request #4684 from ethereum/underscores_in_numeric_literals
[BREAKING] Underscores in numeric literals
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.cpp | 42 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.h | 1 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 22 | ||||
-rw-r--r-- | libsolidity/parsing/Scanner.cpp | 39 |
4 files changed, 90 insertions, 14 deletions
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 77492499..ac4fa72b 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -24,6 +24,9 @@ #include <libsolidity/interface/Version.h> #include <boost/algorithm/cxx11/all_of.hpp> +#include <boost/algorithm/string.hpp> +#include <string> + using namespace std; using namespace dev; using namespace dev::solidity; @@ -183,6 +186,45 @@ bool SyntaxChecker::visit(Throw const& _throwStatement) return true; } +bool SyntaxChecker::visit(Literal const& _literal) +{ + if (_literal.token() != Token::Number) + return true; + + ASTString const& value = _literal.value(); + solAssert(!value.empty(), ""); + + // Generic checks no matter what base this number literal is of: + if (value.back() == '_') + { + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed."); + return true; + } + + 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; + } + + if (!_literal.isHexNumber()) // decimal literal + { + 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; +} + bool SyntaxChecker::visit(UnaryOperation const& _operation) { if (_operation.getOperator() == Token::Add) diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 28a0f66e..897df676 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -73,6 +73,7 @@ private: virtual bool visit(VariableDeclarationStatement const& _statement) override; virtual bool visit(StructDefinition const& _struct) override; + virtual bool visit(Literal const& _literal) override; ErrorReporter& m_errorReporter; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 349a59d4..3dbbca91 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -39,6 +39,7 @@ #include <boost/range/algorithm/copy.hpp> #include <boost/range/adaptor/sliced.hpp> #include <boost/range/adaptor/transformed.hpp> +#include <boost/algorithm/string.hpp> #include <limits> @@ -783,19 +784,22 @@ tuple<bool, rational> 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 - value = bigint(_literal.value()); + value = bigint(valueString); } - else if (expPoint != _literal.value().end()) + else if (expPoint != valueString.end()) { // Parse mantissa and exponent. Checks numeric limit. - tuple<bool, rational> mantissa = parseRational(string(_literal.value().begin(), expPoint)); + tuple<bool, rational> mantissa = parseRational(string(valueString.begin(), expPoint)); if (!get<0>(mantissa)) return make_tuple(false, rational(0)); @@ -805,7 +809,7 @@ tuple<bool, rational> 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<int32_t>::max() || exp < numeric_limits<int32_t>::min()) return make_tuple(false, rational(0)); @@ -834,7 +838,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal else { // parse as rational number - tuple<bool, rational> tmp = parseRational(_literal.value()); + tuple<bool, rational> 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 801d2cc4..30fdf21d 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -724,10 +724,18 @@ Token::Value Scanner::scanHexString() return Token::StringLiteral; } +// Parse for regex [:digit:]+(_[:digit:]+)* void Scanner::scanDecimalDigits() { - while (isDecimalDigit(m_char)) - addLiteralCharAndAdvance(); + // MUST begin with a decimal digit. + if (!isDecimalDigit(m_char)) + return; + + // May continue with decimal digit or underscore for grouping. + do addLiteralCharAndAdvance(); + while (!m_source.isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); + + // Defer further validation of underscore to SyntaxChecker. } Token::Value Scanner::scanNumber(char _charSeen) @@ -738,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 @@ -755,7 +765,8 @@ 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' - while (isHexDigit(m_char)) + + while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation addLiteralCharAndAdvance(); } else if (isDecimalDigit(m_char)) @@ -768,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; } @@ -785,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)) |