diff --git a/docs/types.rst b/docs/types.rst index 12a35aaf7..31f6b53d8 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -218,6 +218,15 @@ String literals are written with either double or single-quotes (``"foo"`` or `` String literals support escape characters, such as ``\n``, ``\xNN`` and ``\uNNNN``. ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. +.. index:: literal, bytes + +Hexadecimal Literals +-------------------- + +Hexademical Literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. + +Hexademical Literals behave like String Literals and have the same convertibility restrictions. + .. index:: enum .. _enums: diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index d730210ae..603f3e421 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -591,7 +591,23 @@ void Scanner::scanToken() break; default: if (isIdentifierStart(m_char)) + { tie(token, m, n) = scanIdentifierOrKeyword(); + + // Special case for hexademical literals + if (token == Token::Hex) + { + // reset + m = 0; + n = 0; + + // Special quoted hex string must follow + if (m_char == '"' || m_char == '\'') + token = scanHexString(); + else + token = Token::Illegal; + } + } else if (isDecimalDigit(m_char)) token = scanNumber(); else if (skipWhitespace()) @@ -684,6 +700,25 @@ Token::Value Scanner::scanString() return Token::StringLiteral; } +Token::Value Scanner::scanHexString() +{ + char const quote = m_char; + advance(); // consume quote + LiteralScope literal(this, LITERAL_TYPE_STRING); + while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) + { + char c = m_char; + if (!scanHexByte(c)) + return Token::Illegal; + addLiteralChar(c); + } + if (m_char != quote) + return Token::Illegal; + literal.complete(); + advance(); // consume quote + return Token::StringLiteral; +} + void Scanner::scanDecimalDigits() { while (isDecimalDigit(m_char)) diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 708adf8f4..36cba1122 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -203,6 +203,7 @@ private: std::tuple scanIdentifierOrKeyword(); Token::Value scanString(); + Token::Value scanHexString(); Token::Value scanSingleLineDocComment(); Token::Value scanMultiLineDocComment(); /// Scans a slash '/' and depending on the characters returns the appropriate token diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index f4de74c76..007baef4b 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -155,6 +155,7 @@ namespace solidity K(External, "external", 0) \ K(For, "for", 0) \ K(Function, "function", 0) \ + K(Hex, "hex", 0) \ K(If, "if", 0) \ K(Indexed, "indexed", 0) \ K(Internal, "internal", 0) \ diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 624614d2a..31b75f256 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -324,6 +324,42 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } +BOOST_AUTO_TEST_CASE(valid_hex_literal) +{ + Scanner scanner(CharStream("{ hex\"00112233FF\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); +} + +BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) +{ + Scanner scanner(CharStream("{ hex\"00112233F\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) +{ + Scanner scanner(CharStream("{ hex\"00112233FF \"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) +{ + Scanner scanner(CharStream("{ hex\"00112233FF'")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) +{ + Scanner scanner(CharStream("{ hex\"hello\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + BOOST_AUTO_TEST_SUITE_END()