From 2d50851ea3be8402f01a4a0f13e18c9e26396310 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 6 Oct 2014 17:13:52 +0200 Subject: [PATCH 01/34] Solidity scanner and some unit tests. The scanner is a modified version of the v8 javascript scanner. --- CMakeLists.txt | 1 + libsolidity/CMakeLists.txt | 49 +++ libsolidity/Scanner.cpp | 653 +++++++++++++++++++++++++++++++++++++ libsolidity/Scanner.h | 252 ++++++++++++++ libsolidity/Token.cpp | 77 +++++ libsolidity/Token.h | 339 +++++++++++++++++++ test/CMakeLists.txt | 1 + test/solidityScanner.cpp | 139 ++++++++ 8 files changed, 1511 insertions(+) create mode 100644 libsolidity/CMakeLists.txt create mode 100644 libsolidity/Scanner.cpp create mode 100644 libsolidity/Scanner.h create mode 100644 libsolidity/Token.cpp create mode 100644 libsolidity/Token.h create mode 100644 test/solidityScanner.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e623c7319..5c5cfb9a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -328,6 +328,7 @@ add_subdirectory(libdevcore) add_subdirectory(libevmface) add_subdirectory(liblll) add_subdirectory(libserpent) +add_subdirectory(libsolidity) if(NOT APPLE) if(PYTHON_LS) add_subdirectory(libpyserpent) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt new file mode 100644 index 000000000..59aa78364 --- /dev/null +++ b/libsolidity/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_policy(SET CMP0015 NEW) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE solidity) + +if(ETH_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST}) +endif() + +file(GLOB HEADERS "*.h") + +include_directories(..) + +target_link_libraries(${EXECUTABLE} evmface) +target_link_libraries(${EXECUTABLE} devcore) + + +if("${TARGET_PLATFORM}" STREQUAL "w64") + target_link_libraries(${EXECUTABLE} boost_system-mt-s) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) +elseif (APPLE) + # Latest mavericks boost libraries only come with -mt + target_link_libraries(${EXECUTABLE} boost_system-mt) + target_link_libraries(${EXECUTABLE} boost_thread-mt) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +elseif (UNIX) + target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +else () + target_link_libraries(${EXECUTABLE} boost_system) + target_link_libraries(${EXECUTABLE} boost_thread) + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp new file mode 100644 index 000000000..101b4a1ab --- /dev/null +++ b/libsolidity/Scanner.cpp @@ -0,0 +1,653 @@ +// Copyright 2006-2012, the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Modifications as part of cpp-ethereum under the following license: +// +// cpp-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cpp-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cpp-ethereum. If not, see . + +#include + +namespace dev { +namespace solidity { + +namespace { + bool IsDecimalDigit(char c) { + return '0' <= c && c <= '9'; + } + bool IsHexDigit(char c) { + return IsDecimalDigit(c) + || ('a' <= c && c <= 'f') + || ('A' <= c && c <= 'F'); + } + bool IsLineTerminator(char c) { return c == '\n'; } + bool IsWhiteSpace(char c) { + return c == ' ' || c == '\n' || c == '\t'; + } + bool IsIdentifierStart(char c) { + return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + bool IsIdentifierPart(char c) { + return IsIdentifierStart(c) || IsDecimalDigit(c); + } + + int HexValue(char c) { + if (c >= '0' && c <= '9') return c - '0'; + else if (c >= 'a' && c <= 'f') return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') return c - 'A' + 10; + else return -1; + } +} + +Scanner::Scanner(const CharStream& _source) +{ + reset(_source); +} + +void Scanner::reset(const CharStream& _source) +{ + m_source = _source; + + // Initialize current_ to not refer to a literal. + m_current_token.token = Token::ILLEGAL; + m_current_token.literal.clear(); + + m_hasLineTerminatorBeforeNext = true; + m_hasMultilineCommentBeforeNext = false; + + m_char = m_source.get(); + skipWhitespace(); + scanToken(); +} + + +bool Scanner::scanHexNumber(char& scanned_number, int expected_length) +{ + BOOST_ASSERT(expected_length <= 4); // prevent overflow + + char x = 0; + for (int i = 0; i < expected_length; i++) { + int d = HexValue(m_char); + if (d < 0) { + rollback(i); + return false; + } + x = x * 16 + d; + advance(); + } + + scanned_number = x; + return true; +} + + +// Ensure that tokens can be stored in a byte. +BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); + +Token::Value Scanner::next() +{ + m_current_token = m_next_token; + m_hasLineTerminatorBeforeNext = false; + m_hasMultilineCommentBeforeNext = false; + scanToken(); + return m_current_token.token; +} + +bool Scanner::skipWhitespace() +{ + const int start_position = getSourcePos(); + + while (true) { + if (IsLineTerminator(m_char)) { + m_hasLineTerminatorBeforeNext = true; + } else if (!IsWhiteSpace(m_char)) { + break; + } + advance(); + } + + // Return whether or not we skipped any characters. + return getSourcePos() != start_position; +} + + +Token::Value Scanner::skipSingleLineComment() +{ + // The line terminator at the end of the line is not considered + // to be part of the single-line comment; it is recognized + // separately by the lexical grammar and becomes part of the + // stream of input elements for the syntactic grammar + while (advance() && !IsLineTerminator(m_char)) { }; + + return Token::WHITESPACE; +} + +Token::Value Scanner::skipMultiLineComment() +{ + BOOST_ASSERT(m_char == '*'); + advance(); + + while (!isSourcePastEndOfInput()) { + char ch = m_char; + advance(); + if (IsLineTerminator(ch)) { + // Following ECMA-262, section 7.4, a comment containing + // a newline will make the comment count as a line-terminator. + m_hasMultilineCommentBeforeNext = true; + } + // If we have reached the end of the multi-line comment, we + // consume the '/' and insert a whitespace. This way all + // multi-line comments are treated as whitespace. + if (ch == '*' && m_char == '/') { + m_char = ' '; + return Token::WHITESPACE; + } + } + + // Unterminated multi-line comment. + return Token::ILLEGAL; +} + +void Scanner::scanToken() +{ + m_next_token.literal.clear(); + Token::Value token; + do { + // Remember the position of the next token + m_next_token.location.beg_pos = getSourcePos(); + + switch (m_char) { + case '\n': + m_hasLineTerminatorBeforeNext = true; // fall-through + case ' ': + case '\t': + token = selectToken(Token::WHITESPACE); + break; + + case '"': case '\'': + token = scanString(); + break; + + case '<': + // < <= << <<= + advance(); + if (m_char == '=') { + token = selectToken(Token::LTE); + } else if (m_char == '<') { + token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); + } else { + token = Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + advance(); + if (m_char == '=') { + token = selectToken(Token::GTE); + } else if (m_char == '>') { + // >> >>= >>> >>>= + advance(); + if (m_char == '=') { + token = selectToken(Token::ASSIGN_SAR); + } else if (m_char == '>') { + token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); + } else { + token = Token::SAR; + } + } else { + token = Token::GT; + } + break; + + case '=': + // = == => + advance(); + if (m_char == '=') { + token = selectToken(Token::EQ); + } else if (m_char == '>') { + token = selectToken(Token::ARROW); + } else { + token = Token::ASSIGN; + } + break; + + case '!': + // ! != !== + advance(); + if (m_char == '=') { + token = selectToken(Token::NE); + } else { + token = Token::NOT; + } + break; + + case '+': + // + ++ += + advance(); + if (m_char == '+') { + token = selectToken(Token::INC); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_ADD); + } else { + token = Token::ADD; + } + break; + + case '-': + // - -- -= + advance(); + if (m_char == '-') { + advance(); + token = Token::DEC; + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_SUB); + } else { + token = Token::SUB; + } + break; + + case '*': + // * *= + token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); + break; + + case '%': + // % %= + token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); + break; + + case '/': + // / // /* /= + advance(); + if (m_char == '/') { + token = skipSingleLineComment(); + } else if (m_char == '*') { + token = skipMultiLineComment(); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_DIV); + } else { + token = Token::DIV; + } + break; + + case '&': + // & && &= + advance(); + if (m_char == '&') { + token = selectToken(Token::AND); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_BIT_AND); + } else { + token = Token::BIT_AND; + } + break; + + case '|': + // | || |= + advance(); + if (m_char == '|') { + token = selectToken(Token::OR); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_BIT_OR); + } else { + token = Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + + case '.': + // . Number + advance(); + if (IsDecimalDigit(m_char)) { + token = scanNumber(true); + } else { + token = Token::PERIOD; + } + break; + + case ':': + token = selectToken(Token::COLON); + break; + + case ';': + token = selectToken(Token::SEMICOLON); + break; + + case ',': + token = selectToken(Token::COMMA); + break; + + case '(': + token = selectToken(Token::LPAREN); + break; + + case ')': + token = selectToken(Token::RPAREN); + break; + + case '[': + token = selectToken(Token::LBRACK); + break; + + case ']': + token = selectToken(Token::RBRACK); + break; + + case '{': + token = selectToken(Token::LBRACE); + break; + + case '}': + token = selectToken(Token::RBRACE); + break; + + case '?': + token = selectToken(Token::CONDITIONAL); + break; + + case '~': + token = selectToken(Token::BIT_NOT); + break; + + default: + if (IsIdentifierStart(m_char)) { + token = scanIdentifierOrKeyword(); + } else if (IsDecimalDigit(m_char)) { + token = scanNumber(false); + } else if (skipWhitespace()) { + token = Token::WHITESPACE; + } else if (isSourcePastEndOfInput()) { + token = Token::EOS; + } else { + token = selectToken(Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. + } while (token == Token::WHITESPACE); + + m_next_token.location.end_pos = getSourcePos(); + m_next_token.token = token; +} + +bool Scanner::scanEscape() +{ + char c = m_char; + advance(); + + // Skip escaped newlines. + if (IsLineTerminator(c)) + return true; + + switch (c) { + case '\'': // fall through + case '"' : // fall through + case '\\': break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + case 'u' : { + if (!scanHexNumber(c, 4)) return false; + break; + } + case 'v' : c = '\v'; break; + case 'x' : { + if (!scanHexNumber(c, 2)) return false; + break; + } + } + + // According to ECMA-262, section 7.8.4, characters not covered by the + // above cases should be illegal, but they are commonly handled as + // non-escaped characters by JS VMs. + addLiteralChar(c); + return true; +} + +Token::Value Scanner::scanString() +{ + const char quote = m_char; + advance(); // consume quote + + LiteralScope literal(this); + while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) { + char c = m_char; + advance(); + if (c == '\\') { + if (isSourcePastEndOfInput() || !scanEscape()) return Token::ILLEGAL; + } else { + addLiteralChar(c); + } + } + if (m_char != quote) return Token::ILLEGAL; + literal.Complete(); + + advance(); // consume quote + return Token::STRING; +} + + +void Scanner::scanDecimalDigits() +{ + while (IsDecimalDigit(m_char)) + addLiteralCharAndAdvance(); +} + + +Token::Value Scanner::scanNumber(bool _periodSeen) +{ + BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction + + enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL; + + LiteralScope literal(this); + if (_periodSeen) { + // we have already seen a decimal point of the float + addLiteralChar('.'); + scanDecimalDigits(); // we know we have at least one digit + } else { + // if the first character is '0' we must check for octals and hex + if (m_char == '0') { + addLiteralCharAndAdvance(); + + // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or + // an octal number. + if (m_char == 'x' || m_char == 'X') { + // hex number + kind = HEX; + addLiteralCharAndAdvance(); + if (!IsHexDigit(m_char)) { + // we must have at least one hex digit after 'x'/'X' + return Token::ILLEGAL; + } + while (IsHexDigit(m_char)) { + addLiteralCharAndAdvance(); + } + } + } + + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) { + scanDecimalDigits(); // optional + if (m_char == '.') { + addLiteralCharAndAdvance(); + scanDecimalDigits(); // optional + } + } + } + + // scan exponent, if any + if (m_char == 'e' || m_char == 'E') { + BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind != DECIMAL) return Token::ILLEGAL; + // scan exponent + addLiteralCharAndAdvance(); + if (m_char == '+' || m_char == '-') + addLiteralCharAndAdvance(); + if (!IsDecimalDigit(m_char)) { + // we must have at least one decimal digit after 'e'/'E' + return Token::ILLEGAL; + } + scanDecimalDigits(); + } + + // The source character immediately following a numeric literal must + // not be an identifier start or a decimal digit; see ECMA-262 + // section 7.8.3, page 17 (note that we read only one decimal digit + // if the value is 0). + if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char)) + return Token::ILLEGAL; + + literal.Complete(); + + return Token::NUMBER; +} + + +// ---------------------------------------------------------------------------- +// Keyword Matcher + +#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ + KEYWORD_GROUP('b') \ + KEYWORD("break", Token::BREAK) \ + KEYWORD_GROUP('c') \ + KEYWORD("case", Token::CASE) \ + KEYWORD("catch", Token::CATCH) \ + KEYWORD("const", Token::CONST) \ + KEYWORD("continue", Token::CONTINUE) \ + KEYWORD_GROUP('d') \ + KEYWORD("debugger", Token::DEBUGGER) \ + KEYWORD("default", Token::DEFAULT) \ + KEYWORD("delete", Token::DELETE) \ + KEYWORD("do", Token::DO) \ + KEYWORD_GROUP('e') \ + KEYWORD("else", Token::ELSE) \ + KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ + KEYWORD_GROUP('f') \ + KEYWORD("false", Token::FALSE_LITERAL) \ + KEYWORD("finally", Token::FINALLY) \ + KEYWORD("for", Token::FOR) \ + KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('i') \ + KEYWORD("if", Token::IF) \ + KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("in", Token::IN) \ + KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('l') \ + KEYWORD_GROUP('n') \ + KEYWORD("new", Token::NEW) \ + KEYWORD("null", Token::NULL_LITERAL) \ + KEYWORD_GROUP('p') \ + KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('r') \ + KEYWORD("return", Token::RETURN) \ + KEYWORD_GROUP('s') \ + KEYWORD("switch", Token::SWITCH) \ + KEYWORD_GROUP('t') \ + KEYWORD("this", Token::THIS) \ + KEYWORD("throw", Token::THROW) \ + KEYWORD("true", Token::TRUE_LITERAL) \ + KEYWORD("try", Token::TRY) \ + KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('v') \ + KEYWORD("var", Token::VAR) \ + KEYWORD("void", Token::VOID) \ + KEYWORD_GROUP('w') \ + KEYWORD("while", Token::WHILE) \ + KEYWORD("with", Token::WITH) + + +static Token::Value KeywordOrIdentifierToken(const std::string& input) +{ + BOOST_ASSERT(!input.empty()); + const int kMinLength = 2; + const int kMaxLength = 10; + if (input.size() < kMinLength || input.size() > kMaxLength) { + return Token::IDENTIFIER; + } + switch (input[0]) { + default: +#define KEYWORD_GROUP_CASE(ch) \ + break; \ + case ch: +#define KEYWORD(keyword, token) \ + { \ + /* 'keyword' is a char array, so sizeof(keyword) is */ \ + /* strlen(keyword) plus 1 for the NUL char. */ \ + const int keyword_length = sizeof(keyword) - 1; \ + BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ + BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ + if (input == keyword) { \ + return token; \ + } \ + } + KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) + } + return Token::IDENTIFIER; +} + +Token::Value Scanner::scanIdentifierOrKeyword() +{ + BOOST_ASSERT(IsIdentifierStart(m_char)); + LiteralScope literal(this); + + addLiteralCharAndAdvance(); + + // Scan the rest of the identifier characters. + while (IsIdentifierPart(m_char)) + addLiteralCharAndAdvance(); + + literal.Complete(); + + return KeywordOrIdentifierToken(m_next_token.literal); +} + + +} } diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h new file mode 100644 index 000000000..3cf52fbc6 --- /dev/null +++ b/libsolidity/Scanner.h @@ -0,0 +1,252 @@ +// Copyright 2006-2012, the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Modifications as part of cpp-ethereum under the following license: +// +// cpp-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cpp-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cpp-ethereum. If not, see . + +#pragma once + +#include + +#include +#include +#include +#include + +namespace dev { +namespace solidity { + + +class AstRawString; +class AstValueFactory; +class ParserRecorder; + +class CharStream { +public: + CharStream() + : m_pos(0) + {} + + explicit CharStream(const std::string& _source) + : m_source(_source), m_pos(0) + {} + int getPos() const { return m_pos; } + bool isPastEndOfInput() const { return m_pos >= m_source.size(); } + char get() const { return m_source[m_pos]; } + char advanceAndGet() { + if (isPastEndOfInput()) return 0; + ++m_pos; + if (isPastEndOfInput()) return 0; + return get(); + } + char rollback(size_t _amount) { + BOOST_ASSERT(m_pos >= _amount); + m_pos -= _amount; + return get(); + } + +private: + std::string m_source; + size_t m_pos; +}; + +// ---------------------------------------------------------------------------- +// JavaScript Scanner. + +class Scanner { +public: + // Scoped helper for literal recording. Automatically drops the literal + // if aborting the scanning before it's complete. + class LiteralScope { + public: + explicit LiteralScope(Scanner* self) + : scanner_(self), complete_(false) { + scanner_->startNewLiteral(); + } + ~LiteralScope() { + if (!complete_) scanner_->dropLiteral(); + } + void Complete() { + complete_ = true; + } + + private: + Scanner* scanner_; + bool complete_; + }; + + // Representation of an interval of source positions. + struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + + bool IsValid() const { + return beg_pos >= 0 && end_pos >= beg_pos; + } + + static Location invalid() { return Location(-1, -1); } + + int beg_pos; + int end_pos; + }; + + explicit Scanner(const CharStream& _source); + + // Resets the scanner as if newly constructed with _input as input. + void reset(const CharStream& _source); + + // Returns the next token and advances input. + Token::Value next(); + // Returns the current token again. + Token::Value getCurrentToken() { return m_current_token.token; } + // Returns the location information for the current token + // (the token last returned by Next()). + Location getCurrentLocation() const { return m_current_token.location; } + const std::string& getCurrentLiteral() const { return m_current_token.literal; } + + // Similar functions for the upcoming token. + + // One token look-ahead (past the token returned by Next()). + Token::Value peek() const { return m_next_token.token; } + + Location peekLocation() const { return m_next_token.location; } + const std::string& peekLiteral() const { return m_next_token.literal; } + + // Returns true if there was a line terminator before the peek'ed token, + // possibly inside a multi-line comment. + bool hasAnyLineTerminatorBeforeNext() const { + return m_hasLineTerminatorBeforeNext || + m_hasMultilineCommentBeforeNext; + } + +private: + // Used for the current and look-ahead token. + struct TokenDesc { + Token::Value token; + Location location; + std::string literal; + }; + + static const int kCharacterLookaheadBufferSize = 1; + + // Literal buffer support + inline void startNewLiteral() { + m_next_token.literal.clear(); + } + + inline void addLiteralChar(char c) { + m_next_token.literal.push_back(c); + } + + inline void dropLiteral() { + m_next_token.literal.clear(); + } + + inline void addLiteralCharAndAdvance() { + addLiteralChar(m_char); + advance(); + } + + // Low-level scanning support. + bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } + void rollback(int amount) { + m_char = m_source.rollback(amount); + } + + inline Token::Value selectToken(Token::Value tok) { + advance(); + return tok; + } + + inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) { + advance(); + if (m_char == next) { + advance(); + return then; + } else { + return else_; + } + } + + bool scanHexNumber(char& scanned_number, int expected_length); + + // Scans a single JavaScript token. + void scanToken(); + + bool skipWhitespace(); + Token::Value skipSingleLineComment(); + Token::Value skipMultiLineComment(); + + void scanDecimalDigits(); + Token::Value scanNumber(bool _periodSeen); + Token::Value scanIdentifierOrKeyword(); + + Token::Value scanString(); + + // Scans an escape-sequence which is part of a string and adds the + // decoded character to the current literal. Returns true if a pattern + // is scanned. + bool scanEscape(); + + // Return the current source position. + int getSourcePos() { + return m_source.getPos(); + } + bool isSourcePastEndOfInput() { + return m_source.isPastEndOfInput(); + } + + TokenDesc m_current_token; // desc for current token (as returned by Next()) + TokenDesc m_next_token; // desc for next token (one token look-ahead) + + CharStream m_source; + + // one character look-ahead, equals 0 at end of input + char m_char; + + // Whether there is a line terminator whitespace character after + // the current token, and before the next. Does not count newlines + // inside multiline comments. + bool m_hasLineTerminatorBeforeNext; + // Whether there is a multi-line comment that contains a + // line-terminator after the current token, and before the next. + bool m_hasMultilineCommentBeforeNext; +}; + +} } diff --git a/libsolidity/Token.cpp b/libsolidity/Token.cpp new file mode 100644 index 000000000..6ae6456a9 --- /dev/null +++ b/libsolidity/Token.cpp @@ -0,0 +1,77 @@ +// Copyright 2006-2012, the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Modifications as part of cpp-ethereum under the following license: +// +// cpp-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cpp-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cpp-ethereum. If not, see . + +#include + +namespace dev { +namespace solidity { + +#define T(name, string, precedence) #name, +const char* const Token::m_name[NUM_TOKENS] = { + TOKEN_LIST(T, T) +}; +#undef T + + +#define T(name, string, precedence) string, +const char* const Token::m_string[NUM_TOKENS] = { + TOKEN_LIST(T, T) +}; +#undef T + + +#define T(name, string, precedence) precedence, +const int8_t Token::m_precedence[NUM_TOKENS] = { + TOKEN_LIST(T, T) +}; +#undef T + + +#define KT(a, b, c) 'T', +#define KK(a, b, c) 'K', +const char Token::m_tokenType[] = { + TOKEN_LIST(KT, KK) +}; +#undef KT +#undef KK + +} } diff --git a/libsolidity/Token.h b/libsolidity/Token.h new file mode 100644 index 000000000..4f5ec1943 --- /dev/null +++ b/libsolidity/Token.h @@ -0,0 +1,339 @@ +// Copyright 2006-2012, the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Modifications as part of cpp-ethereum under the following license: +// +// cpp-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cpp-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cpp-ethereum. If not, see . + +#pragma once + +#include + +#include +#include + +namespace dev { +namespace solidity { + +// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the +// same signature M(name, string, precedence), where name is the +// symbolic token name, string is the corresponding syntactic symbol +// (or NULL, for literals), and precedence is the precedence (or 0). +// The parameters are invoked for token categories as follows: +// +// T: Non-keyword tokens +// K: Keyword tokens + +// IGNORE_TOKEN is a convenience macro that can be supplied as +// an argument (at any position) for a TOKEN_LIST call. It does +// nothing with tokens belonging to the respective category. + +#define IGNORE_TOKEN(name, string, precedence) + +#define TOKEN_LIST(T, K) \ + /* End of source indicator. */ \ + T(EOS, "EOS", 0) \ + \ + /* Punctuators (ECMA-262, section 7.7, page 15). */ \ + T(LPAREN, "(", 0) \ + T(RPAREN, ")", 0) \ + T(LBRACK, "[", 0) \ + T(RBRACK, "]", 0) \ + T(LBRACE, "{", 0) \ + T(RBRACE, "}", 0) \ + T(COLON, ":", 0) \ + T(SEMICOLON, ";", 0) \ + T(PERIOD, ".", 0) \ + T(CONDITIONAL, "?", 3) \ + T(INC, "++", 0) \ + T(DEC, "--", 0) \ + T(ARROW, "=>", 0) \ + \ + /* Assignment operators. */ \ + /* IsAssignmentOp() and Assignment::is_compound() relies on */ \ + /* this block of enum values being contiguous and sorted in the */ \ + /* same order! */ \ + T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \ + T(INIT_LET, "=init_let", 2) /* AST-use only. */ \ + T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ + T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \ + T(ASSIGN, "=", 2) \ + T(ASSIGN_BIT_OR, "|=", 2) \ + T(ASSIGN_BIT_XOR, "^=", 2) \ + T(ASSIGN_BIT_AND, "&=", 2) \ + T(ASSIGN_SHL, "<<=", 2) \ + T(ASSIGN_SAR, ">>=", 2) \ + T(ASSIGN_SHR, ">>>=", 2) \ + T(ASSIGN_ADD, "+=", 2) \ + T(ASSIGN_SUB, "-=", 2) \ + T(ASSIGN_MUL, "*=", 2) \ + T(ASSIGN_DIV, "/=", 2) \ + T(ASSIGN_MOD, "%=", 2) \ + \ + /* Binary operators sorted by precedence. */ \ + /* IsBinaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(COMMA, ",", 1) \ + T(OR, "||", 4) \ + T(AND, "&&", 5) \ + T(BIT_OR, "|", 6) \ + T(BIT_XOR, "^", 7) \ + T(BIT_AND, "&", 8) \ + T(SHL, "<<", 11) \ + T(SAR, ">>", 11) \ + T(SHR, ">>>", 11) \ + T(ROR, "rotate right", 11) /* only used by Crankshaft */ \ + T(ADD, "+", 12) \ + T(SUB, "-", 12) \ + T(MUL, "*", 13) \ + T(DIV, "/", 13) \ + T(MOD, "%", 13) \ + \ + /* Compare operators sorted by precedence. */ \ + /* IsCompareOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(EQ, "==", 9) \ + T(NE, "!=", 9) \ + T(EQ_STRICT, "===", 9) \ + T(NE_STRICT, "!==", 9) \ + T(LT, "<", 10) \ + T(GT, ">", 10) \ + T(LTE, "<=", 10) \ + T(GTE, ">=", 10) \ + K(INSTANCEOF, "instanceof", 10) \ + K(IN, "in", 10) \ + \ + /* Unary operators. */ \ + /* IsUnaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(NOT, "!", 0) \ + T(BIT_NOT, "~", 0) \ + K(DELETE, "delete", 0) \ + K(TYPEOF, "typeof", 0) \ + K(VOID, "void", 0) \ + \ + /* Keywords (ECMA-262, section 7.5.2, page 13). */ \ + K(BREAK, "break", 0) \ + K(CASE, "case", 0) \ + K(CATCH, "catch", 0) \ + K(CONTINUE, "continue", 0) \ + K(DEBUGGER, "debugger", 0) \ + K(DEFAULT, "default", 0) \ + /* DELETE */ \ + K(DO, "do", 0) \ + K(ELSE, "else", 0) \ + K(FINALLY, "finally", 0) \ + K(FOR, "for", 0) \ + K(FUNCTION, "function", 0) \ + K(IF, "if", 0) \ + /* IN */ \ + /* INSTANCEOF */ \ + K(NEW, "new", 0) \ + K(RETURN, "return", 0) \ + K(SWITCH, "switch", 0) \ + K(THIS, "this", 0) \ + K(THROW, "throw", 0) \ + K(TRY, "try", 0) \ + /* TYPEOF */ \ + K(VAR, "var", 0) \ + /* VOID */ \ + K(WHILE, "while", 0) \ + K(WITH, "with", 0) \ + \ + /* Literals (ECMA-262, section 7.8, page 16). */ \ + K(NULL_LITERAL, "null", 0) \ + K(TRUE_LITERAL, "true", 0) \ + K(FALSE_LITERAL, "false", 0) \ + T(NUMBER, NULL, 0) \ + T(STRING, NULL, 0) \ + \ + /* Identifiers (not keywords or future reserved words). */ \ + T(IDENTIFIER, NULL, 0) \ + \ + /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ + T(FUTURE_RESERVED_WORD, NULL, 0) \ + T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ + K(CLASS, "class", 0) \ + K(CONST, "const", 0) \ + K(EXPORT, "export", 0) \ + K(EXTENDS, "extends", 0) \ + K(IMPORT, "import", 0) \ + K(LET, "let", 0) \ + K(STATIC, "static", 0) \ +/* K(YIELD, "yield", 0) */ \ + K(SUPER, "super", 0) \ + \ + /* Illegal token - not able to scan. */ \ + T(ILLEGAL, "ILLEGAL", 0) \ + \ + /* Scanner-internal use only. */ \ + T(WHITESPACE, NULL, 0) + + +class Token { + public: + // All token values. +#define T(name, string, precedence) name, + enum Value { + TOKEN_LIST(T, T) + NUM_TOKENS + }; +#undef T + + // Returns a string corresponding to the C++ token name + // (e.g. "LT" for the token LT). + static const char* Name(Value tok) { + BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned + return m_name[tok]; + } + + // Predicates + static bool IsKeyword(Value tok) { + return m_tokenType[tok] == 'K'; + } + + static bool IsIdentifier(Value tok) { + return tok == IDENTIFIER; + } + + static bool IsAssignmentOp(Value tok) { + return INIT_VAR <= tok && tok <= ASSIGN_MOD; + } + + static bool IsBinaryOp(Value op) { + return COMMA <= op && op <= MOD; + } + + static bool IsTruncatingBinaryOp(Value op) { + return BIT_OR <= op && op <= ROR; + } + + static bool IsCompareOp(Value op) { + return EQ <= op && op <= IN; + } + + static bool IsOrderedRelationalCompareOp(Value op) { + return op == LT || op == LTE || op == GT || op == GTE; + } + + static bool IsEqualityOp(Value op) { + return op == EQ || op == EQ_STRICT; + } + + static bool IsInequalityOp(Value op) { + return op == NE || op == NE_STRICT; + } + + static bool IsArithmeticCompareOp(Value op) { + return IsOrderedRelationalCompareOp(op) || + IsEqualityOp(op) || IsInequalityOp(op); + } + + static Value NegateCompareOp(Value op) { + BOOST_ASSERT(IsArithmeticCompareOp(op)); + switch (op) { + case EQ: return NE; + case NE: return EQ; + case EQ_STRICT: return NE_STRICT; + case NE_STRICT: return EQ_STRICT; + case LT: return GTE; + case GT: return LTE; + case LTE: return GT; + case GTE: return LT; + default: + BOOST_ASSERT(false); // should not get here + return op; + } + } + + static Value ReverseCompareOp(Value op) { + BOOST_ASSERT(IsArithmeticCompareOp(op)); + switch (op) { + case EQ: return EQ; + case NE: return NE; + case EQ_STRICT: return EQ_STRICT; + case NE_STRICT: return NE_STRICT; + case LT: return GT; + case GT: return LT; + case LTE: return GTE; + case GTE: return LTE; + default: + BOOST_ASSERT(false); // should not get here + return op; + } + } + + static bool IsBitOp(Value op) { + return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; + } + + static bool IsUnaryOp(Value op) { + return (NOT <= op && op <= VOID) || op == ADD || op == SUB; + } + + static bool IsCountOp(Value op) { + return op == INC || op == DEC; + } + + static bool IsShiftOp(Value op) { + return (SHL <= op) && (op <= SHR); + } + + // Returns a string corresponding to the JS token string + // (.e., "<" for the token LT) or NULL if the token doesn't + // have a (unique) string (e.g. an IDENTIFIER). + static const char* String(Value tok) { + BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. + return m_string[tok]; + } + + // Returns the precedence > 0 for binary and compare + // operators; returns 0 otherwise. + static int Precedence(Value tok) { + BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. + return m_precedence[tok]; + } + + private: + static const char* const m_name[NUM_TOKENS]; + static const char* const m_string[NUM_TOKENS]; + static const int8_t m_precedence[NUM_TOKENS]; + static const char m_tokenType[NUM_TOKENS]; +}; + +} } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6936addb3..b4d0e46bf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries(testeth ethereum) target_link_libraries(testeth ethcore) target_link_libraries(testeth secp256k1) target_link_libraries(testeth gmp) +target_link_libraries(testeth solidity) target_link_libraries(testeth ${CRYPTOPP_LS}) if ("${TARGET_PLATFORM}" STREQUAL "w64") diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp new file mode 100644 index 000000000..afbcdffae --- /dev/null +++ b/test/solidityScanner.cpp @@ -0,0 +1,139 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Unit tests for the solidity scanner. + */ + +#include +#include + +namespace dev { +namespace solidity { +namespace test { + +BOOST_AUTO_TEST_SUITE(solidity) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::FUNCTION); + BOOST_CHECK_EQUAL(scanner.next(), Token::BREAK); + BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "765"); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1"); + BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string2"); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "identifier1"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(string_escapes) +{ + Scanner scanner(CharStream(" { \"a\\x61\"")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "aa"); +} + +BOOST_AUTO_TEST_CASE(string_escapes_with_zero) +{ + Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), std::string("aa\0abc", 6)); +} + +BOOST_AUTO_TEST_CASE(string_escape_illegal) +{ + Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); + // TODO recovery from illegal tokens should be improved + BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(hex_numbers) +{ + Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), 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.getCurrentLiteral(), "0x765432536763762734623472346"); + BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(locations) +{ + Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 0); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 19); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 20); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 23); + BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 24); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 25); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 26); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 27); + BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 27); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 32); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 45); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 50); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(ambiguities) +{ + // test scanning of some operators which need look-ahead + Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::LTE); + BOOST_CHECK_EQUAL(scanner.next(), Token::LT); + BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN_ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::INC); + BOOST_CHECK_EQUAL(scanner.next(), Token::ARROW); + BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); +} + + +BOOST_AUTO_TEST_SUITE_END() + +} } } // end namespaces From 961327ee7df11f86886e1b05d1e57f6201de9fc2 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 7 Oct 2014 18:25:04 +0200 Subject: [PATCH 02/34] Solidity parser, can not parse much yet. --- libsolidity/AST.cpp | 23 ++++ libsolidity/AST.h | 287 +++++++++++++++++++++++++++++++++++++++ libsolidity/BaseTypes.h | 22 +++ libsolidity/Parser.cpp | 180 ++++++++++++++++++++++++ libsolidity/Parser.h | 64 +++++++++ libsolidity/Scanner.cpp | 42 ++++-- libsolidity/Scanner.h | 18 +-- libsolidity/Token.h | 35 ++++- libsolidity/grammar.txt | 32 +++++ test/solidityParser.cpp | 52 +++++++ test/solidityScanner.cpp | 37 +++-- 11 files changed, 745 insertions(+), 47 deletions(-) create mode 100644 libsolidity/AST.cpp create mode 100644 libsolidity/AST.h create mode 100644 libsolidity/BaseTypes.h create mode 100644 libsolidity/Parser.cpp create mode 100644 libsolidity/Parser.h create mode 100644 libsolidity/grammar.txt create mode 100644 test/solidityParser.cpp diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp new file mode 100644 index 000000000..ba50b7c6f --- /dev/null +++ b/libsolidity/AST.cpp @@ -0,0 +1,23 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity abstract syntax tree. + */ + +#include diff --git a/libsolidity/AST.h b/libsolidity/AST.h new file mode 100644 index 000000000..f71b5b346 --- /dev/null +++ b/libsolidity/AST.h @@ -0,0 +1,287 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity abstract syntax tree. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace dev { +namespace solidity { + +template +using ptr = std::shared_ptr; +template +using vecptr = std::vector>; + +class VariableDeclaration; +class StructDefinition; +class FunctionDefinition; +class TypeName; +class Block; +class Expression; + +class ASTNode +{ +public: + explicit ASTNode(const Location& _location) + : m_location(_location) + {} +private: + Location m_location; +}; + +class ContractDefinition : public ASTNode +{ +public: + ContractDefinition(const Location& _location, + const std::string& _name, + const vecptr& _definedStructs, + const vecptr& _stateVariables, + const vecptr& _definedFunctions) + : ASTNode(_location), + m_name(_name), + m_definedStructs(_definedStructs), + m_stateVariables(_stateVariables), + m_definedFunctions(_definedFunctions) + {} + +private: + std::string m_name; + vecptr m_definedStructs; + vecptr m_stateVariables; + vecptr m_definedFunctions; +}; + +class StructDefinition : public ASTNode +{ +private: + std::string m_name; + vecptr m_members; +}; + +class FunctionDefinition : public ASTNode +{ +private: + std::string m_name; + vecptr m_arguments; + bool m_isDeclaredConst; + vecptr m_returns; + ptr m_body; +}; + +class VariableDeclaration : public ASTNode +{ +public: + VariableDeclaration(const Location& _location, + const ptr& _type, + const std::string& _name) + : ASTNode(_location), + m_type(_type), + m_name(_name) + {} +private: + ptr m_type; /// m_keyType; + ptr m_valueType; +}; + +/// @} + +/// Statements +/// @{ + +class Statement : public ASTNode +{ +}; + +class Block : public Statement +{ +private: + vecptr m_statements; +}; + +class IfStatement : public Statement +{ + +private: + ptr m_condition; + ptr m_trueBody; + ptr m_falseBody; +}; + +class BreakableStatement : public Statement +{ + +}; + +class WhileStatement : public BreakableStatement +{ +private: + ptr m_condition; + ptr m_body; +}; + +class Continue : public Statement +{ + +}; + +class Break : public Statement +{ + +}; + +class Return : public Statement +{ +private: + ptr m_expression; +}; + +class VariableAssignment : public Statement +{ +private: + ptr m_variable; + Token::Value m_assigmentOperator; + ptr m_rightHandSide; ///< can be missing +}; + +class Expression : public Statement +{ +private: +}; + +/// @} + +/// Expressions +/// @{ + +class Assignment : public Expression +{ +private: + ptr m_leftHandSide; + Token::Value m_assigmentOperator; + ptr m_rightHandSide; +}; + +class UnaryOperation : public Expression +{ +private: + Token::Value m_operator; + ptr m_subExpression; + bool isPrefix; +}; + +class BinaryOperation : public Expression +{ +private: + ptr m_left; + ptr m_right; + Token::Value m_operator; +}; + +class FunctionCall : public Expression +{ +private: + std::string m_functionName; // TODO only calls to fixed, named functions for now + vecptr m_arguments; +}; + +class MemberAccess : public Expression +{ +private: + ptr m_expression; + std::string m_memberName; +}; + +class IndexAccess : public Expression +{ + ptr m_base; + ptr m_index; +}; + +class PrimaryExpression : public Expression +{ +}; + +class Identifier : public PrimaryExpression +{ +private: + std::string m_name; +}; + +class Literal : public PrimaryExpression +{ +private: + std::string m_value; +}; + +/// @} + + +} } diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h new file mode 100644 index 000000000..0cc7f8534 --- /dev/null +++ b/libsolidity/BaseTypes.h @@ -0,0 +1,22 @@ +#pragma once + + +namespace dev { +namespace solidity { + +// Representation of an interval of source positions. +struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + + bool IsValid() const { + return beg_pos >= 0 && end_pos >= beg_pos; + } + + static Location invalid() { return Location(-1, -1); } + + int beg_pos; + int end_pos; +}; + +} } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp new file mode 100644 index 000000000..09ea86045 --- /dev/null +++ b/libsolidity/Parser.cpp @@ -0,0 +1,180 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity parser. + */ + +#include "libsolidity/BaseTypes.h" +#include "libsolidity/Parser.h" +#include "libsolidity/Scanner.h" + +namespace dev { +namespace solidity { + +ptr Parser::parse(Scanner& _scanner) +{ + m_scanner = &_scanner; + + return parseContractDefinition(); +} + + +/// AST node factory that also tracks the begin and end position of an AST node +/// while it is being parsed +class Parser::ASTNodeFactory +{ +public: + ASTNodeFactory(const Parser& _parser) + : m_parser(_parser), + m_location(_parser.getPosition(), -1) + {} + + void markEndPosition() + { + m_location.end_pos = m_parser.getEndPosition(); + } + + /// @todo: check that this actually uses perfect forwarding + template + ptr createNode(Args&&... _args) + { + if (m_location.end_pos < 0) markEndPosition(); + return std::make_shared(m_location, std::forward(_args)...); + } + +private: + const Parser& m_parser; + Location m_location; +}; + +int Parser::getPosition() const +{ + return m_scanner->getCurrentLocation().beg_pos; +} + +int Parser::getEndPosition() const +{ + return m_scanner->getCurrentLocation().end_pos; +} + + +ptr Parser::parseContractDefinition() +{ + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::CONTRACT); + std::string name = expectIdentifier(); + expectToken(Token::LBRACE); + + vecptr structs; + vecptr stateVariables; + vecptr functions; + bool visibilityIsPublic = true; + while (true) { + Token::Value currentToken = m_scanner->getCurrentToken(); + if (currentToken == Token::RBRACE) { + break; + } else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) { + visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); + m_scanner->next(); + expectToken(Token::COLON); + } else if (currentToken == Token::FUNCTION) { + functions.push_back(parseFunctionDefinition(visibilityIsPublic)); + } else if (currentToken == Token::STRUCT) { + structs.push_back(parseStructDefinition()); + expectToken(Token::SEMICOLON); + } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || + Token::IsElementaryTypeName(currentToken)) { + stateVariables.push_back(parseVariableDeclaration()); + expectToken(Token::SEMICOLON); + } else { + throwExpectationError("Function, variable or struct declaration expected."); + } + } + nodeFactory.markEndPosition(); + + m_scanner->next(); + expectToken(Token::EOS); + + return nodeFactory.createNode(name, structs, stateVariables, functions); +} + +ptr Parser::parseFunctionDefinition(bool _isPublic) +{ + (void) _isPublic; + throwExpectationError("Function parsing is not yet implemented."); +} + +ptr Parser::parseStructDefinition() +{ + throwExpectationError("Struct definition parsing is not yet implemented."); +} + +ptr Parser::parseVariableDeclaration() +{ + ASTNodeFactory nodeFactory(*this); + + ptr type; + Token::Value token = m_scanner->getCurrentToken(); + if (Token::IsElementaryTypeName(token)) { + type = ASTNodeFactory(*this).createNode(token); + m_scanner->next(); + } else if (token == Token::VAR) { + type = ASTNodeFactory(*this).createNode(); + m_scanner->next(); + } else if (token == Token::MAPPING) { + // TODO + throwExpectationError("mappings are not yet implemented"); + } else if (token == Token::IDENTIFIER) { + type = ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); + m_scanner->next(); + } else { + throwExpectationError("Expected variable declaration"); + } + nodeFactory.markEndPosition(); + std::string name = expectIdentifier(); + return nodeFactory.createNode(type, name); +} + +void Parser::expectToken(Token::Value _value) +{ + if (m_scanner->getCurrentToken() != _value) + throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value))); + m_scanner->next(); +} + +std::string Parser::expectIdentifier() +{ + if (m_scanner->getCurrentToken() != Token::IDENTIFIER) + throwExpectationError("Expected identifier"); + + std::string literal = m_scanner->getCurrentLiteral(); + m_scanner->next(); + return literal; +} + +void Parser::throwExpectationError(const std::string& _description) +{ + (void) _description; + /// @todo make a proper exception hierarchy + throw std::exception();//_description); +} + + +} } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h new file mode 100644 index 000000000..4a48dace5 --- /dev/null +++ b/libsolidity/Parser.h @@ -0,0 +1,64 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity parser. + */ + +#pragma once + +#include "libsolidity/AST.h" + +namespace dev { +namespace solidity { + +class Scanner; + +class Parser +{ +public: + ptr parse(Scanner& _scanner); + +private: + class ASTNodeFactory; + + /// Start position of the current token + int getPosition() const; + /// End position of the current token + int getEndPosition() const; + + /// Parsing functions for the AST nodes + /// @{ + ptr parseContractDefinition(); + ptr parseFunctionDefinition(bool _isPublic); + ptr parseStructDefinition(); + ptr parseVariableDeclaration(); + /// @} + + /// Helper functions + /// @{ + /// If current token value is not _value, throw exception otherwise advance token. + void expectToken(Token::Value _value); + std::string expectIdentifier(); + void throwExpectationError(const std::string& _description); + /// @} + + Scanner* m_scanner; +}; + +} } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 101b4a1ab..a936e24f5 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -82,16 +82,10 @@ void Scanner::reset(const CharStream& _source) { m_source = _source; - // Initialize current_ to not refer to a literal. - m_current_token.token = Token::ILLEGAL; - m_current_token.literal.clear(); - - m_hasLineTerminatorBeforeNext = true; - m_hasMultilineCommentBeforeNext = false; - m_char = m_source.get(); skipWhitespace(); scanToken(); + next(); } @@ -466,7 +460,7 @@ Token::Value Scanner::scanString() literal.Complete(); advance(); // consume quote - return Token::STRING; + return Token::STRING_LITERAL; } @@ -551,13 +545,17 @@ Token::Value Scanner::scanNumber(bool _periodSeen) // Keyword Matcher #define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ + KEYWORD_GROUP('a') \ + KEYWORD("address", Token::BREAK) \ KEYWORD_GROUP('b') \ KEYWORD("break", Token::BREAK) \ + KEYWORD("bool", Token::BOOL) \ KEYWORD_GROUP('c') \ KEYWORD("case", Token::CASE) \ KEYWORD("catch", Token::CATCH) \ KEYWORD("const", Token::CONST) \ KEYWORD("continue", Token::CONTINUE) \ + KEYWORD("contract", Token::CONTRACT) \ KEYWORD_GROUP('d') \ KEYWORD("debugger", Token::DEBUGGER) \ KEYWORD("default", Token::DEFAULT) \ @@ -571,31 +569,55 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("finally", Token::FINALLY) \ KEYWORD("for", Token::FOR) \ KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('h') \ + KEYWORD("hash", Token::HASH) \ + KEYWORD("hash32", Token::HASH32) \ + KEYWORD("hash64", Token::HASH64) \ + KEYWORD("hash128", Token::HASH128) \ + KEYWORD("hash256", Token::HASH256) \ KEYWORD_GROUP('i') \ KEYWORD("if", Token::IF) \ KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD("in", Token::IN) \ KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("int", Token::INT) \ + KEYWORD("int32", Token::INT32) \ + KEYWORD("int64", Token::INT64) \ + KEYWORD("int128", Token::INT128) \ + KEYWORD("int256", Token::INT256) \ KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD_GROUP('l') \ + KEYWORD_GROUP('m') \ KEYWORD_GROUP('n') \ + KEYWORD("mapping", Token::MAPPING) \ KEYWORD("new", Token::NEW) \ KEYWORD("null", Token::NULL_LITERAL) \ KEYWORD_GROUP('p') \ KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::PRIVATE) \ KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("public", Token::PUBLIC) \ KEYWORD_GROUP('r') \ + KEYWORD("real", Token::REAL) \ KEYWORD("return", Token::RETURN) \ KEYWORD_GROUP('s') \ + KEYWORD("string", Token::STRING_TYPE) \ + KEYWORD("struct", Token::STRUCT) \ KEYWORD("switch", Token::SWITCH) \ KEYWORD_GROUP('t') \ + KEYWORD("text", Token::TEXT) \ KEYWORD("this", Token::THIS) \ KEYWORD("throw", Token::THROW) \ KEYWORD("true", Token::TRUE_LITERAL) \ KEYWORD("try", Token::TRY) \ KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('u') \ + KEYWORD("uint", Token::UINT) \ + KEYWORD("uint32", Token::UINT32) \ + KEYWORD("uint64", Token::UINT64) \ + KEYWORD("uint128", Token::UINT128) \ + KEYWORD("uint256", Token::UINT256) \ + KEYWORD("ureal", Token::UREAL) \ KEYWORD_GROUP('v') \ KEYWORD("var", Token::VAR) \ KEYWORD("void", Token::VOID) \ diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 3cf52fbc6..d2dcad29d 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -47,6 +47,7 @@ #include #include #include +#include #include namespace dev { @@ -111,21 +112,6 @@ public: bool complete_; }; - // Representation of an interval of source positions. - struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } - - bool IsValid() const { - return beg_pos >= 0 && end_pos >= beg_pos; - } - - static Location invalid() { return Location(-1, -1); } - - int beg_pos; - int end_pos; - }; - explicit Scanner(const CharStream& _source); // Resets the scanner as if newly constructed with _input as input. @@ -163,8 +149,6 @@ private: std::string literal; }; - static const int kCharacterLookaheadBufferSize = 1; - // Literal buffer support inline void startNewLiteral() { m_next_token.literal.clear(); diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 4f5ec1943..7a39b989f 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -152,6 +152,7 @@ namespace solidity { K(CASE, "case", 0) \ K(CATCH, "catch", 0) \ K(CONTINUE, "continue", 0) \ + K(CONTRACT, "contract", 0) \ K(DEBUGGER, "debugger", 0) \ K(DEFAULT, "default", 0) \ /* DELETE */ \ @@ -163,8 +164,12 @@ namespace solidity { K(IF, "if", 0) \ /* IN */ \ /* INSTANCEOF */ \ + K(MAPPING, "mapping", 0) \ K(NEW, "new", 0) \ + K(PUBLIC, "public", 0) \ + K(PRIVATE, "private", 0) \ K(RETURN, "return", 0) \ + K(STRUCT, "struct", 0) \ K(SWITCH, "switch", 0) \ K(THIS, "this", 0) \ K(THROW, "throw", 0) \ @@ -175,12 +180,36 @@ namespace solidity { K(WHILE, "while", 0) \ K(WITH, "with", 0) \ \ + /* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \ + K(INT, "int", 0) \ + K(INT32, "int32", 0) \ + K(INT64, "int64", 0) \ + K(INT128, "int128", 0) \ + K(INT256, "int256", 0) \ + K(UINT, "uint", 0) \ + K(UINT32, "uint32", 0) \ + K(UINT64, "uint64", 0) \ + K(UINT128, "uint128", 0) \ + K(UINT256, "uint256", 0) \ + K(HASH, "hash", 0) \ + K(HASH32, "hash32", 0) \ + K(HASH64, "hash64", 0) \ + K(HASH128, "hash128", 0) \ + K(HASH256, "hash256", 0) \ + K(ADDRESS, "address", 0) \ + K(BOOL, "bool", 0) \ + K(STRING_TYPE, "string", 0) \ + K(TEXT, "text", 0) \ + K(REAL, "real", 0) \ + K(UREAL, "ureal", 0) \ + T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ + \ /* Literals (ECMA-262, section 7.8, page 16). */ \ K(NULL_LITERAL, "null", 0) \ K(TRUE_LITERAL, "true", 0) \ K(FALSE_LITERAL, "false", 0) \ T(NUMBER, NULL, 0) \ - T(STRING, NULL, 0) \ + T(STRING_LITERAL, NULL, 0) \ \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, NULL, 0) \ @@ -231,6 +260,10 @@ class Token { return tok == IDENTIFIER; } + static bool IsElementaryTypeName(Value tok) { + return INT <= tok && tok < TYPES_END; + } + static bool IsAssignmentOp(Value tok) { return INIT_VAR <= tok && tok <= ASSIGN_MOD; } diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt new file mode 100644 index 000000000..aec02489e --- /dev/null +++ b/libsolidity/grammar.txt @@ -0,0 +1,32 @@ +ContractDefinition = 'contract' Identifier '{' ContractPart* '}' +ContractPart = VariableDeclaration ';' | StructDefinition ';' | + FunctionDefinition ';' | 'public:' | 'private:' + +StructDefinition = 'struct' Identifier '{' + ( VariableDeclaration (';' VariableDeclaration)* )? '} + +FunctionDefinition = 'function' Identifier ArgumentList 'const'? + 'returns' ArgumentList Block +ArgumentList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' +// semantic restriction: mappings and structs (recursively) containing mappings +// are not allowed in argument lists +VariableDeclaration = TypeName Identifier +TypeName = PredefinedType | Identifier | MappingType +MappingType = 'mapping' '(' SimplePredefinedType '=>' TypeName ')' + +Block = '{' Statement* '}' +Statement = IfStatement | WhileStatement | Continue | Break | Return | VariableAssignment | Expression ';' | Block + +IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? +WhileStatement = 'while' '(' Expression ')' Statement +Continue = 'continue' ';' +Break = 'break' ';' +Return = 'return' Expression? ';' +VariableAssignment = VariableDeclaration ( AssignmentOp Expression )? ';' + +Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | MemberAccess | PrimaryExpression +Assignment = Expression (AssignmentOp Expression) +FunctionCall = Identifier '(' ( Expression ( ',' Expression )* ) ')' +MemberAccess = Expression '.' Identifier +IndexAccess = Expression '[' Expresison ']' +PrimaryExpression = Identifier | NumberLiteral | StringLiteral | '(' Expression ')' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp new file mode 100644 index 000000000..f42506767 --- /dev/null +++ b/test/solidityParser.cpp @@ -0,0 +1,52 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Unit tests for the solidity parser. + */ + +#include + +#include +#include +#include + +namespace dev { +namespace solidity { +namespace test { + +BOOST_AUTO_TEST_SUITE(SolidityParser) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + std::string text = "contract test123 {\n" + "\tuint256 stateVariable1;\n" + "}\n"; + Parser parser; + CharStream str(text); + // @todo: figure out why this does not compile + //Scanner scanner(CharStream(text)); + Scanner scanner(str); + BOOST_CHECK_NO_THROW(parser.parse(scanner)); +} + + +BOOST_AUTO_TEST_SUITE_END() + +} } } // end namespaces + diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index afbcdffae..7f84146a3 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -27,21 +27,26 @@ namespace dev { namespace solidity { namespace test { -BOOST_AUTO_TEST_SUITE(solidity) +BOOST_AUTO_TEST_SUITE(SolidityScanner) + +BOOST_AUTO_TEST_CASE(test_empty) +{ + Scanner scanner(CharStream("")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); +} BOOST_AUTO_TEST_CASE(smoke_test) { Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::FUNCTION); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::FUNCTION); BOOST_CHECK_EQUAL(scanner.next(), Token::BREAK); BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "765"); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1"); BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string2"); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "identifier1"); @@ -51,26 +56,23 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_AUTO_TEST_CASE(string_escapes) { Scanner scanner(CharStream(" { \"a\\x61\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "aa"); } BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), std::string("aa\0abc", 6)); } BOOST_AUTO_TEST_CASE(string_escape_illegal) { Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); // TODO recovery from illegal tokens should be improved @@ -83,8 +85,7 @@ BOOST_AUTO_TEST_CASE(string_escape_illegal) BOOST_AUTO_TEST_CASE(hex_numbers) { Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::VAR); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::VAR); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); @@ -96,8 +97,7 @@ BOOST_AUTO_TEST_CASE(hex_numbers) BOOST_AUTO_TEST_CASE(locations) { Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 0); BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 19); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); @@ -122,8 +122,7 @@ BOOST_AUTO_TEST_CASE(ambiguities) { // test scanning of some operators which need look-ahead Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::LTE); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LTE); BOOST_CHECK_EQUAL(scanner.next(), Token::LT); BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN_ADD); From 4c1739169a08353a04b52f520ee5df0b7531ad79 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 8 Oct 2014 20:53:50 +0200 Subject: [PATCH 03/34] Parse everything up to function bodies and report parser errors with location. --- libsolidity/AST.h | 91 +++++++++++++++++++------- libsolidity/BaseTypes.h | 17 ++--- libsolidity/Parser.cpp | 137 +++++++++++++++++++++++++++++++++------ libsolidity/Parser.h | 8 ++- libsolidity/Scanner.cpp | 48 ++++++++++++-- libsolidity/Scanner.h | 16 +++++ libsolidity/Token.h | 1 + libsolidity/grammar.txt | 14 ++-- test/solidityParser.cpp | 103 ++++++++++++++++++++++++++--- test/solidityScanner.cpp | 24 +++---- 10 files changed, 370 insertions(+), 89 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f71b5b346..86c5022d8 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -32,6 +32,9 @@ namespace dev { namespace solidity { +// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do +// not do reference counting but point to a special memory area that is completely released +// explicitly. template using ptr = std::shared_ptr; template @@ -47,9 +50,11 @@ class Expression; class ASTNode { public: - explicit ASTNode(const Location& _location) + explicit ASTNode(Location const& _location) : m_location(_location) {} + + Location getLocation() const { return m_location; } private: Location m_location; }; @@ -57,13 +62,12 @@ private: class ContractDefinition : public ASTNode { public: - ContractDefinition(const Location& _location, - const std::string& _name, - const vecptr& _definedStructs, - const vecptr& _stateVariables, - const vecptr& _definedFunctions) - : ASTNode(_location), - m_name(_name), + ContractDefinition(Location const& _location, + std::string const& _name, + vecptr const& _definedStructs, + vecptr const& _stateVariables, + vecptr const& _definedFunctions) + : ASTNode(_location), m_name(_name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions) @@ -78,33 +82,61 @@ private: class StructDefinition : public ASTNode { +public: + StructDefinition(Location const& _location, + std::string const& _name, + vecptr const& _members) + : ASTNode(_location), m_name(_name), m_members(_members) + {} private: std::string m_name; vecptr m_members; }; +/// Used as function parameter list and return list +/// None of the parameters is allowed to contain mappings (not even recursively +/// inside structs) +class ParameterList : public ASTNode +{ +public: + ParameterList(Location const& _location, vecptr const& _parameters) + : ASTNode(_location), m_parameters(_parameters) + {} +private: + vecptr m_parameters; +}; + class FunctionDefinition : public ASTNode { +public: + FunctionDefinition(Location const& _location, std::string const& _name, bool _isPublic, + ptr const& _parameters, + bool _isDeclaredConst, + ptr const& _returnParameters, + ptr const& _body) + : ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters), + m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), + m_body(_body) + {} private: std::string m_name; - vecptr m_arguments; + bool m_isPublic; + ptr m_parameters; bool m_isDeclaredConst; - vecptr m_returns; + ptr m_returnParameters; ptr m_body; }; class VariableDeclaration : public ASTNode { public: - VariableDeclaration(const Location& _location, - const ptr& _type, - const std::string& _name) - : ASTNode(_location), - m_type(_type), - m_name(_name) + VariableDeclaration(Location const& _location, + ptr const& _type, + std::string const& _name) + : ASTNode(_location), m_type(_type), m_name(_name) {} private: - ptr m_type; /// m_type; ///< can be empty ("var") std::string m_name; }; @@ -114,7 +146,7 @@ private: class TypeName : public ASTNode { public: - explicit TypeName(const Location& _location) + explicit TypeName(Location const& _location) : ASTNode(_location) {} }; @@ -123,7 +155,7 @@ public: class ElementaryTypeName : public TypeName { public: - explicit ElementaryTypeName(const Location& _location, Token::Value _type) + explicit ElementaryTypeName(Location const& _location, Token::Value _type) : TypeName(_location), m_type(_type) {} private: @@ -133,18 +165,19 @@ private: class UserDefinedTypeName : public TypeName { public: - UserDefinedTypeName(const Location& _location, const std::string& _name) + UserDefinedTypeName(Location const& _location, std::string const& _name) : TypeName(_location), m_name(_name) {} private: std::string m_name; }; -class MappingTypeName : public TypeName +class Mapping : public TypeName { public: - explicit MappingTypeName(const Location& _location) - : TypeName(_location) + Mapping(Location const& _location, ptr const& _keyType, + ptr const& _valueType) + : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} private: ptr m_keyType; @@ -158,10 +191,18 @@ private: class Statement : public ASTNode { +public: + explicit Statement(Location const& _location) + : ASTNode(_location) + {} }; class Block : public Statement { +public: + explicit Block(Location const& _location) + : Statement(_location) + {} private: vecptr m_statements; }; @@ -245,10 +286,12 @@ private: Token::Value m_operator; }; +/// Can be ordinary function call, type cast or struct construction. class FunctionCall : public Expression { private: - std::string m_functionName; // TODO only calls to fixed, named functions for now + // if m_functionName is the name of a type, store the token directly + std::string m_functionName; // "in place" calls of return values are not possible for now vecptr m_arguments; }; diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index 0cc7f8534..2e92b07e8 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -4,19 +4,16 @@ namespace dev { namespace solidity { -// Representation of an interval of source positions. +/// Representation of an interval of source positions. +/// The interval includes start and excludes end. struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } + Location(int _start, int _end) : start(_start), end(_end) { } + Location() : start(-1), end(-1) { } - bool IsValid() const { - return beg_pos >= 0 && end_pos >= beg_pos; - } + bool IsValid() const { return start >= 0 && end >= start; } - static Location invalid() { return Location(-1, -1); } - - int beg_pos; - int end_pos; + int start; + int end; }; } } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 09ea86045..2df1b5762 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -20,6 +20,7 @@ * Solidity parser. */ +#include "libdevcore/Log.h" #include "libsolidity/BaseTypes.h" #include "libsolidity/Parser.h" #include "libsolidity/Scanner.h" @@ -27,9 +28,9 @@ namespace dev { namespace solidity { -ptr Parser::parse(Scanner& _scanner) +ptr Parser::parse(std::shared_ptr const& _scanner) { - m_scanner = &_scanner; + m_scanner = _scanner; return parseContractDefinition(); } @@ -41,20 +42,22 @@ class Parser::ASTNodeFactory { public: ASTNodeFactory(const Parser& _parser) - : m_parser(_parser), - m_location(_parser.getPosition(), -1) + : m_parser(_parser), m_location(_parser.getPosition(), -1) {} - void markEndPosition() + void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + + /// Set the end position to the one of the given node. + void setEndPositionFromNode(const ptr& _node) { - m_location.end_pos = m_parser.getEndPosition(); + m_location.end = _node->getLocation().end; } /// @todo: check that this actually uses perfect forwarding template ptr createNode(Args&&... _args) { - if (m_location.end_pos < 0) markEndPosition(); + if (m_location.end < 0) markEndPosition(); return std::make_shared(m_location, std::forward(_args)...); } @@ -65,12 +68,12 @@ private: int Parser::getPosition() const { - return m_scanner->getCurrentLocation().beg_pos; + return m_scanner->getCurrentLocation().start; } int Parser::getEndPosition() const { - return m_scanner->getCurrentLocation().end_pos; + return m_scanner->getCurrentLocation().end; } @@ -98,7 +101,6 @@ ptr Parser::parseContractDefinition() functions.push_back(parseFunctionDefinition(visibilityIsPublic)); } else if (currentToken == Token::STRUCT) { structs.push_back(parseStructDefinition()); - expectToken(Token::SEMICOLON); } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::IsElementaryTypeName(currentToken)) { stateVariables.push_back(parseVariableDeclaration()); @@ -117,19 +119,57 @@ ptr Parser::parseContractDefinition() ptr Parser::parseFunctionDefinition(bool _isPublic) { - (void) _isPublic; - throwExpectationError("Function parsing is not yet implemented."); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::FUNCTION); + std::string name(expectIdentifier()); + ptr parameters(parseParameterList()); + bool isDeclaredConst = false; + if (m_scanner->getCurrentToken() == Token::CONST) { + isDeclaredConst = true; + m_scanner->next(); + } + ptr returnParameters; + if (m_scanner->getCurrentToken() == Token::RETURNS) { + m_scanner->next(); + returnParameters = parseParameterList(); + } + ptr block = parseBlock(); + nodeFactory.setEndPositionFromNode(block); + return nodeFactory.createNode(name, _isPublic, parameters, + isDeclaredConst, returnParameters, block); } ptr Parser::parseStructDefinition() { - throwExpectationError("Struct definition parsing is not yet implemented."); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::STRUCT); + std::string name = expectIdentifier(); + vecptr members; + expectToken(Token::LBRACE); + while (m_scanner->getCurrentToken() != Token::RBRACE) { + members.push_back(parseVariableDeclaration()); + expectToken(Token::SEMICOLON); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBRACE); + + return nodeFactory.createNode(name, members); } ptr Parser::parseVariableDeclaration() { ASTNodeFactory nodeFactory(*this); + ptr type = parseTypeName(); + nodeFactory.markEndPosition(); + std::string name = expectIdentifier(); + return nodeFactory.createNode(type, name); +} + +ptr Parser::parseTypeName() +{ ptr type; Token::Value token = m_scanner->getCurrentToken(); if (Token::IsElementaryTypeName(token)) { @@ -139,17 +179,67 @@ ptr Parser::parseVariableDeclaration() type = ASTNodeFactory(*this).createNode(); m_scanner->next(); } else if (token == Token::MAPPING) { - // TODO - throwExpectationError("mappings are not yet implemented"); + type = parseMapping(); } else if (token == Token::IDENTIFIER) { type = ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); m_scanner->next(); } else { - throwExpectationError("Expected variable declaration"); + throwExpectationError("Expected type name"); } + + return type; +} + +ptr Parser::parseMapping() +{ + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::MAPPING); + expectToken(Token::LPAREN); + + if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken())) + throwExpectationError("Expected elementary type name for mapping key type"); + ptr keyType; + keyType = ASTNodeFactory(*this).createNode(m_scanner->getCurrentToken()); + m_scanner->next(); + + expectToken(Token::ARROW); + ptr valueType = parseTypeName(); nodeFactory.markEndPosition(); - std::string name = expectIdentifier(); - return nodeFactory.createNode(type, name); + expectToken(Token::RPAREN); + + return nodeFactory.createNode(keyType, valueType); +} + +ptr Parser::parseParameterList() +{ + ASTNodeFactory nodeFactory(*this); + + vecptr parameters; + expectToken(Token::LPAREN); + if (m_scanner->getCurrentToken() != Token::RPAREN) { + parameters.push_back(parseVariableDeclaration()); + while (m_scanner->getCurrentToken() != Token::RPAREN) { + expectToken(Token::COMMA); + parameters.push_back(parseVariableDeclaration()); + } + } + nodeFactory.markEndPosition(); + m_scanner->next(); + return nodeFactory.createNode(parameters); +} + +ptr Parser::parseBlock() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::LBRACE); + while (m_scanner->getCurrentToken() != Token::RBRACE) { + m_scanner->next(); + // @todo + } + nodeFactory.markEndPosition(); + expectToken(Token::RBRACE); + return nodeFactory.createNode(); } void Parser::expectToken(Token::Value _value) @@ -171,9 +261,16 @@ std::string Parser::expectIdentifier() void Parser::throwExpectationError(const std::string& _description) { - (void) _description; + int line, column; + std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); + cwarn << "Solidity parser error: " << _description + << "at line " << (line + 1) + << ", column " << (column + 1); + cwarn << m_scanner->getLineAtPosition(getPosition()); + cwarn << std::string(column, ' ') << "^"; + /// @todo make a proper exception hierarchy - throw std::exception();//_description); + throw std::exception(); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 4a48dace5..96f1d6883 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -32,7 +32,7 @@ class Scanner; class Parser { public: - ptr parse(Scanner& _scanner); + ptr parse(std::shared_ptr const& _scanner); private: class ASTNodeFactory; @@ -48,6 +48,10 @@ private: ptr parseFunctionDefinition(bool _isPublic); ptr parseStructDefinition(); ptr parseVariableDeclaration(); + ptr parseTypeName(); + ptr parseMapping(); + ptr parseParameterList(); + ptr parseBlock(); /// @} /// Helper functions @@ -58,7 +62,7 @@ private: void throwExpectationError(const std::string& _description); /// @} - Scanner* m_scanner; + std::shared_ptr m_scanner; }; } } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index a936e24f5..836e0d092 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -40,6 +40,9 @@ // You should have received a copy of the GNU General Public License // along with cpp-ethereum. If not, see . +#include +#include + #include namespace dev { @@ -121,6 +124,7 @@ Token::Value Scanner::next() return m_current_token.token; } + bool Scanner::skipWhitespace() { const int start_position = getSourcePos(); @@ -182,7 +186,7 @@ void Scanner::scanToken() Token::Value token; do { // Remember the position of the next token - m_next_token.location.beg_pos = getSourcePos(); + m_next_token.location.start = getSourcePos(); switch (m_char) { case '\n': @@ -401,7 +405,7 @@ void Scanner::scanToken() // whitespace. } while (token == Token::WHITESPACE); - m_next_token.location.end_pos = getSourcePos(); + m_next_token.location.end = getSourcePos(); m_next_token.token = token; } @@ -546,7 +550,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen) #define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ KEYWORD_GROUP('a') \ - KEYWORD("address", Token::BREAK) \ + KEYWORD("address", Token::ADDRESS) \ KEYWORD_GROUP('b') \ KEYWORD("break", Token::BREAK) \ KEYWORD("bool", Token::BOOL) \ @@ -588,8 +592,8 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD_GROUP('l') \ KEYWORD_GROUP('m') \ - KEYWORD_GROUP('n') \ KEYWORD("mapping", Token::MAPPING) \ + KEYWORD_GROUP('n') \ KEYWORD("new", Token::NEW) \ KEYWORD("null", Token::NULL_LITERAL) \ KEYWORD_GROUP('p') \ @@ -600,8 +604,9 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD_GROUP('r') \ KEYWORD("real", Token::REAL) \ KEYWORD("return", Token::RETURN) \ + KEYWORD("returns", Token::RETURNS) \ KEYWORD_GROUP('s') \ - KEYWORD("string", Token::STRING_TYPE) \ + KEYWORD("string", Token::STRING_TYPE) \ KEYWORD("struct", Token::STRUCT) \ KEYWORD("switch", Token::SWITCH) \ KEYWORD_GROUP('t') \ @@ -671,5 +676,38 @@ Token::Value Scanner::scanIdentifierOrKeyword() return KeywordOrIdentifierToken(m_next_token.literal); } +std::string CharStream::getLineAtPosition(int _position) const +{ + // if _position points to \n, it returns the line before the \n + using size_type = std::string::size_type; + size_type searchStart = std::min(m_source.size(), _position); + if (searchStart > 0) searchStart--; + size_type lineStart = m_source.rfind('\n', searchStart); + if (lineStart == std::string::npos) + lineStart = 0; + else + lineStart++; + return m_source.substr(lineStart, + std::min(m_source.find('\n', lineStart), + m_source.size()) - lineStart); +} + +std::tuple CharStream::translatePositionToLineColumn(int _position) const +{ + using size_type = std::string::size_type; + size_type searchPosition = std::min(m_source.size(), _position); + int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); + + size_type lineStart; + if (searchPosition == 0) { + lineStart = 0; + } else { + lineStart = m_source.rfind('\n', searchPosition - 1); + lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; + } + + return std::tuple(lineNumber, searchPosition - lineStart); +} + } } diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index d2dcad29d..7b1408c0b 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -82,6 +82,12 @@ public: return get(); } + /// Functions that help pretty-printing parse errors + /// Do only use in error cases, they are quite expensive. + /// @{ + std::string getLineAtPosition(int _position) const; + std::tuple translatePositionToLineColumn(int _position) const; + /// @} private: std::string m_source; size_t m_pos; @@ -134,6 +140,16 @@ public: Location peekLocation() const { return m_next_token.location; } const std::string& peekLiteral() const { return m_next_token.literal; } + /// Functions that help pretty-printing parse errors. + /// Do only use in error cases, they are quite expensive. + /// @{ + std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } + std::tuple translatePositionToLineColumn(int _position) const + { + return m_source.translatePositionToLineColumn(_position); + } + /// @} + // Returns true if there was a line terminator before the peek'ed token, // possibly inside a multi-line comment. bool hasAnyLineTerminatorBeforeNext() const { diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 7a39b989f..4476b3837 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -169,6 +169,7 @@ namespace solidity { K(PUBLIC, "public", 0) \ K(PRIVATE, "private", 0) \ K(RETURN, "return", 0) \ + K(RETURNS, "returns", 0) \ K(STRUCT, "struct", 0) \ K(SWITCH, "switch", 0) \ K(THIS, "this", 0) \ diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index aec02489e..1946325f4 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -1,18 +1,18 @@ ContractDefinition = 'contract' Identifier '{' ContractPart* '}' -ContractPart = VariableDeclaration ';' | StructDefinition ';' | - FunctionDefinition ';' | 'public:' | 'private:' +ContractPart = VariableDeclaration ';' | StructDefinition | + FunctionDefinition | 'public:' | 'private:' StructDefinition = 'struct' Identifier '{' ( VariableDeclaration (';' VariableDeclaration)* )? '} -FunctionDefinition = 'function' Identifier ArgumentList 'const'? - 'returns' ArgumentList Block -ArgumentList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' +FunctionDefinition = 'function' Identifier ParameterList 'const'? + ( 'returns' ParameterList )? Block +ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' // semantic restriction: mappings and structs (recursively) containing mappings // are not allowed in argument lists VariableDeclaration = TypeName Identifier -TypeName = PredefinedType | Identifier | MappingType -MappingType = 'mapping' '(' SimplePredefinedType '=>' TypeName ')' +TypeName = ElementaryTypeName | Identifier | Mapping +Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | Continue | Break | Return | VariableAssignment | Expression ';' | Block diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index f42506767..91247a3b7 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -22,6 +22,7 @@ #include +#include #include #include #include @@ -30,19 +31,103 @@ namespace dev { namespace solidity { namespace test { +namespace { + ptr parseText(const std::string& _source) + { + Parser parser; + return parser.parse(std::make_shared(CharStream(_source))); + } +} + BOOST_AUTO_TEST_SUITE(SolidityParser) BOOST_AUTO_TEST_CASE(smoke_test) { - std::string text = "contract test123 {\n" - "\tuint256 stateVariable1;\n" - "}\n"; - Parser parser; - CharStream str(text); - // @todo: figure out why this does not compile - //Scanner scanner(CharStream(text)); - Scanner scanner(str); - BOOST_CHECK_NO_THROW(parser.parse(scanner)); + char const* text = "contract test {\n" + " uint256 stateVariable1;\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration) +{ + char const* text = "contract test {\n" + " uint256 ;\n" + "}\n"; + cwarn << "The next error is expected."; + BOOST_CHECK_THROW(parseText(text), std::exception); +} + +BOOST_AUTO_TEST_CASE(empty_function) +{ + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " function functionName(hash160 arg1, address addr) const\n" + " returns (int id)\n" + " { }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(no_function_params) +{ + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " function functionName() {}\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(single_function_param) +{ + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " function functionName(hash hashin) returns (hash hashout) {}\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(struct_definition) +{ + char const* text = "contract test {\n" + " uint256 stateVar;\n" + " struct MyStructName {\n" + " address addr;\n" + " uint256 count;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(mapping) +{ + char const* text = "contract test {\n" + " mapping(address => string) names;\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(mapping_in_struct) +{ + char const* text = "contract test {\n" + " struct test_struct {\n" + " address addr;\n" + " uint256 count;\n" + " mapping(hash => test_struct) self_reference;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(mapping_to_mapping_in_struct) +{ + char const* text = "contract test {\n" + " struct test_struct {\n" + " address addr;\n" + " mapping (uint64 => mapping (hash => uint)) complex_mapping;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); } diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index 7f84146a3..759d2f101 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -98,23 +98,23 @@ BOOST_AUTO_TEST_CASE(locations) { Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 0); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 19); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 0); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 19); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 20); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 23); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 20); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 23); BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 24); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 25); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 24); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 25); BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 26); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 27); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 26); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 27); BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 27); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 32); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 27); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 32); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 45); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 50); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 45); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 50); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } From 3fd9358c3e33e90261a4aec36a0d38e408e85496 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 9 Oct 2014 12:28:37 +0200 Subject: [PATCH 04/34] Corrected indentation. --- libsolidity/AST.cpp | 22 +- libsolidity/AST.h | 228 +++++----- libsolidity/BaseTypes.h | 10 +- libsolidity/Parser.cpp | 382 +++++++++-------- libsolidity/Parser.h | 73 ++-- libsolidity/Scanner.cpp | 894 ++++++++++++++++++++-------------------- libsolidity/Scanner.h | 354 ++++++++-------- libsolidity/Token.cpp | 8 +- libsolidity/Token.h | 594 +++++++++++++------------- 9 files changed, 1292 insertions(+), 1273 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index ba50b7c6f..76e1ff444 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 86c5022d8..dad257f23 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -50,47 +50,47 @@ class Expression; class ASTNode { public: - explicit ASTNode(Location const& _location) - : m_location(_location) - {} + explicit ASTNode(Location const& _location) + : m_location(_location) + {} - Location getLocation() const { return m_location; } + Location getLocation() const { return m_location; } private: - Location m_location; + Location m_location; }; class ContractDefinition : public ASTNode { public: - ContractDefinition(Location const& _location, - std::string const& _name, - vecptr const& _definedStructs, - vecptr const& _stateVariables, - vecptr const& _definedFunctions) - : ASTNode(_location), m_name(_name), - m_definedStructs(_definedStructs), - m_stateVariables(_stateVariables), - m_definedFunctions(_definedFunctions) - {} + ContractDefinition(Location const& _location, + std::string const& _name, + vecptr const& _definedStructs, + vecptr const& _stateVariables, + vecptr const& _definedFunctions) + : ASTNode(_location), m_name(_name), + m_definedStructs(_definedStructs), + m_stateVariables(_stateVariables), + m_definedFunctions(_definedFunctions) + {} private: - std::string m_name; - vecptr m_definedStructs; - vecptr m_stateVariables; - vecptr m_definedFunctions; + std::string m_name; + vecptr m_definedStructs; + vecptr m_stateVariables; + vecptr m_definedFunctions; }; class StructDefinition : public ASTNode { public: - StructDefinition(Location const& _location, - std::string const& _name, - vecptr const& _members) - : ASTNode(_location), m_name(_name), m_members(_members) - {} + StructDefinition(Location const& _location, + std::string const& _name, + vecptr const& _members) + : ASTNode(_location), m_name(_name), m_members(_members) + {} private: - std::string m_name; - vecptr m_members; + std::string m_name; + vecptr m_members; }; /// Used as function parameter list and return list @@ -99,45 +99,45 @@ private: class ParameterList : public ASTNode { public: - ParameterList(Location const& _location, vecptr const& _parameters) - : ASTNode(_location), m_parameters(_parameters) - {} + ParameterList(Location const& _location, vecptr const& _parameters) + : ASTNode(_location), m_parameters(_parameters) + {} private: - vecptr m_parameters; + vecptr m_parameters; }; class FunctionDefinition : public ASTNode { public: - FunctionDefinition(Location const& _location, std::string const& _name, bool _isPublic, - ptr const& _parameters, - bool _isDeclaredConst, - ptr const& _returnParameters, - ptr const& _body) - : ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters), - m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), - m_body(_body) - {} + FunctionDefinition(Location const& _location, std::string const& _name, bool _isPublic, + ptr const& _parameters, + bool _isDeclaredConst, + ptr const& _returnParameters, + ptr const& _body) + : ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters), + m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), + m_body(_body) + {} private: - std::string m_name; - bool m_isPublic; - ptr m_parameters; - bool m_isDeclaredConst; - ptr m_returnParameters; - ptr m_body; + std::string m_name; + bool m_isPublic; + ptr m_parameters; + bool m_isDeclaredConst; + ptr m_returnParameters; + ptr m_body; }; class VariableDeclaration : public ASTNode { public: - VariableDeclaration(Location const& _location, - ptr const& _type, - std::string const& _name) - : ASTNode(_location), m_type(_type), m_name(_name) - {} + VariableDeclaration(Location const& _location, + ptr const& _type, + std::string const& _name) + : ASTNode(_location), m_type(_type), m_name(_name) + {} private: - ptr m_type; ///< can be empty ("var") - std::string m_name; + ptr m_type; ///< can be empty ("var") + std::string m_name; }; /// types @@ -146,42 +146,42 @@ private: class TypeName : public ASTNode { public: - explicit TypeName(Location const& _location) - : ASTNode(_location) - {} + explicit TypeName(Location const& _location) + : ASTNode(_location) + {} }; /// any pre-defined type that is not a mapping class ElementaryTypeName : public TypeName { public: - explicit ElementaryTypeName(Location const& _location, Token::Value _type) - : TypeName(_location), m_type(_type) - {} + explicit ElementaryTypeName(Location const& _location, Token::Value _type) + : TypeName(_location), m_type(_type) + {} private: - Token::Value m_type; + Token::Value m_type; }; class UserDefinedTypeName : public TypeName { public: - UserDefinedTypeName(Location const& _location, std::string const& _name) - : TypeName(_location), m_name(_name) - {} + UserDefinedTypeName(Location const& _location, std::string const& _name) + : TypeName(_location), m_name(_name) + {} private: - std::string m_name; + std::string m_name; }; class Mapping : public TypeName { public: - Mapping(Location const& _location, ptr const& _keyType, - ptr const& _valueType) - : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) - {} + Mapping(Location const& _location, ptr const& _keyType, + ptr const& _valueType) + : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) + {} private: - ptr m_keyType; - ptr m_valueType; + ptr m_keyType; + ptr m_valueType; }; /// @} @@ -192,28 +192,28 @@ private: class Statement : public ASTNode { public: - explicit Statement(Location const& _location) - : ASTNode(_location) - {} + explicit Statement(Location const& _location) + : ASTNode(_location) + {} }; class Block : public Statement { public: - explicit Block(Location const& _location) - : Statement(_location) - {} + explicit Block(Location const& _location, vecptr const& _statements) + : Statement(_location), m_statements(_statements) + {} private: - vecptr m_statements; + vecptr m_statements; }; class IfStatement : public Statement { private: - ptr m_condition; - ptr m_trueBody; - ptr m_falseBody; + ptr m_condition; + ptr m_trueBody; + ptr m_falseBody; }; class BreakableStatement : public Statement @@ -224,8 +224,8 @@ class BreakableStatement : public Statement class WhileStatement : public BreakableStatement { private: - ptr m_condition; - ptr m_body; + ptr m_condition; + ptr m_body; }; class Continue : public Statement @@ -241,15 +241,15 @@ class Break : public Statement class Return : public Statement { private: - ptr m_expression; + ptr m_expression; }; class VariableAssignment : public Statement { private: - ptr m_variable; - Token::Value m_assigmentOperator; - ptr m_rightHandSide; ///< can be missing + ptr m_variable; + Token::Value m_assigmentOperator; + ptr m_rightHandSide; ///< can be missing }; class Expression : public Statement @@ -265,47 +265,47 @@ private: class Assignment : public Expression { private: - ptr m_leftHandSide; - Token::Value m_assigmentOperator; - ptr m_rightHandSide; + ptr m_leftHandSide; + Token::Value m_assigmentOperator; + ptr m_rightHandSide; }; class UnaryOperation : public Expression { private: - Token::Value m_operator; - ptr m_subExpression; - bool isPrefix; + Token::Value m_operator; + ptr m_subExpression; + bool isPrefix; }; class BinaryOperation : public Expression { private: - ptr m_left; - ptr m_right; - Token::Value m_operator; + ptr m_left; + ptr m_right; + Token::Value m_operator; }; /// Can be ordinary function call, type cast or struct construction. class FunctionCall : public Expression { private: - // if m_functionName is the name of a type, store the token directly - std::string m_functionName; // "in place" calls of return values are not possible for now - vecptr m_arguments; + // if m_functionName is the name of a type, store the token directly + std::string m_functionName; // "in place" calls of return values are not possible for now + vecptr m_arguments; }; class MemberAccess : public Expression { private: - ptr m_expression; - std::string m_memberName; + ptr m_expression; + std::string m_memberName; }; class IndexAccess : public Expression { - ptr m_base; - ptr m_index; + ptr m_base; + ptr m_index; }; class PrimaryExpression : public Expression @@ -315,13 +315,13 @@ class PrimaryExpression : public Expression class Identifier : public PrimaryExpression { private: - std::string m_name; + std::string m_name; }; class Literal : public PrimaryExpression { private: - std::string m_value; + std::string m_value; }; /// @} diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index 2e92b07e8..a037f6553 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -7,13 +7,13 @@ namespace solidity { /// Representation of an interval of source positions. /// The interval includes start and excludes end. struct Location { - Location(int _start, int _end) : start(_start), end(_end) { } - Location() : start(-1), end(-1) { } + Location(int _start, int _end) : start(_start), end(_end) { } + Location() : start(-1), end(-1) { } - bool IsValid() const { return start >= 0 && end >= start; } + bool IsValid() const { return start >= 0 && end >= start; } - int start; - int end; + int start; + int end; }; } } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 2df1b5762..2886b2c18 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -30,9 +30,9 @@ namespace solidity { ptr Parser::parse(std::shared_ptr const& _scanner) { - m_scanner = _scanner; + m_scanner = _scanner; - return parseContractDefinition(); + return parseContractDefinition(); } @@ -41,236 +41,254 @@ ptr Parser::parse(std::shared_ptr const& _scanner) class Parser::ASTNodeFactory { public: - ASTNodeFactory(const Parser& _parser) - : m_parser(_parser), m_location(_parser.getPosition(), -1) - {} - - void markEndPosition() { m_location.end = m_parser.getEndPosition(); } - - /// Set the end position to the one of the given node. - void setEndPositionFromNode(const ptr& _node) - { - m_location.end = _node->getLocation().end; - } - - /// @todo: check that this actually uses perfect forwarding - template - ptr createNode(Args&&... _args) - { - if (m_location.end < 0) markEndPosition(); - return std::make_shared(m_location, std::forward(_args)...); - } + ASTNodeFactory(const Parser& _parser) + : m_parser(_parser), m_location(_parser.getPosition(), -1) + {} + + void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + + /// Set the end position to the one of the given node. + void setEndPositionFromNode(const ptr& _node) + { + m_location.end = _node->getLocation().end; + } + + /// @todo: check that this actually uses perfect forwarding + template + ptr createNode(Args&&... _args) + { + if (m_location.end < 0) markEndPosition(); + return std::make_shared(m_location, std::forward(_args)...); + } private: - const Parser& m_parser; - Location m_location; + const Parser& m_parser; + Location m_location; }; int Parser::getPosition() const { - return m_scanner->getCurrentLocation().start; + return m_scanner->getCurrentLocation().start; } int Parser::getEndPosition() const { - return m_scanner->getCurrentLocation().end; + return m_scanner->getCurrentLocation().end; } ptr Parser::parseContractDefinition() { - ASTNodeFactory nodeFactory(*this); - - expectToken(Token::CONTRACT); - std::string name = expectIdentifier(); - expectToken(Token::LBRACE); - - vecptr structs; - vecptr stateVariables; - vecptr functions; - bool visibilityIsPublic = true; - while (true) { - Token::Value currentToken = m_scanner->getCurrentToken(); - if (currentToken == Token::RBRACE) { - break; - } else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) { - visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); - m_scanner->next(); - expectToken(Token::COLON); - } else if (currentToken == Token::FUNCTION) { - functions.push_back(parseFunctionDefinition(visibilityIsPublic)); - } else if (currentToken == Token::STRUCT) { - structs.push_back(parseStructDefinition()); - } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || - Token::IsElementaryTypeName(currentToken)) { - stateVariables.push_back(parseVariableDeclaration()); - expectToken(Token::SEMICOLON); - } else { - throwExpectationError("Function, variable or struct declaration expected."); - } - } - nodeFactory.markEndPosition(); - - m_scanner->next(); - expectToken(Token::EOS); - - return nodeFactory.createNode(name, structs, stateVariables, functions); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::CONTRACT); + std::string name = expectIdentifier(); + expectToken(Token::LBRACE); + + vecptr structs; + vecptr stateVariables; + vecptr functions; + bool visibilityIsPublic = true; + while (true) { + Token::Value currentToken = m_scanner->getCurrentToken(); + if (currentToken == Token::RBRACE) { + break; + } else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) { + visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); + m_scanner->next(); + expectToken(Token::COLON); + } else if (currentToken == Token::FUNCTION) { + functions.push_back(parseFunctionDefinition(visibilityIsPublic)); + } else if (currentToken == Token::STRUCT) { + structs.push_back(parseStructDefinition()); + } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || + Token::IsElementaryTypeName(currentToken)) { + stateVariables.push_back(parseVariableDeclaration()); + expectToken(Token::SEMICOLON); + } else { + throwExpectationError("Function, variable or struct declaration expected."); + } + } + nodeFactory.markEndPosition(); + + m_scanner->next(); + expectToken(Token::EOS); + + return nodeFactory.createNode(name, structs, stateVariables, functions); } ptr Parser::parseFunctionDefinition(bool _isPublic) { - ASTNodeFactory nodeFactory(*this); - - expectToken(Token::FUNCTION); - std::string name(expectIdentifier()); - ptr parameters(parseParameterList()); - bool isDeclaredConst = false; - if (m_scanner->getCurrentToken() == Token::CONST) { - isDeclaredConst = true; - m_scanner->next(); - } - ptr returnParameters; - if (m_scanner->getCurrentToken() == Token::RETURNS) { - m_scanner->next(); - returnParameters = parseParameterList(); - } - ptr block = parseBlock(); - nodeFactory.setEndPositionFromNode(block); - return nodeFactory.createNode(name, _isPublic, parameters, - isDeclaredConst, returnParameters, block); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::FUNCTION); + std::string name(expectIdentifier()); + ptr parameters(parseParameterList()); + bool isDeclaredConst = false; + if (m_scanner->getCurrentToken() == Token::CONST) { + isDeclaredConst = true; + m_scanner->next(); + } + ptr returnParameters; + if (m_scanner->getCurrentToken() == Token::RETURNS) { + m_scanner->next(); + returnParameters = parseParameterList(); + } + ptr block = parseBlock(); + nodeFactory.setEndPositionFromNode(block); + return nodeFactory.createNode(name, _isPublic, parameters, + isDeclaredConst, returnParameters, block); } ptr Parser::parseStructDefinition() { - ASTNodeFactory nodeFactory(*this); - - expectToken(Token::STRUCT); - std::string name = expectIdentifier(); - vecptr members; - expectToken(Token::LBRACE); - while (m_scanner->getCurrentToken() != Token::RBRACE) { - members.push_back(parseVariableDeclaration()); - expectToken(Token::SEMICOLON); - } - nodeFactory.markEndPosition(); - expectToken(Token::RBRACE); - - return nodeFactory.createNode(name, members); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::STRUCT); + std::string name = expectIdentifier(); + vecptr members; + expectToken(Token::LBRACE); + while (m_scanner->getCurrentToken() != Token::RBRACE) { + members.push_back(parseVariableDeclaration()); + expectToken(Token::SEMICOLON); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBRACE); + + return nodeFactory.createNode(name, members); } ptr Parser::parseVariableDeclaration() { - ASTNodeFactory nodeFactory(*this); + ASTNodeFactory nodeFactory(*this); - ptr type = parseTypeName(); - nodeFactory.markEndPosition(); - std::string name = expectIdentifier(); - return nodeFactory.createNode(type, name); + ptr type = parseTypeName(); + nodeFactory.markEndPosition(); + std::string name = expectIdentifier(); + return nodeFactory.createNode(type, name); } ptr Parser::parseTypeName() { - ptr type; - Token::Value token = m_scanner->getCurrentToken(); - if (Token::IsElementaryTypeName(token)) { - type = ASTNodeFactory(*this).createNode(token); - m_scanner->next(); - } else if (token == Token::VAR) { - type = ASTNodeFactory(*this).createNode(); - m_scanner->next(); - } else if (token == Token::MAPPING) { - type = parseMapping(); - } else if (token == Token::IDENTIFIER) { - type = ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); - m_scanner->next(); - } else { - throwExpectationError("Expected type name"); - } - - return type; + ptr type; + Token::Value token = m_scanner->getCurrentToken(); + if (Token::IsElementaryTypeName(token)) { + type = ASTNodeFactory(*this).createNode(token); + m_scanner->next(); + } else if (token == Token::VAR) { + type = ASTNodeFactory(*this).createNode(); + m_scanner->next(); + } else if (token == Token::MAPPING) { + type = parseMapping(); + } else if (token == Token::IDENTIFIER) { + type = ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); + m_scanner->next(); + } else { + throwExpectationError("Expected type name"); + } + + return type; } ptr Parser::parseMapping() { - ASTNodeFactory nodeFactory(*this); + ASTNodeFactory nodeFactory(*this); - expectToken(Token::MAPPING); - expectToken(Token::LPAREN); + expectToken(Token::MAPPING); + expectToken(Token::LPAREN); - if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken())) - throwExpectationError("Expected elementary type name for mapping key type"); - ptr keyType; - keyType = ASTNodeFactory(*this).createNode(m_scanner->getCurrentToken()); - m_scanner->next(); + if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken())) + throwExpectationError("Expected elementary type name for mapping key type"); + ptr keyType; + keyType = ASTNodeFactory(*this).createNode(m_scanner->getCurrentToken()); + m_scanner->next(); - expectToken(Token::ARROW); - ptr valueType = parseTypeName(); - nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); + expectToken(Token::ARROW); + ptr valueType = parseTypeName(); + nodeFactory.markEndPosition(); + expectToken(Token::RPAREN); - return nodeFactory.createNode(keyType, valueType); + return nodeFactory.createNode(keyType, valueType); } ptr Parser::parseParameterList() { - ASTNodeFactory nodeFactory(*this); - - vecptr parameters; - expectToken(Token::LPAREN); - if (m_scanner->getCurrentToken() != Token::RPAREN) { - parameters.push_back(parseVariableDeclaration()); - while (m_scanner->getCurrentToken() != Token::RPAREN) { - expectToken(Token::COMMA); - parameters.push_back(parseVariableDeclaration()); - } - } - nodeFactory.markEndPosition(); - m_scanner->next(); - return nodeFactory.createNode(parameters); + ASTNodeFactory nodeFactory(*this); + + vecptr parameters; + expectToken(Token::LPAREN); + if (m_scanner->getCurrentToken() != Token::RPAREN) { + parameters.push_back(parseVariableDeclaration()); + while (m_scanner->getCurrentToken() != Token::RPAREN) { + expectToken(Token::COMMA); + parameters.push_back(parseVariableDeclaration()); + } + } + nodeFactory.markEndPosition(); + m_scanner->next(); + return nodeFactory.createNode(parameters); } ptr Parser::parseBlock() { - ASTNodeFactory nodeFactory(*this); - expectToken(Token::LBRACE); - while (m_scanner->getCurrentToken() != Token::RBRACE) { - m_scanner->next(); - // @todo - } - nodeFactory.markEndPosition(); - expectToken(Token::RBRACE); - return nodeFactory.createNode(); + + ASTNodeFactory nodeFactory(*this); + expectToken(Token::LBRACE); + vecptr statements; + while (m_scanner->getCurrentToken() != Token::RBRACE) { + m_scanner->next(); + statements.push_back(parseStatement()); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBRACE); + return nodeFactory.createNode(statements); +} + +ptr Parser::parseStatement() +{ + + switch (m_scanner->getCurrentToken()) { + case Token::IF: + return parseIfStatement(); + case Token::WHILE: + return parseWhileStatement(); + case Token::LBRACE: + return parseBlock(); + // starting from here, all statements must be terminated by a semicolon + case Token::CONTINUE: // all following + return + } } void Parser::expectToken(Token::Value _value) { - if (m_scanner->getCurrentToken() != _value) - throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value))); - m_scanner->next(); + if (m_scanner->getCurrentToken() != _value) + throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value))); + m_scanner->next(); } std::string Parser::expectIdentifier() { - if (m_scanner->getCurrentToken() != Token::IDENTIFIER) - throwExpectationError("Expected identifier"); + if (m_scanner->getCurrentToken() != Token::IDENTIFIER) + throwExpectationError("Expected identifier"); - std::string literal = m_scanner->getCurrentLiteral(); - m_scanner->next(); - return literal; + std::string literal = m_scanner->getCurrentLiteral(); + m_scanner->next(); + return literal; } void Parser::throwExpectationError(const std::string& _description) { - int line, column; - std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); - cwarn << "Solidity parser error: " << _description - << "at line " << (line + 1) - << ", column " << (column + 1); - cwarn << m_scanner->getLineAtPosition(getPosition()); - cwarn << std::string(column, ' ') << "^"; - - /// @todo make a proper exception hierarchy - throw std::exception(); + int line, column; + std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); + cwarn << "Solidity parser error: " << _description + << "at line " << (line + 1) + << ", column " << (column + 1); + cwarn << m_scanner->getLineAtPosition(getPosition()); + cwarn << std::string(column, ' ') << "^"; + + /// @todo make a proper exception hierarchy + throw std::exception(); } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 96f1d6883..65409a296 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -32,37 +32,38 @@ class Scanner; class Parser { public: - ptr parse(std::shared_ptr const& _scanner); + ptr parse(std::shared_ptr const& _scanner); private: - class ASTNodeFactory; + class ASTNodeFactory; - /// Start position of the current token - int getPosition() const; - /// End position of the current token - int getEndPosition() const; + /// Start position of the current token + int getPosition() const; + /// End position of the current token + int getEndPosition() const; - /// Parsing functions for the AST nodes - /// @{ - ptr parseContractDefinition(); - ptr parseFunctionDefinition(bool _isPublic); - ptr parseStructDefinition(); - ptr parseVariableDeclaration(); - ptr parseTypeName(); - ptr parseMapping(); - ptr parseParameterList(); - ptr parseBlock(); - /// @} + /// Parsing functions for the AST nodes + /// @{ + ptr parseContractDefinition(); + ptr parseFunctionDefinition(bool _isPublic); + ptr parseStructDefinition(); + ptr parseVariableDeclaration(); + ptr parseTypeName(); + ptr parseMapping(); + ptr parseParameterList(); + ptr parseBlock(); + ptr parseStatement(); + /// @} - /// Helper functions - /// @{ - /// If current token value is not _value, throw exception otherwise advance token. - void expectToken(Token::Value _value); - std::string expectIdentifier(); - void throwExpectationError(const std::string& _description); - /// @} + /// Helper functions + /// @{ + /// If current token value is not _value, throw exception otherwise advance token. + void expectToken(Token::Value _value); + std::string expectIdentifier(); + void throwExpectationError(const std::string& _description); + /// @} - std::shared_ptr m_scanner; + std::shared_ptr m_scanner; }; } } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 836e0d092..334da8e7f 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -49,66 +49,66 @@ namespace dev { namespace solidity { namespace { - bool IsDecimalDigit(char c) { - return '0' <= c && c <= '9'; - } - bool IsHexDigit(char c) { - return IsDecimalDigit(c) - || ('a' <= c && c <= 'f') - || ('A' <= c && c <= 'F'); - } - bool IsLineTerminator(char c) { return c == '\n'; } - bool IsWhiteSpace(char c) { - return c == ' ' || c == '\n' || c == '\t'; - } - bool IsIdentifierStart(char c) { - return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); - } - bool IsIdentifierPart(char c) { - return IsIdentifierStart(c) || IsDecimalDigit(c); - } - - int HexValue(char c) { - if (c >= '0' && c <= '9') return c - '0'; - else if (c >= 'a' && c <= 'f') return c - 'a' + 10; - else if (c >= 'A' && c <= 'F') return c - 'A' + 10; - else return -1; - } + bool IsDecimalDigit(char c) { + return '0' <= c && c <= '9'; + } + bool IsHexDigit(char c) { + return IsDecimalDigit(c) + || ('a' <= c && c <= 'f') + || ('A' <= c && c <= 'F'); + } + bool IsLineTerminator(char c) { return c == '\n'; } + bool IsWhiteSpace(char c) { + return c == ' ' || c == '\n' || c == '\t'; + } + bool IsIdentifierStart(char c) { + return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + bool IsIdentifierPart(char c) { + return IsIdentifierStart(c) || IsDecimalDigit(c); + } + + int HexValue(char c) { + if (c >= '0' && c <= '9') return c - '0'; + else if (c >= 'a' && c <= 'f') return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') return c - 'A' + 10; + else return -1; + } } Scanner::Scanner(const CharStream& _source) { - reset(_source); + reset(_source); } void Scanner::reset(const CharStream& _source) { - m_source = _source; + m_source = _source; - m_char = m_source.get(); - skipWhitespace(); - scanToken(); - next(); + m_char = m_source.get(); + skipWhitespace(); + scanToken(); + next(); } bool Scanner::scanHexNumber(char& scanned_number, int expected_length) { - BOOST_ASSERT(expected_length <= 4); // prevent overflow - - char x = 0; - for (int i = 0; i < expected_length; i++) { - int d = HexValue(m_char); - if (d < 0) { - rollback(i); - return false; - } - x = x * 16 + d; - advance(); - } - - scanned_number = x; - return true; + BOOST_ASSERT(expected_length <= 4); // prevent overflow + + char x = 0; + for (int i = 0; i < expected_length; i++) { + int d = HexValue(m_char); + if (d < 0) { + rollback(i); + return false; + } + x = x * 16 + d; + advance(); + } + + scanned_number = x; + return true; } @@ -117,29 +117,29 @@ BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); Token::Value Scanner::next() { - m_current_token = m_next_token; - m_hasLineTerminatorBeforeNext = false; - m_hasMultilineCommentBeforeNext = false; - scanToken(); - return m_current_token.token; + m_current_token = m_next_token; + m_hasLineTerminatorBeforeNext = false; + m_hasMultilineCommentBeforeNext = false; + scanToken(); + return m_current_token.token; } bool Scanner::skipWhitespace() { - const int start_position = getSourcePos(); - - while (true) { - if (IsLineTerminator(m_char)) { - m_hasLineTerminatorBeforeNext = true; - } else if (!IsWhiteSpace(m_char)) { - break; - } - advance(); - } - - // Return whether or not we skipped any characters. - return getSourcePos() != start_position; + const int start_position = getSourcePos(); + + while (true) { + if (IsLineTerminator(m_char)) { + m_hasLineTerminatorBeforeNext = true; + } else if (!IsWhiteSpace(m_char)) { + break; + } + advance(); + } + + // Return whether or not we skipped any characters. + return getSourcePos() != start_position; } @@ -156,28 +156,28 @@ Token::Value Scanner::skipSingleLineComment() Token::Value Scanner::skipMultiLineComment() { - BOOST_ASSERT(m_char == '*'); - advance(); - - while (!isSourcePastEndOfInput()) { - char ch = m_char; - advance(); - if (IsLineTerminator(ch)) { - // Following ECMA-262, section 7.4, a comment containing - // a newline will make the comment count as a line-terminator. - m_hasMultilineCommentBeforeNext = true; - } - // If we have reached the end of the multi-line comment, we - // consume the '/' and insert a whitespace. This way all - // multi-line comments are treated as whitespace. - if (ch == '*' && m_char == '/') { - m_char = ' '; - return Token::WHITESPACE; - } - } - - // Unterminated multi-line comment. - return Token::ILLEGAL; + BOOST_ASSERT(m_char == '*'); + advance(); + + while (!isSourcePastEndOfInput()) { + char ch = m_char; + advance(); + if (IsLineTerminator(ch)) { + // Following ECMA-262, section 7.4, a comment containing + // a newline will make the comment count as a line-terminator. + m_hasMultilineCommentBeforeNext = true; + } + // If we have reached the end of the multi-line comment, we + // consume the '/' and insert a whitespace. This way all + // multi-line comments are treated as whitespace. + if (ch == '*' && m_char == '/') { + m_char = ' '; + return Token::WHITESPACE; + } + } + + // Unterminated multi-line comment. + return Token::ILLEGAL; } void Scanner::scanToken() @@ -185,224 +185,224 @@ void Scanner::scanToken() m_next_token.literal.clear(); Token::Value token; do { - // Remember the position of the next token - m_next_token.location.start = getSourcePos(); - - switch (m_char) { - case '\n': - m_hasLineTerminatorBeforeNext = true; // fall-through - case ' ': - case '\t': - token = selectToken(Token::WHITESPACE); - break; - - case '"': case '\'': - token = scanString(); - break; - - case '<': - // < <= << <<= - advance(); - if (m_char == '=') { - token = selectToken(Token::LTE); - } else if (m_char == '<') { - token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); - } else { - token = Token::LT; - } - break; - - case '>': - // > >= >> >>= >>> >>>= - advance(); - if (m_char == '=') { - token = selectToken(Token::GTE); - } else if (m_char == '>') { - // >> >>= >>> >>>= - advance(); - if (m_char == '=') { - token = selectToken(Token::ASSIGN_SAR); - } else if (m_char == '>') { - token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); - } else { - token = Token::SAR; - } - } else { - token = Token::GT; - } - break; - - case '=': - // = == => - advance(); - if (m_char == '=') { - token = selectToken(Token::EQ); - } else if (m_char == '>') { - token = selectToken(Token::ARROW); - } else { - token = Token::ASSIGN; - } - break; - - case '!': - // ! != !== - advance(); - if (m_char == '=') { - token = selectToken(Token::NE); - } else { - token = Token::NOT; - } - break; - - case '+': - // + ++ += - advance(); - if (m_char == '+') { - token = selectToken(Token::INC); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_ADD); - } else { - token = Token::ADD; - } - break; - - case '-': - // - -- -= - advance(); - if (m_char == '-') { - advance(); - token = Token::DEC; - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_SUB); - } else { - token = Token::SUB; - } - break; - - case '*': - // * *= - token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); - break; - - case '%': - // % %= - token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); - break; - - case '/': - // / // /* /= - advance(); - if (m_char == '/') { - token = skipSingleLineComment(); - } else if (m_char == '*') { - token = skipMultiLineComment(); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_DIV); - } else { - token = Token::DIV; - } - break; - - case '&': - // & && &= - advance(); - if (m_char == '&') { - token = selectToken(Token::AND); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_BIT_AND); - } else { - token = Token::BIT_AND; - } - break; - - case '|': - // | || |= - advance(); - if (m_char == '|') { - token = selectToken(Token::OR); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_BIT_OR); - } else { - token = Token::BIT_OR; - } - break; - - case '^': - // ^ ^= - token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - break; - - case '.': - // . Number - advance(); - if (IsDecimalDigit(m_char)) { - token = scanNumber(true); - } else { - token = Token::PERIOD; - } - break; - - case ':': - token = selectToken(Token::COLON); - break; - - case ';': - token = selectToken(Token::SEMICOLON); - break; - - case ',': - token = selectToken(Token::COMMA); - break; - - case '(': - token = selectToken(Token::LPAREN); - break; - - case ')': - token = selectToken(Token::RPAREN); - break; - - case '[': - token = selectToken(Token::LBRACK); - break; - - case ']': - token = selectToken(Token::RBRACK); - break; - - case '{': - token = selectToken(Token::LBRACE); - break; - - case '}': - token = selectToken(Token::RBRACE); - break; - - case '?': - token = selectToken(Token::CONDITIONAL); - break; - - case '~': - token = selectToken(Token::BIT_NOT); - break; - - default: - if (IsIdentifierStart(m_char)) { - token = scanIdentifierOrKeyword(); - } else if (IsDecimalDigit(m_char)) { - token = scanNumber(false); - } else if (skipWhitespace()) { - token = Token::WHITESPACE; - } else if (isSourcePastEndOfInput()) { - token = Token::EOS; - } else { - token = selectToken(Token::ILLEGAL); - } - break; - } - - // Continue scanning for tokens as long as we're just skipping - // whitespace. + // Remember the position of the next token + m_next_token.location.start = getSourcePos(); + + switch (m_char) { + case '\n': + m_hasLineTerminatorBeforeNext = true; // fall-through + case ' ': + case '\t': + token = selectToken(Token::WHITESPACE); + break; + + case '"': case '\'': + token = scanString(); + break; + + case '<': + // < <= << <<= + advance(); + if (m_char == '=') { + token = selectToken(Token::LTE); + } else if (m_char == '<') { + token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); + } else { + token = Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + advance(); + if (m_char == '=') { + token = selectToken(Token::GTE); + } else if (m_char == '>') { + // >> >>= >>> >>>= + advance(); + if (m_char == '=') { + token = selectToken(Token::ASSIGN_SAR); + } else if (m_char == '>') { + token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); + } else { + token = Token::SAR; + } + } else { + token = Token::GT; + } + break; + + case '=': + // = == => + advance(); + if (m_char == '=') { + token = selectToken(Token::EQ); + } else if (m_char == '>') { + token = selectToken(Token::ARROW); + } else { + token = Token::ASSIGN; + } + break; + + case '!': + // ! != !== + advance(); + if (m_char == '=') { + token = selectToken(Token::NE); + } else { + token = Token::NOT; + } + break; + + case '+': + // + ++ += + advance(); + if (m_char == '+') { + token = selectToken(Token::INC); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_ADD); + } else { + token = Token::ADD; + } + break; + + case '-': + // - -- -= + advance(); + if (m_char == '-') { + advance(); + token = Token::DEC; + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_SUB); + } else { + token = Token::SUB; + } + break; + + case '*': + // * *= + token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); + break; + + case '%': + // % %= + token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); + break; + + case '/': + // / // /* /= + advance(); + if (m_char == '/') { + token = skipSingleLineComment(); + } else if (m_char == '*') { + token = skipMultiLineComment(); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_DIV); + } else { + token = Token::DIV; + } + break; + + case '&': + // & && &= + advance(); + if (m_char == '&') { + token = selectToken(Token::AND); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_BIT_AND); + } else { + token = Token::BIT_AND; + } + break; + + case '|': + // | || |= + advance(); + if (m_char == '|') { + token = selectToken(Token::OR); + } else if (m_char == '=') { + token = selectToken(Token::ASSIGN_BIT_OR); + } else { + token = Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + + case '.': + // . Number + advance(); + if (IsDecimalDigit(m_char)) { + token = scanNumber(true); + } else { + token = Token::PERIOD; + } + break; + + case ':': + token = selectToken(Token::COLON); + break; + + case ';': + token = selectToken(Token::SEMICOLON); + break; + + case ',': + token = selectToken(Token::COMMA); + break; + + case '(': + token = selectToken(Token::LPAREN); + break; + + case ')': + token = selectToken(Token::RPAREN); + break; + + case '[': + token = selectToken(Token::LBRACK); + break; + + case ']': + token = selectToken(Token::RBRACK); + break; + + case '{': + token = selectToken(Token::LBRACE); + break; + + case '}': + token = selectToken(Token::RBRACE); + break; + + case '?': + token = selectToken(Token::CONDITIONAL); + break; + + case '~': + token = selectToken(Token::BIT_NOT); + break; + + default: + if (IsIdentifierStart(m_char)) { + token = scanIdentifierOrKeyword(); + } else if (IsDecimalDigit(m_char)) { + token = scanNumber(false); + } else if (skipWhitespace()) { + token = Token::WHITESPACE; + } else if (isSourcePastEndOfInput()) { + token = Token::EOS; + } else { + token = selectToken(Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. } while (token == Token::WHITESPACE); m_next_token.location.end = getSourcePos(); @@ -411,67 +411,67 @@ void Scanner::scanToken() bool Scanner::scanEscape() { - char c = m_char; - advance(); - - // Skip escaped newlines. - if (IsLineTerminator(c)) - return true; - - switch (c) { - case '\'': // fall through - case '"' : // fall through - case '\\': break; - case 'b' : c = '\b'; break; - case 'f' : c = '\f'; break; - case 'n' : c = '\n'; break; - case 'r' : c = '\r'; break; - case 't' : c = '\t'; break; - case 'u' : { - if (!scanHexNumber(c, 4)) return false; - break; - } - case 'v' : c = '\v'; break; - case 'x' : { - if (!scanHexNumber(c, 2)) return false; - break; - } - } - - // According to ECMA-262, section 7.8.4, characters not covered by the - // above cases should be illegal, but they are commonly handled as - // non-escaped characters by JS VMs. - addLiteralChar(c); - return true; + char c = m_char; + advance(); + + // Skip escaped newlines. + if (IsLineTerminator(c)) + return true; + + switch (c) { + case '\'': // fall through + case '"' : // fall through + case '\\': break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + case 'u' : { + if (!scanHexNumber(c, 4)) return false; + break; + } + case 'v' : c = '\v'; break; + case 'x' : { + if (!scanHexNumber(c, 2)) return false; + break; + } + } + + // According to ECMA-262, section 7.8.4, characters not covered by the + // above cases should be illegal, but they are commonly handled as + // non-escaped characters by JS VMs. + addLiteralChar(c); + return true; } Token::Value Scanner::scanString() { - const char quote = m_char; - advance(); // consume quote - - LiteralScope literal(this); - while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) { - char c = m_char; - advance(); - if (c == '\\') { - if (isSourcePastEndOfInput() || !scanEscape()) return Token::ILLEGAL; - } else { - addLiteralChar(c); - } - } - if (m_char != quote) return Token::ILLEGAL; - literal.Complete(); - - advance(); // consume quote - return Token::STRING_LITERAL; + const char quote = m_char; + advance(); // consume quote + + LiteralScope literal(this); + while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) { + char c = m_char; + advance(); + if (c == '\\') { + if (isSourcePastEndOfInput() || !scanEscape()) return Token::ILLEGAL; + } else { + addLiteralChar(c); + } + } + if (m_char != quote) return Token::ILLEGAL; + literal.Complete(); + + advance(); // consume quote + return Token::STRING_LITERAL; } void Scanner::scanDecimalDigits() { - while (IsDecimalDigit(m_char)) - addLiteralCharAndAdvance(); + while (IsDecimalDigit(m_char)) + addLiteralCharAndAdvance(); } @@ -483,53 +483,53 @@ Token::Value Scanner::scanNumber(bool _periodSeen) LiteralScope literal(this); if (_periodSeen) { - // we have already seen a decimal point of the float - addLiteralChar('.'); - scanDecimalDigits(); // we know we have at least one digit + // we have already seen a decimal point of the float + addLiteralChar('.'); + scanDecimalDigits(); // we know we have at least one digit } else { - // if the first character is '0' we must check for octals and hex - if (m_char == '0') { - addLiteralCharAndAdvance(); - - // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or - // an octal number. - if (m_char == 'x' || m_char == 'X') { - // hex number - kind = HEX; - addLiteralCharAndAdvance(); - if (!IsHexDigit(m_char)) { - // we must have at least one hex digit after 'x'/'X' - return Token::ILLEGAL; - } - while (IsHexDigit(m_char)) { - addLiteralCharAndAdvance(); - } - } - } - - // Parse decimal digits and allow trailing fractional part. - if (kind == DECIMAL) { - scanDecimalDigits(); // optional - if (m_char == '.') { - addLiteralCharAndAdvance(); - scanDecimalDigits(); // optional - } - } + // if the first character is '0' we must check for octals and hex + if (m_char == '0') { + addLiteralCharAndAdvance(); + + // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or + // an octal number. + if (m_char == 'x' || m_char == 'X') { + // hex number + kind = HEX; + addLiteralCharAndAdvance(); + if (!IsHexDigit(m_char)) { + // we must have at least one hex digit after 'x'/'X' + return Token::ILLEGAL; + } + while (IsHexDigit(m_char)) { + addLiteralCharAndAdvance(); + } + } + } + + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) { + scanDecimalDigits(); // optional + if (m_char == '.') { + addLiteralCharAndAdvance(); + scanDecimalDigits(); // optional + } + } } // scan exponent, if any if (m_char == 'e' || m_char == 'E') { - BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number - if (kind != DECIMAL) return Token::ILLEGAL; - // scan exponent - addLiteralCharAndAdvance(); - if (m_char == '+' || m_char == '-') - addLiteralCharAndAdvance(); - if (!IsDecimalDigit(m_char)) { - // we must have at least one decimal digit after 'e'/'E' - return Token::ILLEGAL; - } - scanDecimalDigits(); + BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind != DECIMAL) return Token::ILLEGAL; + // scan exponent + addLiteralCharAndAdvance(); + if (m_char == '+' || m_char == '-') + addLiteralCharAndAdvance(); + if (!IsDecimalDigit(m_char)) { + // we must have at least one decimal digit after 'e'/'E' + return Token::ILLEGAL; + } + scanDecimalDigits(); } // The source character immediately following a numeric literal must @@ -537,7 +537,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen) // section 7.8.3, page 17 (note that we read only one decimal digit // if the value is 0). if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char)) - return Token::ILLEGAL; + return Token::ILLEGAL; literal.Complete(); @@ -637,76 +637,76 @@ static Token::Value KeywordOrIdentifierToken(const std::string& input) const int kMinLength = 2; const int kMaxLength = 10; if (input.size() < kMinLength || input.size() > kMaxLength) { - return Token::IDENTIFIER; + return Token::IDENTIFIER; } switch (input[0]) { - default: + default: #define KEYWORD_GROUP_CASE(ch) \ - break; \ - case ch: + break; \ + case ch: #define KEYWORD(keyword, token) \ - { \ - /* 'keyword' is a char array, so sizeof(keyword) is */ \ - /* strlen(keyword) plus 1 for the NUL char. */ \ - const int keyword_length = sizeof(keyword) - 1; \ - BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ - BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ - if (input == keyword) { \ - return token; \ - } \ - } - KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) + { \ + /* 'keyword' is a char array, so sizeof(keyword) is */ \ + /* strlen(keyword) plus 1 for the NUL char. */ \ + const int keyword_length = sizeof(keyword) - 1; \ + BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ + BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ + if (input == keyword) { \ + return token; \ + } \ + } + KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) } return Token::IDENTIFIER; } Token::Value Scanner::scanIdentifierOrKeyword() { - BOOST_ASSERT(IsIdentifierStart(m_char)); - LiteralScope literal(this); + BOOST_ASSERT(IsIdentifierStart(m_char)); + LiteralScope literal(this); - addLiteralCharAndAdvance(); + addLiteralCharAndAdvance(); - // Scan the rest of the identifier characters. - while (IsIdentifierPart(m_char)) - addLiteralCharAndAdvance(); + // Scan the rest of the identifier characters. + while (IsIdentifierPart(m_char)) + addLiteralCharAndAdvance(); - literal.Complete(); + literal.Complete(); - return KeywordOrIdentifierToken(m_next_token.literal); + return KeywordOrIdentifierToken(m_next_token.literal); } std::string CharStream::getLineAtPosition(int _position) const { - // if _position points to \n, it returns the line before the \n - using size_type = std::string::size_type; - size_type searchStart = std::min(m_source.size(), _position); - if (searchStart > 0) searchStart--; - size_type lineStart = m_source.rfind('\n', searchStart); - if (lineStart == std::string::npos) - lineStart = 0; - else - lineStart++; - return m_source.substr(lineStart, - std::min(m_source.find('\n', lineStart), - m_source.size()) - lineStart); + // if _position points to \n, it returns the line before the \n + using size_type = std::string::size_type; + size_type searchStart = std::min(m_source.size(), _position); + if (searchStart > 0) searchStart--; + size_type lineStart = m_source.rfind('\n', searchStart); + if (lineStart == std::string::npos) + lineStart = 0; + else + lineStart++; + return m_source.substr(lineStart, + std::min(m_source.find('\n', lineStart), + m_source.size()) - lineStart); } std::tuple CharStream::translatePositionToLineColumn(int _position) const { - using size_type = std::string::size_type; - size_type searchPosition = std::min(m_source.size(), _position); - int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); - - size_type lineStart; - if (searchPosition == 0) { - lineStart = 0; - } else { - lineStart = m_source.rfind('\n', searchPosition - 1); - lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; - } - - return std::tuple(lineNumber, searchPosition - lineStart); + using size_type = std::string::size_type; + size_type searchPosition = std::min(m_source.size(), _position); + int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); + + size_type lineStart; + if (searchPosition == 0) { + lineStart = 0; + } else { + lineStart = m_source.rfind('\n', searchPosition - 1); + lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; + } + + return std::tuple(lineNumber, searchPosition - lineStart); } diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 7b1408c0b..4bc841111 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -60,37 +60,37 @@ class ParserRecorder; class CharStream { public: - CharStream() - : m_pos(0) - {} - - explicit CharStream(const std::string& _source) - : m_source(_source), m_pos(0) - {} - int getPos() const { return m_pos; } - bool isPastEndOfInput() const { return m_pos >= m_source.size(); } - char get() const { return m_source[m_pos]; } - char advanceAndGet() { - if (isPastEndOfInput()) return 0; - ++m_pos; - if (isPastEndOfInput()) return 0; - return get(); - } - char rollback(size_t _amount) { - BOOST_ASSERT(m_pos >= _amount); - m_pos -= _amount; - return get(); - } - - /// Functions that help pretty-printing parse errors - /// Do only use in error cases, they are quite expensive. - /// @{ - std::string getLineAtPosition(int _position) const; - std::tuple translatePositionToLineColumn(int _position) const; - /// @} + CharStream() + : m_pos(0) + {} + + explicit CharStream(const std::string& _source) + : m_source(_source), m_pos(0) + {} + int getPos() const { return m_pos; } + bool isPastEndOfInput() const { return m_pos >= m_source.size(); } + char get() const { return m_source[m_pos]; } + char advanceAndGet() { + if (isPastEndOfInput()) return 0; + ++m_pos; + if (isPastEndOfInput()) return 0; + return get(); + } + char rollback(size_t _amount) { + BOOST_ASSERT(m_pos >= _amount); + m_pos -= _amount; + return get(); + } + + /// Functions that help pretty-printing parse errors + /// Do only use in error cases, they are quite expensive. + /// @{ + std::string getLineAtPosition(int _position) const; + std::tuple translatePositionToLineColumn(int _position) const; + /// @} private: - std::string m_source; - size_t m_pos; + std::string m_source; + size_t m_pos; }; // ---------------------------------------------------------------------------- @@ -98,155 +98,155 @@ private: class Scanner { public: - // Scoped helper for literal recording. Automatically drops the literal - // if aborting the scanning before it's complete. - class LiteralScope { - public: - explicit LiteralScope(Scanner* self) - : scanner_(self), complete_(false) { - scanner_->startNewLiteral(); - } - ~LiteralScope() { - if (!complete_) scanner_->dropLiteral(); - } - void Complete() { - complete_ = true; - } - - private: - Scanner* scanner_; - bool complete_; - }; - - explicit Scanner(const CharStream& _source); - - // Resets the scanner as if newly constructed with _input as input. - void reset(const CharStream& _source); - - // Returns the next token and advances input. - Token::Value next(); - // Returns the current token again. - Token::Value getCurrentToken() { return m_current_token.token; } - // Returns the location information for the current token - // (the token last returned by Next()). - Location getCurrentLocation() const { return m_current_token.location; } - const std::string& getCurrentLiteral() const { return m_current_token.literal; } - - // Similar functions for the upcoming token. - - // One token look-ahead (past the token returned by Next()). - Token::Value peek() const { return m_next_token.token; } - - Location peekLocation() const { return m_next_token.location; } - const std::string& peekLiteral() const { return m_next_token.literal; } - - /// Functions that help pretty-printing parse errors. - /// Do only use in error cases, they are quite expensive. - /// @{ - std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } - std::tuple translatePositionToLineColumn(int _position) const - { - return m_source.translatePositionToLineColumn(_position); - } - /// @} - - // Returns true if there was a line terminator before the peek'ed token, - // possibly inside a multi-line comment. - bool hasAnyLineTerminatorBeforeNext() const { - return m_hasLineTerminatorBeforeNext || - m_hasMultilineCommentBeforeNext; - } + // Scoped helper for literal recording. Automatically drops the literal + // if aborting the scanning before it's complete. + class LiteralScope { + public: + explicit LiteralScope(Scanner* self) + : scanner_(self), complete_(false) { + scanner_->startNewLiteral(); + } + ~LiteralScope() { + if (!complete_) scanner_->dropLiteral(); + } + void Complete() { + complete_ = true; + } + + private: + Scanner* scanner_; + bool complete_; + }; + + explicit Scanner(const CharStream& _source); + + // Resets the scanner as if newly constructed with _input as input. + void reset(const CharStream& _source); + + // Returns the next token and advances input. + Token::Value next(); + // Returns the current token again. + Token::Value getCurrentToken() { return m_current_token.token; } + // Returns the location information for the current token + // (the token last returned by Next()). + Location getCurrentLocation() const { return m_current_token.location; } + const std::string& getCurrentLiteral() const { return m_current_token.literal; } + + // Similar functions for the upcoming token. + + // One token look-ahead (past the token returned by Next()). + Token::Value peek() const { return m_next_token.token; } + + Location peekLocation() const { return m_next_token.location; } + const std::string& peekLiteral() const { return m_next_token.literal; } + + /// Functions that help pretty-printing parse errors. + /// Do only use in error cases, they are quite expensive. + /// @{ + std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } + std::tuple translatePositionToLineColumn(int _position) const + { + return m_source.translatePositionToLineColumn(_position); + } + /// @} + + // Returns true if there was a line terminator before the peek'ed token, + // possibly inside a multi-line comment. + bool hasAnyLineTerminatorBeforeNext() const { + return m_hasLineTerminatorBeforeNext || + m_hasMultilineCommentBeforeNext; + } private: - // Used for the current and look-ahead token. - struct TokenDesc { - Token::Value token; - Location location; - std::string literal; - }; - - // Literal buffer support - inline void startNewLiteral() { - m_next_token.literal.clear(); - } - - inline void addLiteralChar(char c) { - m_next_token.literal.push_back(c); - } - - inline void dropLiteral() { - m_next_token.literal.clear(); - } - - inline void addLiteralCharAndAdvance() { - addLiteralChar(m_char); - advance(); - } - - // Low-level scanning support. - bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } - void rollback(int amount) { - m_char = m_source.rollback(amount); - } - - inline Token::Value selectToken(Token::Value tok) { - advance(); - return tok; - } - - inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) { - advance(); - if (m_char == next) { - advance(); - return then; - } else { - return else_; - } - } - - bool scanHexNumber(char& scanned_number, int expected_length); - - // Scans a single JavaScript token. - void scanToken(); - - bool skipWhitespace(); - Token::Value skipSingleLineComment(); - Token::Value skipMultiLineComment(); - - void scanDecimalDigits(); - Token::Value scanNumber(bool _periodSeen); - Token::Value scanIdentifierOrKeyword(); - - Token::Value scanString(); - - // Scans an escape-sequence which is part of a string and adds the - // decoded character to the current literal. Returns true if a pattern - // is scanned. - bool scanEscape(); - - // Return the current source position. - int getSourcePos() { - return m_source.getPos(); - } - bool isSourcePastEndOfInput() { - return m_source.isPastEndOfInput(); - } - - TokenDesc m_current_token; // desc for current token (as returned by Next()) - TokenDesc m_next_token; // desc for next token (one token look-ahead) - - CharStream m_source; - - // one character look-ahead, equals 0 at end of input - char m_char; - - // Whether there is a line terminator whitespace character after - // the current token, and before the next. Does not count newlines - // inside multiline comments. - bool m_hasLineTerminatorBeforeNext; - // Whether there is a multi-line comment that contains a - // line-terminator after the current token, and before the next. - bool m_hasMultilineCommentBeforeNext; + // Used for the current and look-ahead token. + struct TokenDesc { + Token::Value token; + Location location; + std::string literal; + }; + + // Literal buffer support + inline void startNewLiteral() { + m_next_token.literal.clear(); + } + + inline void addLiteralChar(char c) { + m_next_token.literal.push_back(c); + } + + inline void dropLiteral() { + m_next_token.literal.clear(); + } + + inline void addLiteralCharAndAdvance() { + addLiteralChar(m_char); + advance(); + } + + // Low-level scanning support. + bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } + void rollback(int amount) { + m_char = m_source.rollback(amount); + } + + inline Token::Value selectToken(Token::Value tok) { + advance(); + return tok; + } + + inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) { + advance(); + if (m_char == next) { + advance(); + return then; + } else { + return else_; + } + } + + bool scanHexNumber(char& scanned_number, int expected_length); + + // Scans a single JavaScript token. + void scanToken(); + + bool skipWhitespace(); + Token::Value skipSingleLineComment(); + Token::Value skipMultiLineComment(); + + void scanDecimalDigits(); + Token::Value scanNumber(bool _periodSeen); + Token::Value scanIdentifierOrKeyword(); + + Token::Value scanString(); + + // Scans an escape-sequence which is part of a string and adds the + // decoded character to the current literal. Returns true if a pattern + // is scanned. + bool scanEscape(); + + // Return the current source position. + int getSourcePos() { + return m_source.getPos(); + } + bool isSourcePastEndOfInput() { + return m_source.isPastEndOfInput(); + } + + TokenDesc m_current_token; // desc for current token (as returned by Next()) + TokenDesc m_next_token; // desc for next token (one token look-ahead) + + CharStream m_source; + + // one character look-ahead, equals 0 at end of input + char m_char; + + // Whether there is a line terminator whitespace character after + // the current token, and before the next. Does not count newlines + // inside multiline comments. + bool m_hasLineTerminatorBeforeNext; + // Whether there is a multi-line comment that contains a + // line-terminator after the current token, and before the next. + bool m_hasMultilineCommentBeforeNext; }; } } diff --git a/libsolidity/Token.cpp b/libsolidity/Token.cpp index 6ae6456a9..0264f7e85 100644 --- a/libsolidity/Token.cpp +++ b/libsolidity/Token.cpp @@ -47,21 +47,21 @@ namespace solidity { #define T(name, string, precedence) #name, const char* const Token::m_name[NUM_TOKENS] = { - TOKEN_LIST(T, T) + TOKEN_LIST(T, T) }; #undef T #define T(name, string, precedence) string, const char* const Token::m_string[NUM_TOKENS] = { - TOKEN_LIST(T, T) + TOKEN_LIST(T, T) }; #undef T #define T(name, string, precedence) precedence, const int8_t Token::m_precedence[NUM_TOKENS] = { - TOKEN_LIST(T, T) + TOKEN_LIST(T, T) }; #undef T @@ -69,7 +69,7 @@ const int8_t Token::m_precedence[NUM_TOKENS] = { #define KT(a, b, c) 'T', #define KK(a, b, c) 'K', const char Token::m_tokenType[] = { - TOKEN_LIST(KT, KK) + TOKEN_LIST(KT, KK) }; #undef KT #undef KK diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 4476b3837..2ff5067bc 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -65,309 +65,309 @@ namespace solidity { #define IGNORE_TOKEN(name, string, precedence) -#define TOKEN_LIST(T, K) \ - /* End of source indicator. */ \ - T(EOS, "EOS", 0) \ - \ - /* Punctuators (ECMA-262, section 7.7, page 15). */ \ - T(LPAREN, "(", 0) \ - T(RPAREN, ")", 0) \ - T(LBRACK, "[", 0) \ - T(RBRACK, "]", 0) \ - T(LBRACE, "{", 0) \ - T(RBRACE, "}", 0) \ - T(COLON, ":", 0) \ - T(SEMICOLON, ";", 0) \ - T(PERIOD, ".", 0) \ - T(CONDITIONAL, "?", 3) \ - T(INC, "++", 0) \ - T(DEC, "--", 0) \ - T(ARROW, "=>", 0) \ - \ - /* Assignment operators. */ \ - /* IsAssignmentOp() and Assignment::is_compound() relies on */ \ - /* this block of enum values being contiguous and sorted in the */ \ - /* same order! */ \ - T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \ - T(INIT_LET, "=init_let", 2) /* AST-use only. */ \ - T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ - T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \ - T(ASSIGN, "=", 2) \ - T(ASSIGN_BIT_OR, "|=", 2) \ - T(ASSIGN_BIT_XOR, "^=", 2) \ - T(ASSIGN_BIT_AND, "&=", 2) \ - T(ASSIGN_SHL, "<<=", 2) \ - T(ASSIGN_SAR, ">>=", 2) \ - T(ASSIGN_SHR, ">>>=", 2) \ - T(ASSIGN_ADD, "+=", 2) \ - T(ASSIGN_SUB, "-=", 2) \ - T(ASSIGN_MUL, "*=", 2) \ - T(ASSIGN_DIV, "/=", 2) \ - T(ASSIGN_MOD, "%=", 2) \ - \ - /* Binary operators sorted by precedence. */ \ - /* IsBinaryOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(COMMA, ",", 1) \ - T(OR, "||", 4) \ - T(AND, "&&", 5) \ - T(BIT_OR, "|", 6) \ - T(BIT_XOR, "^", 7) \ - T(BIT_AND, "&", 8) \ - T(SHL, "<<", 11) \ - T(SAR, ">>", 11) \ - T(SHR, ">>>", 11) \ - T(ROR, "rotate right", 11) /* only used by Crankshaft */ \ - T(ADD, "+", 12) \ - T(SUB, "-", 12) \ - T(MUL, "*", 13) \ - T(DIV, "/", 13) \ - T(MOD, "%", 13) \ - \ - /* Compare operators sorted by precedence. */ \ - /* IsCompareOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(EQ, "==", 9) \ - T(NE, "!=", 9) \ - T(EQ_STRICT, "===", 9) \ - T(NE_STRICT, "!==", 9) \ - T(LT, "<", 10) \ - T(GT, ">", 10) \ - T(LTE, "<=", 10) \ - T(GTE, ">=", 10) \ - K(INSTANCEOF, "instanceof", 10) \ - K(IN, "in", 10) \ - \ - /* Unary operators. */ \ - /* IsUnaryOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(NOT, "!", 0) \ - T(BIT_NOT, "~", 0) \ - K(DELETE, "delete", 0) \ - K(TYPEOF, "typeof", 0) \ - K(VOID, "void", 0) \ - \ - /* Keywords (ECMA-262, section 7.5.2, page 13). */ \ - K(BREAK, "break", 0) \ - K(CASE, "case", 0) \ - K(CATCH, "catch", 0) \ - K(CONTINUE, "continue", 0) \ - K(CONTRACT, "contract", 0) \ - K(DEBUGGER, "debugger", 0) \ - K(DEFAULT, "default", 0) \ - /* DELETE */ \ - K(DO, "do", 0) \ - K(ELSE, "else", 0) \ - K(FINALLY, "finally", 0) \ - K(FOR, "for", 0) \ - K(FUNCTION, "function", 0) \ - K(IF, "if", 0) \ - /* IN */ \ - /* INSTANCEOF */ \ - K(MAPPING, "mapping", 0) \ - K(NEW, "new", 0) \ - K(PUBLIC, "public", 0) \ - K(PRIVATE, "private", 0) \ - K(RETURN, "return", 0) \ - K(RETURNS, "returns", 0) \ - K(STRUCT, "struct", 0) \ - K(SWITCH, "switch", 0) \ - K(THIS, "this", 0) \ - K(THROW, "throw", 0) \ - K(TRY, "try", 0) \ - /* TYPEOF */ \ - K(VAR, "var", 0) \ - /* VOID */ \ - K(WHILE, "while", 0) \ - K(WITH, "with", 0) \ - \ - /* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \ - K(INT, "int", 0) \ - K(INT32, "int32", 0) \ - K(INT64, "int64", 0) \ - K(INT128, "int128", 0) \ - K(INT256, "int256", 0) \ - K(UINT, "uint", 0) \ - K(UINT32, "uint32", 0) \ - K(UINT64, "uint64", 0) \ - K(UINT128, "uint128", 0) \ - K(UINT256, "uint256", 0) \ - K(HASH, "hash", 0) \ - K(HASH32, "hash32", 0) \ - K(HASH64, "hash64", 0) \ - K(HASH128, "hash128", 0) \ - K(HASH256, "hash256", 0) \ - K(ADDRESS, "address", 0) \ - K(BOOL, "bool", 0) \ - K(STRING_TYPE, "string", 0) \ - K(TEXT, "text", 0) \ - K(REAL, "real", 0) \ - K(UREAL, "ureal", 0) \ - T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ - \ - /* Literals (ECMA-262, section 7.8, page 16). */ \ - K(NULL_LITERAL, "null", 0) \ - K(TRUE_LITERAL, "true", 0) \ - K(FALSE_LITERAL, "false", 0) \ - T(NUMBER, NULL, 0) \ - T(STRING_LITERAL, NULL, 0) \ - \ - /* Identifiers (not keywords or future reserved words). */ \ - T(IDENTIFIER, NULL, 0) \ - \ - /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ - T(FUTURE_RESERVED_WORD, NULL, 0) \ - T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ - K(CLASS, "class", 0) \ - K(CONST, "const", 0) \ - K(EXPORT, "export", 0) \ - K(EXTENDS, "extends", 0) \ - K(IMPORT, "import", 0) \ - K(LET, "let", 0) \ - K(STATIC, "static", 0) \ -/* K(YIELD, "yield", 0) */ \ - K(SUPER, "super", 0) \ - \ - /* Illegal token - not able to scan. */ \ - T(ILLEGAL, "ILLEGAL", 0) \ - \ - /* Scanner-internal use only. */ \ - T(WHITESPACE, NULL, 0) +#define TOKEN_LIST(T, K) \ + /* End of source indicator. */ \ + T(EOS, "EOS", 0) \ + \ + /* Punctuators (ECMA-262, section 7.7, page 15). */ \ + T(LPAREN, "(", 0) \ + T(RPAREN, ")", 0) \ + T(LBRACK, "[", 0) \ + T(RBRACK, "]", 0) \ + T(LBRACE, "{", 0) \ + T(RBRACE, "}", 0) \ + T(COLON, ":", 0) \ + T(SEMICOLON, ";", 0) \ + T(PERIOD, ".", 0) \ + T(CONDITIONAL, "?", 3) \ + T(INC, "++", 0) \ + T(DEC, "--", 0) \ + T(ARROW, "=>", 0) \ + \ + /* Assignment operators. */ \ + /* IsAssignmentOp() and Assignment::is_compound() relies on */ \ + /* this block of enum values being contiguous and sorted in the */ \ + /* same order! */ \ + T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \ + T(INIT_LET, "=init_let", 2) /* AST-use only. */ \ + T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ + T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \ + T(ASSIGN, "=", 2) \ + T(ASSIGN_BIT_OR, "|=", 2) \ + T(ASSIGN_BIT_XOR, "^=", 2) \ + T(ASSIGN_BIT_AND, "&=", 2) \ + T(ASSIGN_SHL, "<<=", 2) \ + T(ASSIGN_SAR, ">>=", 2) \ + T(ASSIGN_SHR, ">>>=", 2) \ + T(ASSIGN_ADD, "+=", 2) \ + T(ASSIGN_SUB, "-=", 2) \ + T(ASSIGN_MUL, "*=", 2) \ + T(ASSIGN_DIV, "/=", 2) \ + T(ASSIGN_MOD, "%=", 2) \ + \ + /* Binary operators sorted by precedence. */ \ + /* IsBinaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(COMMA, ",", 1) \ + T(OR, "||", 4) \ + T(AND, "&&", 5) \ + T(BIT_OR, "|", 6) \ + T(BIT_XOR, "^", 7) \ + T(BIT_AND, "&", 8) \ + T(SHL, "<<", 11) \ + T(SAR, ">>", 11) \ + T(SHR, ">>>", 11) \ + T(ROR, "rotate right", 11) /* only used by Crankshaft */ \ + T(ADD, "+", 12) \ + T(SUB, "-", 12) \ + T(MUL, "*", 13) \ + T(DIV, "/", 13) \ + T(MOD, "%", 13) \ + \ + /* Compare operators sorted by precedence. */ \ + /* IsCompareOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(EQ, "==", 9) \ + T(NE, "!=", 9) \ + T(EQ_STRICT, "===", 9) \ + T(NE_STRICT, "!==", 9) \ + T(LT, "<", 10) \ + T(GT, ">", 10) \ + T(LTE, "<=", 10) \ + T(GTE, ">=", 10) \ + K(INSTANCEOF, "instanceof", 10) \ + K(IN, "in", 10) \ + \ + /* Unary operators. */ \ + /* IsUnaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ + T(NOT, "!", 0) \ + T(BIT_NOT, "~", 0) \ + K(DELETE, "delete", 0) \ + K(TYPEOF, "typeof", 0) \ + K(VOID, "void", 0) \ + \ + /* Keywords (ECMA-262, section 7.5.2, page 13). */ \ + K(BREAK, "break", 0) \ + K(CASE, "case", 0) \ + K(CATCH, "catch", 0) \ + K(CONTINUE, "continue", 0) \ + K(CONTRACT, "contract", 0) \ + K(DEBUGGER, "debugger", 0) \ + K(DEFAULT, "default", 0) \ + /* DELETE */ \ + K(DO, "do", 0) \ + K(ELSE, "else", 0) \ + K(FINALLY, "finally", 0) \ + K(FOR, "for", 0) \ + K(FUNCTION, "function", 0) \ + K(IF, "if", 0) \ + /* IN */ \ + /* INSTANCEOF */ \ + K(MAPPING, "mapping", 0) \ + K(NEW, "new", 0) \ + K(PUBLIC, "public", 0) \ + K(PRIVATE, "private", 0) \ + K(RETURN, "return", 0) \ + K(RETURNS, "returns", 0) \ + K(STRUCT, "struct", 0) \ + K(SWITCH, "switch", 0) \ + K(THIS, "this", 0) \ + K(THROW, "throw", 0) \ + K(TRY, "try", 0) \ + /* TYPEOF */ \ + K(VAR, "var", 0) \ + /* VOID */ \ + K(WHILE, "while", 0) \ + K(WITH, "with", 0) \ + \ + /* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \ + K(INT, "int", 0) \ + K(INT32, "int32", 0) \ + K(INT64, "int64", 0) \ + K(INT128, "int128", 0) \ + K(INT256, "int256", 0) \ + K(UINT, "uint", 0) \ + K(UINT32, "uint32", 0) \ + K(UINT64, "uint64", 0) \ + K(UINT128, "uint128", 0) \ + K(UINT256, "uint256", 0) \ + K(HASH, "hash", 0) \ + K(HASH32, "hash32", 0) \ + K(HASH64, "hash64", 0) \ + K(HASH128, "hash128", 0) \ + K(HASH256, "hash256", 0) \ + K(ADDRESS, "address", 0) \ + K(BOOL, "bool", 0) \ + K(STRING_TYPE, "string", 0) \ + K(TEXT, "text", 0) \ + K(REAL, "real", 0) \ + K(UREAL, "ureal", 0) \ + T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ + \ + /* Literals (ECMA-262, section 7.8, page 16). */ \ + K(NULL_LITERAL, "null", 0) \ + K(TRUE_LITERAL, "true", 0) \ + K(FALSE_LITERAL, "false", 0) \ + T(NUMBER, NULL, 0) \ + T(STRING_LITERAL, NULL, 0) \ + \ + /* Identifiers (not keywords or future reserved words). */ \ + T(IDENTIFIER, NULL, 0) \ + \ + /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ + T(FUTURE_RESERVED_WORD, NULL, 0) \ + T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ + K(CLASS, "class", 0) \ + K(CONST, "const", 0) \ + K(EXPORT, "export", 0) \ + K(EXTENDS, "extends", 0) \ + K(IMPORT, "import", 0) \ + K(LET, "let", 0) \ + K(STATIC, "static", 0) \ +/* K(YIELD, "yield", 0) */ \ + K(SUPER, "super", 0) \ + \ + /* Illegal token - not able to scan. */ \ + T(ILLEGAL, "ILLEGAL", 0) \ + \ + /* Scanner-internal use only. */ \ + T(WHITESPACE, NULL, 0) class Token { - public: - // All token values. +public: + // All token values. #define T(name, string, precedence) name, - enum Value { - TOKEN_LIST(T, T) - NUM_TOKENS - }; + enum Value { + TOKEN_LIST(T, T) + NUM_TOKENS + }; #undef T - // Returns a string corresponding to the C++ token name - // (e.g. "LT" for the token LT). - static const char* Name(Value tok) { - BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned - return m_name[tok]; - } - - // Predicates - static bool IsKeyword(Value tok) { - return m_tokenType[tok] == 'K'; - } - - static bool IsIdentifier(Value tok) { - return tok == IDENTIFIER; - } - - static bool IsElementaryTypeName(Value tok) { - return INT <= tok && tok < TYPES_END; - } - - static bool IsAssignmentOp(Value tok) { - return INIT_VAR <= tok && tok <= ASSIGN_MOD; - } - - static bool IsBinaryOp(Value op) { - return COMMA <= op && op <= MOD; - } - - static bool IsTruncatingBinaryOp(Value op) { - return BIT_OR <= op && op <= ROR; - } - - static bool IsCompareOp(Value op) { - return EQ <= op && op <= IN; - } - - static bool IsOrderedRelationalCompareOp(Value op) { - return op == LT || op == LTE || op == GT || op == GTE; - } - - static bool IsEqualityOp(Value op) { - return op == EQ || op == EQ_STRICT; - } - - static bool IsInequalityOp(Value op) { - return op == NE || op == NE_STRICT; - } - - static bool IsArithmeticCompareOp(Value op) { - return IsOrderedRelationalCompareOp(op) || - IsEqualityOp(op) || IsInequalityOp(op); - } - - static Value NegateCompareOp(Value op) { - BOOST_ASSERT(IsArithmeticCompareOp(op)); - switch (op) { - case EQ: return NE; - case NE: return EQ; - case EQ_STRICT: return NE_STRICT; - case NE_STRICT: return EQ_STRICT; - case LT: return GTE; - case GT: return LTE; - case LTE: return GT; - case GTE: return LT; - default: - BOOST_ASSERT(false); // should not get here - return op; - } - } - - static Value ReverseCompareOp(Value op) { - BOOST_ASSERT(IsArithmeticCompareOp(op)); - switch (op) { - case EQ: return EQ; - case NE: return NE; - case EQ_STRICT: return EQ_STRICT; - case NE_STRICT: return NE_STRICT; - case LT: return GT; - case GT: return LT; - case LTE: return GTE; - case GTE: return LTE; - default: - BOOST_ASSERT(false); // should not get here - return op; - } - } - - static bool IsBitOp(Value op) { - return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; - } - - static bool IsUnaryOp(Value op) { - return (NOT <= op && op <= VOID) || op == ADD || op == SUB; - } - - static bool IsCountOp(Value op) { - return op == INC || op == DEC; - } - - static bool IsShiftOp(Value op) { - return (SHL <= op) && (op <= SHR); - } - - // Returns a string corresponding to the JS token string - // (.e., "<" for the token LT) or NULL if the token doesn't - // have a (unique) string (e.g. an IDENTIFIER). - static const char* String(Value tok) { - BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. - return m_string[tok]; - } - - // Returns the precedence > 0 for binary and compare - // operators; returns 0 otherwise. - static int Precedence(Value tok) { - BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. - return m_precedence[tok]; - } - - private: - static const char* const m_name[NUM_TOKENS]; - static const char* const m_string[NUM_TOKENS]; - static const int8_t m_precedence[NUM_TOKENS]; - static const char m_tokenType[NUM_TOKENS]; + // Returns a string corresponding to the C++ token name + // (e.g. "LT" for the token LT). + static const char* Name(Value tok) { + BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned + return m_name[tok]; + } + + // Predicates + static bool IsKeyword(Value tok) { + return m_tokenType[tok] == 'K'; + } + + static bool IsIdentifier(Value tok) { + return tok == IDENTIFIER; + } + + static bool IsElementaryTypeName(Value tok) { + return INT <= tok && tok < TYPES_END; + } + + static bool IsAssignmentOp(Value tok) { + return INIT_VAR <= tok && tok <= ASSIGN_MOD; + } + + static bool IsBinaryOp(Value op) { + return COMMA <= op && op <= MOD; + } + + static bool IsTruncatingBinaryOp(Value op) { + return BIT_OR <= op && op <= ROR; + } + + static bool IsCompareOp(Value op) { + return EQ <= op && op <= IN; + } + + static bool IsOrderedRelationalCompareOp(Value op) { + return op == LT || op == LTE || op == GT || op == GTE; + } + + static bool IsEqualityOp(Value op) { + return op == EQ || op == EQ_STRICT; + } + + static bool IsInequalityOp(Value op) { + return op == NE || op == NE_STRICT; + } + + static bool IsArithmeticCompareOp(Value op) { + return IsOrderedRelationalCompareOp(op) || + IsEqualityOp(op) || IsInequalityOp(op); + } + + static Value NegateCompareOp(Value op) { + BOOST_ASSERT(IsArithmeticCompareOp(op)); + switch (op) { + case EQ: return NE; + case NE: return EQ; + case EQ_STRICT: return NE_STRICT; + case NE_STRICT: return EQ_STRICT; + case LT: return GTE; + case GT: return LTE; + case LTE: return GT; + case GTE: return LT; + default: + BOOST_ASSERT(false); // should not get here + return op; + } + } + + static Value ReverseCompareOp(Value op) { + BOOST_ASSERT(IsArithmeticCompareOp(op)); + switch (op) { + case EQ: return EQ; + case NE: return NE; + case EQ_STRICT: return EQ_STRICT; + case NE_STRICT: return NE_STRICT; + case LT: return GT; + case GT: return LT; + case LTE: return GTE; + case GTE: return LTE; + default: + BOOST_ASSERT(false); // should not get here + return op; + } + } + + static bool IsBitOp(Value op) { + return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; + } + + static bool IsUnaryOp(Value op) { + return (NOT <= op && op <= VOID) || op == ADD || op == SUB; + } + + static bool IsCountOp(Value op) { + return op == INC || op == DEC; + } + + static bool IsShiftOp(Value op) { + return (SHL <= op) && (op <= SHR); + } + + // Returns a string corresponding to the JS token string + // (.e., "<" for the token LT) or NULL if the token doesn't + // have a (unique) string (e.g. an IDENTIFIER). + static const char* String(Value tok) { + BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. + return m_string[tok]; + } + + // Returns the precedence > 0 for binary and compare + // operators; returns 0 otherwise. + static int Precedence(Value tok) { + BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. + return m_precedence[tok]; + } + +private: + static const char* const m_name[NUM_TOKENS]; + static const char* const m_string[NUM_TOKENS]; + static const int8_t m_precedence[NUM_TOKENS]; + static const char m_tokenType[NUM_TOKENS]; }; } } From f070d3bdea93d11dfa1dece3d15acc501e060109 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 9 Oct 2014 15:57:49 +0200 Subject: [PATCH 05/34] Initial implementation of Solidity parser finished, not yet tested much. --- libsolidity/AST.h | 110 +++++++++++++++---- libsolidity/Parser.cpp | 233 +++++++++++++++++++++++++++++++++++++++- libsolidity/Parser.h | 10 ++ libsolidity/grammar.txt | 13 ++- test/solidityParser.cpp | 78 ++++++++++++++ 5 files changed, 416 insertions(+), 28 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index dad257f23..9a4b95215 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -54,6 +54,8 @@ public: : m_location(_location) {} + virtual ~ASTNode() {} + Location getLocation() const { return m_location; } private: Location m_location; @@ -146,9 +148,7 @@ private: class TypeName : public ASTNode { public: - explicit TypeName(Location const& _location) - : ASTNode(_location) - {} + explicit TypeName(Location const& _location) : ASTNode(_location) {} }; /// any pre-defined type that is not a mapping @@ -192,15 +192,13 @@ private: class Statement : public ASTNode { public: - explicit Statement(Location const& _location) - : ASTNode(_location) - {} + explicit Statement(Location const& _location) : ASTNode(_location) {} }; class Block : public Statement { public: - explicit Block(Location const& _location, vecptr const& _statements) + Block(Location const& _location, vecptr const& _statements) : Statement(_location), m_statements(_statements) {} private: @@ -209,7 +207,12 @@ private: class IfStatement : public Statement { - +public: + IfStatement(Location const& _location, ptr const& _condition, + ptr const& _trueBody, ptr const& _falseBody) + : Statement(_location), m_condition(_condition), + m_trueBody(_trueBody), m_falseBody(_falseBody) + {} private: ptr m_condition; ptr m_trueBody; @@ -218,11 +221,17 @@ private: class BreakableStatement : public Statement { - +public: + BreakableStatement(Location const& _location) : Statement(_location) {} }; class WhileStatement : public BreakableStatement { +public: + WhileStatement(Location const& _location, ptr const& _condition, + ptr const& _body) + : BreakableStatement(_location), m_condition(_condition), m_body(_body) + {} private: ptr m_condition; ptr m_body; @@ -230,31 +239,42 @@ private: class Continue : public Statement { - +public: + Continue(Location const& _location) : Statement(_location) {} }; class Break : public Statement { - +public: + Break(Location const& _location) : Statement(_location) {} }; class Return : public Statement { +public: + Return(Location const& _location, ptr _expression) + : Statement(_location), m_expression(_expression) + {} private: ptr m_expression; }; -class VariableAssignment : public Statement +class VariableDefinition : public Statement { +public: + VariableDefinition(Location const& _location, ptr _variable, + ptr _value) + : Statement(_location), m_variable(_variable), m_value(_value) + {} private: ptr m_variable; - Token::Value m_assigmentOperator; - ptr m_rightHandSide; ///< can be missing + ptr m_value; ///< can be missing }; class Expression : public Statement { -private: +public: + Expression(Location const& _location) : Statement(_location) {} }; /// @} @@ -264,6 +284,12 @@ private: class Assignment : public Expression { +public: + Assignment(Location const& _location, ptr const& _leftHandSide, + Token::Value _assignmentOperator, ptr const& _rightHandSide) + : Expression(_location), m_leftHandSide(_leftHandSide), + m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) + {} private: ptr m_leftHandSide; Token::Value m_assigmentOperator; @@ -272,31 +298,52 @@ private: class UnaryOperation : public Expression { +public: + UnaryOperation(Location const& _location, Token::Value _operator, + ptr const& _subExpression, bool _isPrefix) + : Expression(_location), m_operator(_operator), + m_subExpression(_subExpression), m_isPrefix(_isPrefix) + {} + private: Token::Value m_operator; ptr m_subExpression; - bool isPrefix; + bool m_isPrefix; }; class BinaryOperation : public Expression { +public: + BinaryOperation(Location const& _location, ptr const& _left, + Token::Value _operator, ptr const& _right) + : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) + {} private: ptr m_left; - ptr m_right; Token::Value m_operator; + ptr m_right; }; /// Can be ordinary function call, type cast or struct construction. class FunctionCall : public Expression { +public: + FunctionCall(Location const& _location, ptr const& _expression, + vecptr const& _arguments) + : Expression(_location), m_expression(_expression), m_arguments(_arguments) + {} private: - // if m_functionName is the name of a type, store the token directly - std::string m_functionName; // "in place" calls of return values are not possible for now + ptr m_expression; vecptr m_arguments; }; class MemberAccess : public Expression { +public: + MemberAccess(Location const& _location, ptr _expression, + std::string const& _memberName) + : Expression(_location), m_expression(_expression), m_memberName(_memberName) + {} private: ptr m_expression; std::string m_memberName; @@ -304,23 +351,48 @@ private: class IndexAccess : public Expression { +public: + IndexAccess(Location const& _location, ptr const& _base, + ptr const& _index) + : Expression(_location), m_base(_base), m_index(_index) + {} +private: ptr m_base; ptr m_index; }; class PrimaryExpression : public Expression { +public: + PrimaryExpression(Location const& _location) : Expression(_location) {} }; class Identifier : public PrimaryExpression { +public: + Identifier(Location const& _location, std::string const& _name) + : PrimaryExpression(_location), m_name(_name) {} private: std::string m_name; }; +class ElementaryTypeNameExpression : public PrimaryExpression +{ +public: + ElementaryTypeNameExpression(Location const& _location, Token::Value _type) + : PrimaryExpression(_location), m_type(_type) {} +private: + Token::Value m_type; +}; + class Literal : public PrimaryExpression { +public: + Literal(Location const& _location, Token::Value _token, std::string const& _value) + : PrimaryExpression(_location), m_token(_token), m_value(_value) + {} private: + Token::Value m_token; std::string m_value; }; diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 2886b2c18..24c8e599e 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -231,12 +231,10 @@ ptr Parser::parseParameterList() ptr Parser::parseBlock() { - ASTNodeFactory nodeFactory(*this); expectToken(Token::LBRACE); vecptr statements; while (m_scanner->getCurrentToken() != Token::RBRACE) { - m_scanner->next(); statements.push_back(parseStatement()); } nodeFactory.markEndPosition(); @@ -246,6 +244,7 @@ ptr Parser::parseBlock() ptr Parser::parseStatement() { + ptr statement; switch (m_scanner->getCurrentToken()) { case Token::IF: @@ -254,12 +253,229 @@ ptr Parser::parseStatement() return parseWhileStatement(); case Token::LBRACE: return parseBlock(); + // starting from here, all statements must be terminated by a semicolon - case Token::CONTINUE: // all following - return + case Token::CONTINUE: + statement = ASTNodeFactory(*this).createNode(); + break; + case Token::BREAK: + statement = ASTNodeFactory(*this).createNode(); + break; + case Token::RETURN: + { + ASTNodeFactory nodeFactory(*this); + ptr expression; + if (m_scanner->next() != Token::SEMICOLON) { + expression = parseExpression(); + nodeFactory.setEndPositionFromNode(expression); + } + statement = nodeFactory.createNode(expression); + } + break; + default: + // distinguish between variable definition (and potentially assignment) and expressions + // (which include assignments to other expressions and pre-declared variables) + // We have a variable definition if we ge a keyword that specifies a type name, or + // in the case of a user-defined type, we have two identifiers following each other. + if (m_scanner->getCurrentToken() == Token::MAPPING || + m_scanner->getCurrentToken() == Token::VAR || + Token::IsElementaryTypeName(m_scanner->getCurrentToken()) || + (m_scanner->getCurrentToken() == Token::IDENTIFIER && + m_scanner->peek() == Token::IDENTIFIER)) { + statement = parseVariableDefinition(); + } else { + // "ordinary" expression + statement = parseExpression(); + } + } + expectToken(Token::SEMICOLON); + return statement; +} + +ptr Parser::parseIfStatement() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::IF); + expectToken(Token::LPAREN); + ptr condition = parseExpression(); + expectToken(Token::RPAREN); + ptr trueBody = parseStatement(); + ptr falseBody; + if (m_scanner->getCurrentToken() == Token::ELSE) { + m_scanner->next(); + falseBody = parseStatement(); + nodeFactory.setEndPositionFromNode(falseBody); + } else { + nodeFactory.setEndPositionFromNode(trueBody); + } + return nodeFactory.createNode(condition, trueBody, falseBody); +} + +ptr Parser::parseWhileStatement() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::WHILE); + expectToken(Token::LPAREN); + ptr condition = parseExpression(); + expectToken(Token::RPAREN); + ptr body = parseStatement(); + nodeFactory.setEndPositionFromNode(body); + return nodeFactory.createNode(condition, body); +} + +ptr Parser::parseVariableDefinition() +{ + ASTNodeFactory nodeFactory(*this); + ptr variable = parseVariableDeclaration(); + ptr value; + if (m_scanner->getCurrentToken() == Token::ASSIGN) { + m_scanner->next(); + value = parseExpression(); + nodeFactory.setEndPositionFromNode(value); + } else { + nodeFactory.setEndPositionFromNode(variable); + } + return nodeFactory.createNode(variable, value); +} + +ptr Parser::parseExpression() +{ + ASTNodeFactory nodeFactory(*this); + ptr expression = parseBinaryExpression(); + if (!Token::IsAssignmentOp(m_scanner->getCurrentToken())) + return expression; + + Token::Value assignmentOperator = expectAssignmentOperator(); + ptr rightHandSide = parseExpression(); + nodeFactory.setEndPositionFromNode(rightHandSide); + return nodeFactory.createNode(expression, assignmentOperator, rightHandSide); +} + +ptr Parser::parseBinaryExpression(int _minPrecedence) +{ + ASTNodeFactory nodeFactory(*this); + ptr expression = parseUnaryExpression(); + int precedence = Token::Precedence(m_scanner->getCurrentToken()); + for (; precedence >= _minPrecedence; --precedence) { + while (Token::Precedence(m_scanner->getCurrentToken()) == precedence) { + Token::Value op = m_scanner->getCurrentToken(); + m_scanner->next(); + ptr right = parseBinaryExpression(precedence + 1); + nodeFactory.setEndPositionFromNode(right); + expression = nodeFactory.createNode(expression, op, right); + } + } + return expression; +} + +ptr Parser::parseUnaryExpression() +{ + ASTNodeFactory nodeFactory(*this); + Token::Value token = m_scanner->getCurrentToken(); + if (Token::IsUnaryOp(token) || Token::IsCountOp(token)) { + // prefix expression + m_scanner->next(); + ptr subExpression = parseUnaryExpression(); + nodeFactory.setEndPositionFromNode(subExpression); + return nodeFactory.createNode(token, subExpression, true); + } else { + // potential postfix expression + ptr subExpression = parseLeftHandSideExpression(); + token = m_scanner->getCurrentToken(); + if (!Token::IsCountOp(token)) + return subExpression; + nodeFactory.markEndPosition(); + m_scanner->next(); + return nodeFactory.createNode(token, subExpression, false); + } +} + +ptr Parser::parseLeftHandSideExpression() +{ + ASTNodeFactory nodeFactory(*this); + ptr expression = parsePrimaryExpression(); + + while (true) { + switch (m_scanner->getCurrentToken()) { + case Token::LBRACK: + { + m_scanner->next(); + ptr index = parseExpression(); + nodeFactory.markEndPosition(); + expectToken(Token::RBRACK); + expression = nodeFactory.createNode(expression, index); + } + break; + case Token::PERIOD: + { + m_scanner->next(); + nodeFactory.markEndPosition(); + std::string memberName = expectIdentifier(); + expression = nodeFactory.createNode(expression, memberName); + } + break; + case Token::LPAREN: + { + m_scanner->next(); + vecptr arguments = parseFunctionCallArguments(); + nodeFactory.markEndPosition(); + expectToken(Token::RPAREN); + expression = nodeFactory.createNode(expression, arguments); + } + break; + default: + return expression; + } + } +} + +ptr Parser::parsePrimaryExpression() +{ + Token::Value token = m_scanner->getCurrentToken(); + switch (token) { + case Token::TRUE_LITERAL: + case Token::FALSE_LITERAL: + m_scanner->next(); + return ASTNodeFactory(*this).createNode(token, std::string()); + case Token::NUMBER: + case Token::STRING_LITERAL: + m_scanner->next(); + return ASTNodeFactory(*this).createNode(token, m_scanner->getCurrentLiteral()); + case Token::IDENTIFIER: + m_scanner->next(); + return ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); + case Token::LPAREN: + { + m_scanner->next(); + ptr expression = parseExpression(); + expectToken(Token::RPAREN); + return expression; + } + default: + if (Token::IsElementaryTypeName(token)) { + // used for casts + m_scanner->next(); + return ASTNodeFactory(*this).createNode(token); + } else { + throwExpectationError("Expected primary expression."); + return ptr(); // this is not reached + } } } +vecptr Parser::parseFunctionCallArguments() +{ + vecptr arguments; + if (m_scanner->getCurrentToken() != Token::RPAREN) { + arguments.push_back(parseExpression()); + while (m_scanner->getCurrentToken() != Token::RPAREN) { + expectToken(Token::COMMA); + arguments.push_back(parseExpression()); + } + } + return arguments; +} + void Parser::expectToken(Token::Value _value) { if (m_scanner->getCurrentToken() != _value) @@ -267,6 +483,15 @@ void Parser::expectToken(Token::Value _value) m_scanner->next(); } +Token::Value Parser::expectAssignmentOperator() +{ + Token::Value op = m_scanner->getCurrentToken(); + if (!Token::IsAssignmentOp(op)) + throwExpectationError(std::string("Expected assignment operator")); + m_scanner->next(); + return op; +} + std::string Parser::expectIdentifier() { if (m_scanner->getCurrentToken() != Token::IDENTIFIER) diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 65409a296..7036c3c20 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -53,12 +53,22 @@ private: ptr parseParameterList(); ptr parseBlock(); ptr parseStatement(); + ptr parseIfStatement(); + ptr parseWhileStatement(); + ptr parseVariableDefinition(); + ptr parseExpression(); + ptr parseBinaryExpression(int _minPrecedence = 4); + ptr parseUnaryExpression(); + ptr parseLeftHandSideExpression(); + ptr parsePrimaryExpression(); + vecptr parseFunctionCallArguments(); /// @} /// Helper functions /// @{ /// If current token value is not _value, throw exception otherwise advance token. void expectToken(Token::Value _value); + Token::Value expectAssignmentOperator(); std::string expectIdentifier(); void throwExpectationError(const std::string& _description); /// @} diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 1946325f4..c0ab06074 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -15,18 +15,21 @@ TypeName = ElementaryTypeName | Identifier | Mapping Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' Block = '{' Statement* '}' -Statement = IfStatement | WhileStatement | Continue | Break | Return | VariableAssignment | Expression ';' | Block +Statement = IfStatement | WhileStatement | Block | + ( Continue | Break | Return | VariableDefinition | Expression ) ';' IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement Continue = 'continue' ';' Break = 'break' ';' Return = 'return' Expression? ';' -VariableAssignment = VariableDeclaration ( AssignmentOp Expression )? ';' +VariableDefinition = VariableDeclaration ( = Expression )? ';' -Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | MemberAccess | PrimaryExpression +Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | + MemberAccess | PrimaryExpression +// The expression syntax is actually much more complicated Assignment = Expression (AssignmentOp Expression) -FunctionCall = Identifier '(' ( Expression ( ',' Expression )* ) ')' +FunctionCall = Expression '(' ( Expression ( ',' Expression )* ) ')' MemberAccess = Expression '.' Identifier IndexAccess = Expression '[' Expresison ']' -PrimaryExpression = Identifier | NumberLiteral | StringLiteral | '(' Expression ')' +PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 91247a3b7..86d09f170 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -130,6 +130,84 @@ BOOST_AUTO_TEST_CASE(mapping_to_mapping_in_struct) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(variable_definition) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " var b;\n" + " uint256 c;\n" + " mapping(address=>hash) d;\n" + " customtype varname;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(variable_definition_with_initialization) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " var b = 2;\n" + " uint256 c = 0x87;\n" + " mapping(address=>hash) d;\n" + " string name = \"Solidity\";" + " customtype varname;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(operator_expression) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 x = (1 + 4) || false && (1 - 12) + -9;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(complex_expression) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 x = (1 + 4).member(++67)[a/=9] || true;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(while_loop) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " uint256 x = (1 + 4).member(++67) || true;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(if_statement) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) {\n" + " if (a >= 8) return 2; else { var b = 7; }\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(else_if_statement) +{ + char const* text = "contract test {\n" + " function fun(uint256 a) returns (address b) {\n" + " if (a < 0) b = 0x67; else if (a == 0) b = 0x12; else b = 0x78;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + + BOOST_AUTO_TEST_SUITE_END() From 8aad54050c697653c9a61be530e190d48e80597e Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 10 Oct 2014 16:37:54 +0200 Subject: [PATCH 06/34] AST printer and command line tool, some fixes. --- CMakeLists.txt | 1 + libsolidity/AST.cpp | 232 ++++++++++++++++++++++ libsolidity/AST.h | 118 +++++++---- libsolidity/ASTForward.h | 53 +++++ libsolidity/ASTPrinter.cpp | 388 +++++++++++++++++++++++++++++++++++++ libsolidity/ASTPrinter.h | 88 +++++++++ libsolidity/ASTVisitor.h | 76 ++++++++ libsolidity/Parser.cpp | 52 +++-- libsolidity/Parser.h | 3 +- solc/CMakeLists.txt | 31 +++ solc/main.cpp | 78 ++++++++ 11 files changed, 1065 insertions(+), 55 deletions(-) create mode 100644 libsolidity/ASTForward.h create mode 100644 libsolidity/ASTPrinter.cpp create mode 100644 libsolidity/ASTPrinter.h create mode 100644 libsolidity/ASTVisitor.h create mode 100644 solc/CMakeLists.txt create mode 100644 solc/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c5cfb9a8..8705cf5ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -335,6 +335,7 @@ add_subdirectory(libpyserpent) endif() endif() add_subdirectory(lllc) +add_subdirectory(solc) add_subdirectory(sc) if (NOT LANGUAGES) add_subdirectory(secp256k1) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 76e1ff444..e0775c6ce 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -21,3 +21,235 @@ */ #include +#include + +namespace dev { +namespace solidity { + +void ContractDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + listAccept(m_definedStructs, _visitor); + listAccept(m_stateVariables, _visitor); + listAccept(m_definedFunctions, _visitor); + } + _visitor.endVisit(*this); +} + +void StructDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + listAccept(m_members, _visitor); + } + _visitor.endVisit(*this); +} + +void ParameterList::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + listAccept(m_parameters, _visitor); + } + _visitor.endVisit(*this); +} + +void FunctionDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_parameters->accept(_visitor); + if (m_returnParameters) + m_returnParameters->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void VariableDeclaration::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + if (m_type) + m_type->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void TypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void UserDefinedTypeName::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Mapping::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_keyType->accept(_visitor); + m_valueType->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Statement::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Block::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + listAccept(m_statements, _visitor); + } + _visitor.endVisit(*this); +} + +void IfStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_condition->accept(_visitor); + m_trueBody->accept(_visitor); + if (m_falseBody) + m_falseBody->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void BreakableStatement::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void WhileStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_condition->accept(_visitor); + m_body->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Continue::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Break::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Return::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + if (m_expression) + m_expression->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void VariableDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_variable->accept(_visitor); + if (m_value) + m_value->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Expression::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Assignment::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_leftHandSide->accept(_visitor); + m_rightHandSide->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void UnaryOperation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_subExpression->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void BinaryOperation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_left->accept(_visitor); + m_right->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void FunctionCall::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_expression->accept(_visitor); + listAccept(m_arguments, _visitor); + } + _visitor.endVisit(*this); +} + +void MemberAccess::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_expression->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void IndexAccess::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) { + m_base->accept(_visitor); + m_index->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void PrimaryExpression::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Identifier::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Literal::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +} } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 9a4b95215..bedb5f106 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -26,26 +26,14 @@ #include #include +#include #include #include namespace dev { namespace solidity { -// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do -// not do reference counting but point to a special memory area that is completely released -// explicitly. -template -using ptr = std::shared_ptr; -template -using vecptr = std::vector>; - -class VariableDeclaration; -class StructDefinition; -class FunctionDefinition; -class TypeName; -class Block; -class Expression; +class ASTVisitor; class ASTNode { @@ -56,7 +44,13 @@ public: virtual ~ASTNode() {} - Location getLocation() const { return m_location; } + virtual void accept(ASTVisitor& _visitor) = 0; + template + static void listAccept(vecptr& _list, ASTVisitor& _visitor) { + for (ptr& element : _list) element->accept(_visitor); + } + + Location const& getLocation() const { return m_location; } private: Location m_location; }; @@ -65,7 +59,7 @@ class ContractDefinition : public ASTNode { public: ContractDefinition(Location const& _location, - std::string const& _name, + ptr const& _name, vecptr const& _definedStructs, vecptr const& _stateVariables, vecptr const& _definedFunctions) @@ -75,8 +69,11 @@ public: m_definedFunctions(_definedFunctions) {} + virtual void accept(ASTVisitor& _visitor) override; + + const ASTString& getName() const { return *m_name; } private: - std::string m_name; + ptr m_name; vecptr m_definedStructs; vecptr m_stateVariables; vecptr m_definedFunctions; @@ -86,12 +83,15 @@ class StructDefinition : public ASTNode { public: StructDefinition(Location const& _location, - std::string const& _name, + ptr const& _name, vecptr const& _members) : ASTNode(_location), m_name(_name), m_members(_members) {} + virtual void accept(ASTVisitor& _visitor) override; + + const ASTString& getName() const { return *m_name; } private: - std::string m_name; + ptr m_name; vecptr m_members; }; @@ -104,6 +104,7 @@ public: ParameterList(Location const& _location, vecptr const& _parameters) : ASTNode(_location), m_parameters(_parameters) {} + virtual void accept(ASTVisitor& _visitor) override; private: vecptr m_parameters; }; @@ -111,7 +112,7 @@ private: class FunctionDefinition : public ASTNode { public: - FunctionDefinition(Location const& _location, std::string const& _name, bool _isPublic, + FunctionDefinition(Location const& _location, ptr const& _name, bool _isPublic, ptr const& _parameters, bool _isDeclaredConst, ptr const& _returnParameters, @@ -120,8 +121,13 @@ public: m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), m_body(_body) {} + virtual void accept(ASTVisitor& _visitor) override; + + const ASTString& getName() const { return *m_name; } + bool isPublic() const { return m_isPublic; } + bool isDeclaredConst() const { return m_isDeclaredConst; } private: - std::string m_name; + ptr m_name; bool m_isPublic; ptr m_parameters; bool m_isDeclaredConst; @@ -134,12 +140,15 @@ class VariableDeclaration : public ASTNode public: VariableDeclaration(Location const& _location, ptr const& _type, - std::string const& _name) + ptr const& _name) : ASTNode(_location), m_type(_type), m_name(_name) {} + virtual void accept(ASTVisitor& _visitor) override; + + const ASTString& getName() const { return *m_name; } private: ptr m_type; ///< can be empty ("var") - std::string m_name; + ptr m_name; }; /// types @@ -149,6 +158,7 @@ class TypeName : public ASTNode { public: explicit TypeName(Location const& _location) : ASTNode(_location) {} + virtual void accept(ASTVisitor& _visitor) override; }; /// any pre-defined type that is not a mapping @@ -158,6 +168,9 @@ public: explicit ElementaryTypeName(Location const& _location, Token::Value _type) : TypeName(_location), m_type(_type) {} + virtual void accept(ASTVisitor& _visitor) override; + + Token::Value getType() const { return m_type; } private: Token::Value m_type; }; @@ -165,11 +178,14 @@ private: class UserDefinedTypeName : public TypeName { public: - UserDefinedTypeName(Location const& _location, std::string const& _name) + UserDefinedTypeName(Location const& _location, ptr const& _name) : TypeName(_location), m_name(_name) {} + virtual void accept(ASTVisitor& _visitor) override; + + const ASTString& getName() const { return *m_name; } private: - std::string m_name; + ptr m_name; }; class Mapping : public TypeName @@ -179,6 +195,7 @@ public: ptr const& _valueType) : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} + virtual void accept(ASTVisitor& _visitor) override; private: ptr m_keyType; ptr m_valueType; @@ -193,6 +210,7 @@ class Statement : public ASTNode { public: explicit Statement(Location const& _location) : ASTNode(_location) {} + virtual void accept(ASTVisitor& _visitor) override; }; class Block : public Statement @@ -201,6 +219,7 @@ public: Block(Location const& _location, vecptr const& _statements) : Statement(_location), m_statements(_statements) {} + virtual void accept(ASTVisitor& _visitor) override; private: vecptr m_statements; }; @@ -213,16 +232,18 @@ public: : Statement(_location), m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} + virtual void accept(ASTVisitor& _visitor) override; private: ptr m_condition; ptr m_trueBody; - ptr m_falseBody; + ptr m_falseBody; //< "else" part, optional }; class BreakableStatement : public Statement { public: BreakableStatement(Location const& _location) : Statement(_location) {} + virtual void accept(ASTVisitor& _visitor) override; }; class WhileStatement : public BreakableStatement @@ -232,6 +253,7 @@ public: ptr const& _body) : BreakableStatement(_location), m_condition(_condition), m_body(_body) {} + virtual void accept(ASTVisitor& _visitor) override; private: ptr m_condition; ptr m_body; @@ -241,12 +263,14 @@ class Continue : public Statement { public: Continue(Location const& _location) : Statement(_location) {} + virtual void accept(ASTVisitor& _visitor) override; }; class Break : public Statement { public: Break(Location const& _location) : Statement(_location) {} + virtual void accept(ASTVisitor& _visitor) override; }; class Return : public Statement @@ -255,8 +279,9 @@ public: Return(Location const& _location, ptr _expression) : Statement(_location), m_expression(_expression) {} + virtual void accept(ASTVisitor& _visitor) override; private: - ptr m_expression; + ptr m_expression; //< value to return, optional }; class VariableDefinition : public Statement @@ -266,6 +291,7 @@ public: ptr _value) : Statement(_location), m_variable(_variable), m_value(_value) {} + virtual void accept(ASTVisitor& _visitor) override; private: ptr m_variable; ptr m_value; ///< can be missing @@ -275,6 +301,7 @@ class Expression : public Statement { public: Expression(Location const& _location) : Statement(_location) {} + virtual void accept(ASTVisitor& _visitor) override; }; /// @} @@ -290,6 +317,9 @@ public: : Expression(_location), m_leftHandSide(_leftHandSide), m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} + virtual void accept(ASTVisitor& _visitor) override; + + Token::Value getAssignmentOperator() const { return m_assigmentOperator; } private: ptr m_leftHandSide; Token::Value m_assigmentOperator; @@ -304,7 +334,10 @@ public: : Expression(_location), m_operator(_operator), m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} + virtual void accept(ASTVisitor& _visitor) override; + Token::Value getOperator() const { return m_operator; } + bool isPrefixOperation() const { return m_isPrefix; } private: Token::Value m_operator; ptr m_subExpression; @@ -318,6 +351,9 @@ public: Token::Value _operator, ptr const& _right) : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} + virtual void accept(ASTVisitor& _visitor) override; + + Token::Value getOperator() const { return m_operator; } private: ptr m_left; Token::Value m_operator; @@ -332,6 +368,7 @@ public: vecptr const& _arguments) : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} + virtual void accept(ASTVisitor& _visitor) override; private: ptr m_expression; vecptr m_arguments; @@ -341,12 +378,14 @@ class MemberAccess : public Expression { public: MemberAccess(Location const& _location, ptr _expression, - std::string const& _memberName) + ptr const& _memberName) : Expression(_location), m_expression(_expression), m_memberName(_memberName) {} + virtual void accept(ASTVisitor& _visitor) override; + const ASTString& getMemberName() const { return *m_memberName; } private: ptr m_expression; - std::string m_memberName; + ptr m_memberName; }; class IndexAccess : public Expression @@ -356,6 +395,7 @@ public: ptr const& _index) : Expression(_location), m_base(_base), m_index(_index) {} + virtual void accept(ASTVisitor& _visitor) override; private: ptr m_base; ptr m_index; @@ -365,15 +405,19 @@ class PrimaryExpression : public Expression { public: PrimaryExpression(Location const& _location) : Expression(_location) {} + virtual void accept(ASTVisitor& _visitor) override; }; class Identifier : public PrimaryExpression { public: - Identifier(Location const& _location, std::string const& _name) + Identifier(Location const& _location, ptr const& _name) : PrimaryExpression(_location), m_name(_name) {} + virtual void accept(ASTVisitor& _visitor) override; + + ASTString const& getName() const { return *m_name; } private: - std::string m_name; + ptr m_name; }; class ElementaryTypeNameExpression : public PrimaryExpression @@ -381,6 +425,9 @@ class ElementaryTypeNameExpression : public PrimaryExpression public: ElementaryTypeNameExpression(Location const& _location, Token::Value _type) : PrimaryExpression(_location), m_type(_type) {} + virtual void accept(ASTVisitor& _visitor) override; + + Token::Value getType() const { return m_type; } private: Token::Value m_type; }; @@ -388,15 +435,18 @@ private: class Literal : public PrimaryExpression { public: - Literal(Location const& _location, Token::Value _token, std::string const& _value) + Literal(Location const& _location, Token::Value _token, ptr const& _value) : PrimaryExpression(_location), m_token(_token), m_value(_value) {} + virtual void accept(ASTVisitor& _visitor) override; + + Token::Value getToken() const { return m_token; } + ASTString const& getValue() const { return *m_value; } private: Token::Value m_token; - std::string m_value; + ptr m_value; }; /// @} - } } diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h new file mode 100644 index 000000000..b930a2238 --- /dev/null +++ b/libsolidity/ASTForward.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +// Forward-declare all AST node types + +namespace dev { +namespace solidity { + +class ASTNode; +class ContractDefinition; +class StructDefinition; +class ParameterList; +class FunctionDefinition; +class VariableDeclaration; +class TypeName; +class ElementaryTypeName; +class UserDefinedTypeName; +class Mapping; +class Statement; +class Block; +class IfStatement; +class BreakableStatement; +class WhileStatement; +class Continue; +class Break; +class Return; +class VariableDefinition; +class Expression; +class Assignment; +class UnaryOperation; +class BinaryOperation; +class FunctionCall; +class MemberAccess; +class IndexAccess; +class PrimaryExpression; +class ElementaryTypeNameExpression; +class Literal; + +// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do +// not do reference counting but point to a special memory area that is completely released +// explicitly. +template +using ptr = std::shared_ptr; +template +using vecptr = std::vector>; + +using ASTString = std::string; + + +} } diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp new file mode 100644 index 000000000..a5fcf959f --- /dev/null +++ b/libsolidity/ASTPrinter.cpp @@ -0,0 +1,388 @@ +#include +#include + +namespace dev { +namespace solidity { + +ASTPrinter::ASTPrinter(ptr _ast, const std::string& _source) + : m_indentation(0), m_source(_source), m_ast(_ast) +{ +} + +void ASTPrinter::print(std::ostream& _stream) +{ + m_ostream = &_stream; + m_ast->accept(*this); + m_ostream = nullptr; +} + + +bool ASTPrinter::visit(ContractDefinition& _node) +{ + writeLine("ContractDefinition \"" + _node.getName() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(StructDefinition& _node) +{ + writeLine("StructDefinition \"" + _node.getName() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ParameterList& _node) +{ + writeLine("ParameterList"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(FunctionDefinition& _node) +{ + writeLine("FunctionDefinition \"" + _node.getName() + "\"" + + (_node.isPublic() ? " - public" : "") + + (_node.isDeclaredConst() ? " - const" : "")); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(VariableDeclaration& _node) +{ + writeLine("VariableDeclaration \"" + _node.getName() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(TypeName& _node) +{ + writeLine("TypeName"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ElementaryTypeName& _node) +{ + writeLine(std::string("ElementaryTypeName ") + Token::String(_node.getType())); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(UserDefinedTypeName& _node) +{ + writeLine("UserDefinedTypeName \"" + _node.getName() + "\""); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Mapping& _node) +{ + writeLine("Mapping"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Statement& _node) +{ + writeLine("Statement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Block& _node) +{ + writeLine("Block"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(IfStatement& _node) +{ + writeLine("IfStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(BreakableStatement& _node) +{ + writeLine("BreakableStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(WhileStatement& _node) +{ + writeLine("WhileStatement"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Continue& _node) +{ + writeLine("Continue"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Break& _node) +{ + writeLine("Break"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Return& _node) +{ + writeLine("Return"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(VariableDefinition& _node) +{ + writeLine("VariableDefinition"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Expression& _node) +{ + writeLine("Expression"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Assignment& _node) +{ + writeLine(std::string("Assignment using operator ") + Token::String(_node.getAssignmentOperator())); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(UnaryOperation& _node) +{ + writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + + ") " + Token::String(_node.getOperator())); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(BinaryOperation& _node) +{ + writeLine(std::string("BinaryOperation using operator ") + Token::String(_node.getOperator())); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(FunctionCall& _node) +{ + writeLine("FunctionCall"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(MemberAccess& _node) +{ + writeLine("MemberAccess to member " + _node.getMemberName()); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(IndexAccess& _node) +{ + writeLine("IndexAccess"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(PrimaryExpression& _node) +{ + writeLine("PrimaryExpression"); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) +{ + writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getType())); + printSourcePart(_node); + return goDeeper(); +} + +bool ASTPrinter::visit(Literal& _node) +{ + const char* tokenString = Token::String(_node.getToken()); + if (tokenString == nullptr) + tokenString = "----"; + writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); + printSourcePart(_node); + return goDeeper(); +} + +void ASTPrinter::endVisit(ASTNode&) +{ + m_indentation--; +} + +// @todo instead of this, we could make the default implementation of endVisit call the +// superclass' endVisit +void ASTPrinter::endVisit(ContractDefinition&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(StructDefinition&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ParameterList&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(FunctionDefinition&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(VariableDeclaration&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(TypeName&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ElementaryTypeName&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(UserDefinedTypeName&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Mapping&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Statement&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Block&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(IfStatement&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(BreakableStatement&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(WhileStatement&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Continue&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Break&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Return&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(VariableDefinition&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Expression&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Assignment&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(UnaryOperation&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(BinaryOperation&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(FunctionCall&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(MemberAccess&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(IndexAccess&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(PrimaryExpression&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(ElementaryTypeNameExpression&) +{ + m_indentation--; +} + +void ASTPrinter::endVisit(Literal&) +{ + m_indentation--; +} + +void ASTPrinter::printSourcePart(ASTNode const& _node) +{ + if (!m_source.empty()) { + Location const& location(_node.getLocation()); + *m_ostream << getIndentation() << " Source: |" + << m_source.substr(location.start, location.end - location.start) << "|\n"; + } +} + +std::string ASTPrinter::getIndentation() const +{ + return std::string(m_indentation * 2, ' '); +} + +void ASTPrinter::writeLine(const std::string& _line) +{ + *m_ostream << getIndentation() << _line << '\n'; +} + +} } diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h new file mode 100644 index 000000000..d3cad11c2 --- /dev/null +++ b/libsolidity/ASTPrinter.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include + +namespace dev { +namespace solidity { + +class ASTPrinter : public ASTVisitor +{ +public: + /// Create a printer for the given abstract syntax tree. If the source is specified, + /// the corresponding parts of the source are printed with each node. + ASTPrinter(ptr _ast, const std::string& _source = std::string()); + /// Output the string representation of the AST to _stream. + void print(std::ostream& _stream); + + bool visit(ContractDefinition& _node); + bool visit(StructDefinition& _node); + bool visit(ParameterList& _node); + bool visit(FunctionDefinition& _node); + bool visit(VariableDeclaration& _node); + bool visit(TypeName& _node); + bool visit(ElementaryTypeName& _node); + bool visit(UserDefinedTypeName& _node); + bool visit(Mapping& _node); + bool visit(Statement& _node); + bool visit(Block& _node); + bool visit(IfStatement& _node); + bool visit(BreakableStatement& _node); + bool visit(WhileStatement& _node); + bool visit(Continue& _node); + bool visit(Break& _node); + bool visit(Return& _node); + bool visit(VariableDefinition& _node); + bool visit(Expression& _node); + bool visit(Assignment& _node); + bool visit(UnaryOperation& _node); + bool visit(BinaryOperation& _node); + bool visit(FunctionCall& _node); + bool visit(MemberAccess& _node); + bool visit(IndexAccess& _node); + bool visit(PrimaryExpression& _node); + bool visit(ElementaryTypeNameExpression& _node); + bool visit(Literal& _node); + + void endVisit(ASTNode & _node); + void endVisit(ContractDefinition&); + void endVisit(StructDefinition&); + void endVisit(ParameterList&); + void endVisit(FunctionDefinition&); + void endVisit(VariableDeclaration&); + void endVisit(TypeName&); + void endVisit(ElementaryTypeName&); + void endVisit(UserDefinedTypeName&); + void endVisit(Mapping&); + void endVisit(Statement&); + void endVisit(Block&); + void endVisit(IfStatement&); + void endVisit(BreakableStatement&); + void endVisit(WhileStatement&); + void endVisit(Continue&); + void endVisit(Break&); + void endVisit(Return&); + void endVisit(VariableDefinition&); + void endVisit(Expression&); + void endVisit(Assignment&); + void endVisit(UnaryOperation&); + void endVisit(BinaryOperation&); + void endVisit(FunctionCall&); + void endVisit(MemberAccess&); + void endVisit(IndexAccess&); + void endVisit(PrimaryExpression&); + void endVisit(ElementaryTypeNameExpression&); + void endVisit(Literal&); + +private: + void printSourcePart(ASTNode const& _node); + std::string getIndentation() const; + void writeLine(std::string const& _line); + bool goDeeper() { m_indentation++; return true; } + int m_indentation; + std::string m_source; + ptr m_ast; + std::ostream* m_ostream; +}; + +} } diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h new file mode 100644 index 000000000..a68d76aee --- /dev/null +++ b/libsolidity/ASTVisitor.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +namespace dev { +namespace solidity { + +class ASTVisitor { +public: + /// These functions are called after a call to ASTNode::accept, + /// first visit, then (if visit returns true) recursively for all + /// child nodes in document order (exception for contracts) and then + /// endVisit. + virtual bool visit(ASTNode&) { return true; } + virtual bool visit(ContractDefinition&) { return true; } + virtual bool visit(StructDefinition&) { return true; } + virtual bool visit(ParameterList&) { return true; } + virtual bool visit(FunctionDefinition&) { return true; } + virtual bool visit(VariableDeclaration&) { return true; } + virtual bool visit(TypeName&) { return true; } + virtual bool visit(ElementaryTypeName&) { return true; } + virtual bool visit(UserDefinedTypeName&) { return true; } + virtual bool visit(Mapping&) { return true; } + virtual bool visit(Statement&) { return true; } + virtual bool visit(Block&) { return true; } + virtual bool visit(IfStatement&) { return true; } + virtual bool visit(BreakableStatement&) { return true; } + virtual bool visit(WhileStatement&) { return true; } + virtual bool visit(Continue&) { return true; } + virtual bool visit(Break&) { return true; } + virtual bool visit(Return&) { return true; } + virtual bool visit(VariableDefinition&) { return true; } + virtual bool visit(Expression&) { return true; } + virtual bool visit(Assignment&) { return true; } + virtual bool visit(UnaryOperation&) { return true; } + virtual bool visit(BinaryOperation&) { return true; } + virtual bool visit(FunctionCall&) { return true; } + virtual bool visit(MemberAccess&) { return true; } + virtual bool visit(IndexAccess&) { return true; } + virtual bool visit(PrimaryExpression&) { return true; } + virtual bool visit(ElementaryTypeNameExpression&) { return true; } + virtual bool visit(Literal&) { return true; } + + virtual void endVisit(ASTNode&) { } + virtual void endVisit(ContractDefinition&) { } + virtual void endVisit(StructDefinition&) { } + virtual void endVisit(ParameterList&) { } + virtual void endVisit(FunctionDefinition&) { } + virtual void endVisit(VariableDeclaration&) { } + virtual void endVisit(TypeName&) { } + virtual void endVisit(ElementaryTypeName&) { } + virtual void endVisit(UserDefinedTypeName&) { } + virtual void endVisit(Mapping&) { } + virtual void endVisit(Statement&) { } + virtual void endVisit(Block&) { } + virtual void endVisit(IfStatement&) { } + virtual void endVisit(BreakableStatement&) { } + virtual void endVisit(WhileStatement&) { } + virtual void endVisit(Continue&) { } + virtual void endVisit(Break&) { } + virtual void endVisit(Return&) { } + virtual void endVisit(VariableDefinition&) { } + virtual void endVisit(Expression&) { } + virtual void endVisit(Assignment&) { } + virtual void endVisit(UnaryOperation&) { } + virtual void endVisit(BinaryOperation&) { } + virtual void endVisit(FunctionCall&) { } + virtual void endVisit(MemberAccess&) { } + virtual void endVisit(IndexAccess&) { } + virtual void endVisit(PrimaryExpression&) { } + virtual void endVisit(ElementaryTypeNameExpression&) { } + virtual void endVisit(Literal&) { } +}; + +} } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 24c8e599e..3ccd09300 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -57,7 +57,8 @@ public: template ptr createNode(Args&&... _args) { - if (m_location.end < 0) markEndPosition(); + if (m_location.end < 0) + markEndPosition(); return std::make_shared(m_location, std::forward(_args)...); } @@ -82,7 +83,7 @@ ptr Parser::parseContractDefinition() ASTNodeFactory nodeFactory(*this); expectToken(Token::CONTRACT); - std::string name = expectIdentifier(); + ptr name = expectIdentifierToken(); expectToken(Token::LBRACE); vecptr structs; @@ -111,7 +112,7 @@ ptr Parser::parseContractDefinition() } nodeFactory.markEndPosition(); - m_scanner->next(); + expectToken(Token::RBRACE); expectToken(Token::EOS); return nodeFactory.createNode(name, structs, stateVariables, functions); @@ -122,7 +123,7 @@ ptr Parser::parseFunctionDefinition(bool _isPublic) ASTNodeFactory nodeFactory(*this); expectToken(Token::FUNCTION); - std::string name(expectIdentifier()); + ptr name(expectIdentifierToken()); ptr parameters(parseParameterList()); bool isDeclaredConst = false; if (m_scanner->getCurrentToken() == Token::CONST) { @@ -145,7 +146,7 @@ ptr Parser::parseStructDefinition() ASTNodeFactory nodeFactory(*this); expectToken(Token::STRUCT); - std::string name = expectIdentifier(); + ptr name = expectIdentifierToken(); vecptr members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { @@ -164,8 +165,7 @@ ptr Parser::parseVariableDeclaration() ptr type = parseTypeName(); nodeFactory.markEndPosition(); - std::string name = expectIdentifier(); - return nodeFactory.createNode(type, name); + return nodeFactory.createNode(type, expectIdentifierToken()); } ptr Parser::parseTypeName() @@ -181,8 +181,9 @@ ptr Parser::parseTypeName() } else if (token == Token::MAPPING) { type = parseMapping(); } else if (token == Token::IDENTIFIER) { - type = ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); - m_scanner->next(); + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); + type = nodeFactory.createNode(expectIdentifierToken()); } else { throwExpectationError("Expected type name"); } @@ -410,8 +411,7 @@ ptr Parser::parseLeftHandSideExpression() { m_scanner->next(); nodeFactory.markEndPosition(); - std::string memberName = expectIdentifier(); - expression = nodeFactory.createNode(expression, memberName); + expression = nodeFactory.createNode(expression, expectIdentifierToken()); } break; case Token::LPAREN: @@ -431,19 +431,25 @@ ptr Parser::parseLeftHandSideExpression() ptr Parser::parsePrimaryExpression() { + ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); + ptr expression; + switch (token) { case Token::TRUE_LITERAL: case Token::FALSE_LITERAL: + expression = nodeFactory.createNode(token, ptr()); m_scanner->next(); - return ASTNodeFactory(*this).createNode(token, std::string()); + break; case Token::NUMBER: case Token::STRING_LITERAL: - m_scanner->next(); - return ASTNodeFactory(*this).createNode(token, m_scanner->getCurrentLiteral()); + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode(token, getLiteralAndAdvance()); + break; case Token::IDENTIFIER: - m_scanner->next(); - return ASTNodeFactory(*this).createNode(m_scanner->getCurrentLiteral()); + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode(getLiteralAndAdvance()); + break; case Token::LPAREN: { m_scanner->next(); @@ -454,13 +460,14 @@ ptr Parser::parsePrimaryExpression() default: if (Token::IsElementaryTypeName(token)) { // used for casts + expression = nodeFactory.createNode(token); m_scanner->next(); - return ASTNodeFactory(*this).createNode(token); } else { throwExpectationError("Expected primary expression."); return ptr(); // this is not reached } } + return expression; } vecptr Parser::parseFunctionCallArguments() @@ -492,14 +499,19 @@ Token::Value Parser::expectAssignmentOperator() return op; } -std::string Parser::expectIdentifier() +ptr Parser::expectIdentifierToken() { if (m_scanner->getCurrentToken() != Token::IDENTIFIER) throwExpectationError("Expected identifier"); - std::string literal = m_scanner->getCurrentLiteral(); + return getLiteralAndAdvance(); +} + +ptr Parser::getLiteralAndAdvance() +{ + ptr identifier = std::make_shared(m_scanner->getCurrentLiteral()); m_scanner->next(); - return literal; + return identifier; } void Parser::throwExpectationError(const std::string& _description) diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 7036c3c20..f8940d1a6 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -69,7 +69,8 @@ private: /// If current token value is not _value, throw exception otherwise advance token. void expectToken(Token::Value _value); Token::Value expectAssignmentOperator(); - std::string expectIdentifier(); + ptr expectIdentifierToken(); + ptr getLiteralAndAdvance(); void throwExpectationError(const std::string& _description); /// @} diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt new file mode 100644 index 000000000..9224c109d --- /dev/null +++ b/solc/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_policy(SET CMP0015 NEW) + +aux_source_directory(. SRC_LIST) + +include_directories(..) + +set(EXECUTABLE solc) + +add_executable(${EXECUTABLE} ${SRC_LIST}) + +target_link_libraries(${EXECUTABLE} solidity) +target_link_libraries(${EXECUTABLE} devcore) + +if ("${TARGET_PLATFORM}" STREQUAL "w64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") + target_link_libraries(${EXECUTABLE} gcc) + target_link_libraries(${EXECUTABLE} gdi32) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) +elseif (UNIX) +else () + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +install( TARGETS ${EXECUTABLE} DESTINATION bin ) + diff --git a/solc/main.cpp b/solc/main.cpp new file mode 100644 index 000000000..6fca11a67 --- /dev/null +++ b/solc/main.cpp @@ -0,0 +1,78 @@ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace dev { +namespace solidity { + +ptr parseAST(std::string const& _source) +{ + ptr scanner = std::make_shared(CharStream(_source)); + Parser parser; + return parser.parse(scanner); +} + +} } // end namespaces + +void help() +{ + std::cout + << "Usage solc [OPTIONS] " << std::endl + << "Options:" << std::endl + << " -h,--help Show this help message and exit." << std::endl + << " -V,--version Show the version and exit." << std::endl; + exit(0); +} + +void version() +{ + std::cout + << "solc, the solidity complier commandline interface " << dev::Version << std::endl + << " by Christian , (c) 2014." << std::endl + << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << std::endl; + exit(0); +} + +int main(int argc, char** argv) +{ + std::string infile; + + for (int i = 1; i < argc; ++i) + { + std::string arg = argv[i]; + if (arg == "-h" || arg == "--help") + help(); + else if (arg == "-V" || arg == "--version") + version(); + else + infile = argv[i]; + } + + std::string src; + if (infile.empty()) + { + std::string s; + while (!std::cin.eof()) + { + getline(std::cin, s); + src.append(s); + } + } else { + src = dev::asString(dev::contents(infile)); + } + + std::cout << "Parsing..." << std::endl; + // @todo catch exception + dev::solidity::ptr ast = dev::solidity::parseAST(src); + std::cout << "Syntax tree for the contract:" << std::endl; + dev::solidity::ASTPrinter printer(ast, src); + printer.print(std::cout); + return 0; +} From a5a577d35cc5854b207214ef803f16f6cc0fdfe3 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 13 Oct 2014 15:07:21 +0200 Subject: [PATCH 07/34] Name resolution. --- libsolidity/AST.h | 17 ++- libsolidity/ASTForward.h | 1 + libsolidity/ASTPrinter.cpp | 12 +++ libsolidity/ASTPrinter.h | 116 ++++++++++---------- libsolidity/ASTVisitor.h | 2 + libsolidity/NameAndTypeResolver.cpp | 142 +++++++++++++++++++++++++ libsolidity/NameAndTypeResolver.h | 40 +++++++ libsolidity/Parser.cpp | 9 +- libsolidity/Parser.h | 4 +- libsolidity/Scope.h | 39 +++++++ solc/main.cpp | 8 +- test/solidityNameAndTypeResolution.cpp | 113 ++++++++++++++++++++ 12 files changed, 437 insertions(+), 66 deletions(-) create mode 100644 libsolidity/NameAndTypeResolver.cpp create mode 100644 libsolidity/NameAndTypeResolver.h create mode 100644 libsolidity/Scope.h create mode 100644 test/solidityNameAndTypeResolution.cpp diff --git a/libsolidity/AST.h b/libsolidity/AST.h index bedb5f106..cbb780871 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -72,6 +72,9 @@ public: virtual void accept(ASTVisitor& _visitor) override; const ASTString& getName() const { return *m_name; } + vecptr const& getDefinedStructs() { return m_definedStructs; } + vecptr const& getStateVariables() { return m_stateVariables; } + vecptr const& getDefinedFunctions() { return m_definedFunctions; } private: ptr m_name; vecptr m_definedStructs; @@ -105,6 +108,8 @@ public: : ASTNode(_location), m_parameters(_parameters) {} virtual void accept(ASTVisitor& _visitor) override; + + vecptr const& getParameters() { return m_parameters; } private: vecptr m_parameters; }; @@ -126,12 +131,16 @@ public: const ASTString& getName() const { return *m_name; } bool isPublic() const { return m_isPublic; } bool isDeclaredConst() const { return m_isDeclaredConst; } + vecptr const& getParameters() { return m_parameters->getParameters(); } + bool hasReturnParameters() const { return m_returnParameters.get() != nullptr; } + vecptr const& getReturnParameters() { return m_returnParameters->getParameters(); } + Block& getBody() { return *m_body; } private: ptr m_name; bool m_isPublic; ptr m_parameters; bool m_isDeclaredConst; - ptr m_returnParameters; + ptr m_returnParameters; //< either "null"pointer or pointer to non-empty parameter list ptr m_body; }; @@ -145,6 +154,7 @@ public: {} virtual void accept(ASTVisitor& _visitor) override; + TypeName* getTypeName() const { return m_type.get(); } const ASTString& getName() const { return *m_name; } private: ptr m_type; ///< can be empty ("var") @@ -416,8 +426,13 @@ public: virtual void accept(ASTVisitor& _visitor) override; ASTString const& getName() const { return *m_name; } + void setReferencedObject(ASTNode& _referencedObject) { m_referencedObject = &_referencedObject; } + ASTNode* getReferencedVariable() { return m_referencedObject; } private: ptr m_name; + + //! Node the name refers to. Has to be a declaration of some sort. + ASTNode* m_referencedObject; }; class ElementaryTypeNameExpression : public PrimaryExpression diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index b930a2238..963e3d47d 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -36,6 +36,7 @@ class FunctionCall; class MemberAccess; class IndexAccess; class PrimaryExpression; +class Identifier; class ElementaryTypeNameExpression; class Literal; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index a5fcf959f..129696213 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -202,6 +202,13 @@ bool ASTPrinter::visit(PrimaryExpression& _node) return goDeeper(); } +bool ASTPrinter::visit(Identifier& _node) +{ + writeLine(std::string("Identifier ") + _node.getName()); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) { writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getType())); @@ -356,6 +363,11 @@ void ASTPrinter::endVisit(PrimaryExpression&) m_indentation--; } +void ASTPrinter::endVisit(Identifier&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(ElementaryTypeNameExpression&) { m_indentation--; diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index d3cad11c2..8e908a84a 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -15,64 +15,66 @@ public: /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); - bool visit(ContractDefinition& _node); - bool visit(StructDefinition& _node); - bool visit(ParameterList& _node); - bool visit(FunctionDefinition& _node); - bool visit(VariableDeclaration& _node); - bool visit(TypeName& _node); - bool visit(ElementaryTypeName& _node); - bool visit(UserDefinedTypeName& _node); - bool visit(Mapping& _node); - bool visit(Statement& _node); - bool visit(Block& _node); - bool visit(IfStatement& _node); - bool visit(BreakableStatement& _node); - bool visit(WhileStatement& _node); - bool visit(Continue& _node); - bool visit(Break& _node); - bool visit(Return& _node); - bool visit(VariableDefinition& _node); - bool visit(Expression& _node); - bool visit(Assignment& _node); - bool visit(UnaryOperation& _node); - bool visit(BinaryOperation& _node); - bool visit(FunctionCall& _node); - bool visit(MemberAccess& _node); - bool visit(IndexAccess& _node); - bool visit(PrimaryExpression& _node); - bool visit(ElementaryTypeNameExpression& _node); - bool visit(Literal& _node); + bool visit(ContractDefinition& _node) override; + bool visit(StructDefinition& _node) override; + bool visit(ParameterList& _node) override; + bool visit(FunctionDefinition& _node) override; + bool visit(VariableDeclaration& _node) override; + bool visit(TypeName& _node) override; + bool visit(ElementaryTypeName& _node) override; + bool visit(UserDefinedTypeName& _node) override; + bool visit(Mapping& _node) override; + bool visit(Statement& _node) override; + bool visit(Block& _node) override; + bool visit(IfStatement& _node) override; + bool visit(BreakableStatement& _node) override; + bool visit(WhileStatement& _node) override; + bool visit(Continue& _node) override; + bool visit(Break& _node) override; + bool visit(Return& _node) override; + bool visit(VariableDefinition& _node) override; + bool visit(Expression& _node) override; + bool visit(Assignment& _node) override; + bool visit(UnaryOperation& _node) override; + bool visit(BinaryOperation& _node) override; + bool visit(FunctionCall& _node) override; + bool visit(MemberAccess& _node) override; + bool visit(IndexAccess& _node) override; + bool visit(PrimaryExpression& _node) override; + bool visit(Identifier& _node) override; + bool visit(ElementaryTypeNameExpression& _node) override; + bool visit(Literal& _node) override; - void endVisit(ASTNode & _node); - void endVisit(ContractDefinition&); - void endVisit(StructDefinition&); - void endVisit(ParameterList&); - void endVisit(FunctionDefinition&); - void endVisit(VariableDeclaration&); - void endVisit(TypeName&); - void endVisit(ElementaryTypeName&); - void endVisit(UserDefinedTypeName&); - void endVisit(Mapping&); - void endVisit(Statement&); - void endVisit(Block&); - void endVisit(IfStatement&); - void endVisit(BreakableStatement&); - void endVisit(WhileStatement&); - void endVisit(Continue&); - void endVisit(Break&); - void endVisit(Return&); - void endVisit(VariableDefinition&); - void endVisit(Expression&); - void endVisit(Assignment&); - void endVisit(UnaryOperation&); - void endVisit(BinaryOperation&); - void endVisit(FunctionCall&); - void endVisit(MemberAccess&); - void endVisit(IndexAccess&); - void endVisit(PrimaryExpression&); - void endVisit(ElementaryTypeNameExpression&); - void endVisit(Literal&); + void endVisit(ASTNode & _node) override; + void endVisit(ContractDefinition&) override; + void endVisit(StructDefinition&) override; + void endVisit(ParameterList&) override; + void endVisit(FunctionDefinition&) override; + void endVisit(VariableDeclaration&) override; + void endVisit(TypeName&) override; + void endVisit(ElementaryTypeName&) override; + void endVisit(UserDefinedTypeName&) override; + void endVisit(Mapping&) override; + void endVisit(Statement&) override; + void endVisit(Block&) override; + void endVisit(IfStatement&) override; + void endVisit(BreakableStatement&) override; + void endVisit(WhileStatement&) override; + void endVisit(Continue&) override; + void endVisit(Break&) override; + void endVisit(Return&) override; + void endVisit(VariableDefinition&) override; + void endVisit(Expression&) override; + void endVisit(Assignment&) override; + void endVisit(UnaryOperation&) override; + void endVisit(BinaryOperation&) override; + void endVisit(FunctionCall&) override; + void endVisit(MemberAccess&) override; + void endVisit(IndexAccess&) override; + void endVisit(PrimaryExpression&) override; + void endVisit(Identifier&) override; + void endVisit(ElementaryTypeNameExpression&) override; + void endVisit(Literal&) override; private: void printSourcePart(ASTNode const& _node); diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index a68d76aee..f3ae3be71 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -39,6 +39,7 @@ public: virtual bool visit(MemberAccess&) { return true; } virtual bool visit(IndexAccess&) { return true; } virtual bool visit(PrimaryExpression&) { return true; } + virtual bool visit(Identifier&) { return true; } virtual bool visit(ElementaryTypeNameExpression&) { return true; } virtual bool visit(Literal&) { return true; } @@ -69,6 +70,7 @@ public: virtual void endVisit(MemberAccess&) { } virtual void endVisit(IndexAccess&) { } virtual void endVisit(PrimaryExpression&) { } + virtual void endVisit(Identifier&) { } virtual void endVisit(ElementaryTypeNameExpression&) { } virtual void endVisit(Literal&) { } }; diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp new file mode 100644 index 000000000..c4f0612bd --- /dev/null +++ b/libsolidity/NameAndTypeResolver.cpp @@ -0,0 +1,142 @@ +#include + +#include +#include + +namespace dev { +namespace solidity { + + +class NameAndTypeResolver::ScopeHelper { +public: + ScopeHelper(NameAndTypeResolver& _resolver, ASTString const& _name, ASTNode& _declaration) + : m_resolver(_resolver) + { + m_resolver.registerName(_name, _declaration); + m_resolver.enterNewSubScope(_declaration); + } + ~ScopeHelper() + { + m_resolver.closeCurrentScope(); + } + +private: + NameAndTypeResolver& m_resolver; +}; + + +NameAndTypeResolver::NameAndTypeResolver() +{ +} + +void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) +{ + reset(); + + handleContract(_contract); +} + +void NameAndTypeResolver::handleContract(ContractDefinition& _contract) +{ + ScopeHelper scopeHelper(*this, _contract.getName(), _contract); + + for (ptr const& variable : _contract.getStateVariables()) + registerName(variable->getName(), *variable); + // @todo structs + + for (ptr const& function : _contract.getDefinedFunctions()) + handleFunction(*function); + + // @todo resolve names used in mappings +} + +void NameAndTypeResolver::reset() +{ + m_scopes.clear(); + m_globalScope = Scope(); + m_currentScope = &m_globalScope; +} + +void NameAndTypeResolver::handleFunction(FunctionDefinition& _function) +{ + ScopeHelper scopeHelper(*this, _function.getName(), _function); + + // @todo resolve names used in mappings + for (ptr const& variable : _function.getParameters()) + registerName(variable->getName(), *variable); + if (_function.hasReturnParameters()) + for (ptr const& variable : _function.getReturnParameters()) + registerName(variable->getName(), *variable); + handleFunctionBody(_function.getBody()); +} + +void NameAndTypeResolver::handleFunctionBody(Block& _functionBody) +{ + registerVariablesInFunction(_functionBody); + resolveReferencesInFunction(_functionBody); +} + +void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody) +{ + class VariableDeclarationFinder : public ASTVisitor { + public: + VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} + virtual bool visit(VariableDeclaration& _variable) override { + m_resolver.registerName(_variable.getName(), _variable); + return false; + } + private: + NameAndTypeResolver& m_resolver; + }; + + VariableDeclarationFinder declarationFinder(*this); + _functionBody.accept(declarationFinder); +} + +void NameAndTypeResolver::resolveReferencesInFunction(Block& _functionBody) +{ + class ReferencesResolver : public ASTVisitor { + public: + ReferencesResolver(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} + virtual bool visit(Identifier& _identifier) override { + ASTNode* node = m_resolver.getNameFromCurrentScope(_identifier.getName()); + if (node == nullptr) + throw std::exception(); // @todo + _identifier.setReferencedObject(*node); + return false; + } + private: + NameAndTypeResolver& m_resolver; + }; + + ReferencesResolver referencesResolver(*this); + _functionBody.accept(referencesResolver); +} + + +void NameAndTypeResolver::registerName(ASTString const& _name, ASTNode& _declaration) +{ + if (!m_currentScope->registerName(_name, _declaration)) + throw std::exception(); // @todo +} + +ASTNode* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +{ + return m_currentScope->resolveName(_name, _recursive); +} + +void NameAndTypeResolver::enterNewSubScope(ASTNode& _node) +{ + decltype(m_scopes)::iterator iter; + bool newlyAdded; + std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope)); + BOOST_ASSERT(newlyAdded); + m_currentScope = &iter->second; +} + +void NameAndTypeResolver::closeCurrentScope() +{ + m_currentScope = m_currentScope->getOuterScope(); +} + +} } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h new file mode 100644 index 000000000..ca714ac26 --- /dev/null +++ b/libsolidity/NameAndTypeResolver.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include +#include + +namespace dev { +namespace solidity { + +class NameAndTypeResolver +{ +public: + NameAndTypeResolver(); + + void resolveNamesAndTypes(ContractDefinition& _contract); +private: + class ScopeHelper; //< RIIA helper to open and close scopes + + void reset(); + + void handleContract(ContractDefinition& _contract); + void handleFunction(FunctionDefinition& _function); + void handleFunctionBody(Block& _functionBody); + void registerVariablesInFunction(Block& _functionBody); + void resolveReferencesInFunction(Block& _functionBody); + + void registerName(ASTString const& _name, ASTNode& _declaration); + ASTNode* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + + void enterNewSubScope(ASTNode& _node); + void closeCurrentScope(); + + Scope m_globalScope; // not part of the map + std::map m_scopes; + + Scope* m_currentScope; +}; + +} } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 3ccd09300..eb171cbca 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -28,7 +28,7 @@ namespace dev { namespace solidity { -ptr Parser::parse(std::shared_ptr const& _scanner) +ptr Parser::parse(std::shared_ptr const& _scanner) { m_scanner = _scanner; @@ -132,8 +132,9 @@ ptr Parser::parseFunctionDefinition(bool _isPublic) } ptr returnParameters; if (m_scanner->getCurrentToken() == Token::RETURNS) { + const bool permitEmptyParameterList = false; m_scanner->next(); - returnParameters = parseParameterList(); + returnParameters = parseParameterList(permitEmptyParameterList); } ptr block = parseBlock(); nodeFactory.setEndPositionFromNode(block); @@ -212,13 +213,13 @@ ptr Parser::parseMapping() return nodeFactory.createNode(keyType, valueType); } -ptr Parser::parseParameterList() +ptr Parser::parseParameterList(bool _permitEmpty) { ASTNodeFactory nodeFactory(*this); vecptr parameters; expectToken(Token::LPAREN); - if (m_scanner->getCurrentToken() != Token::RPAREN) { + if (!_permitEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { parameters.push_back(parseVariableDeclaration()); while (m_scanner->getCurrentToken() != Token::RPAREN) { expectToken(Token::COMMA); diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index f8940d1a6..88369052e 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -32,7 +32,7 @@ class Scanner; class Parser { public: - ptr parse(std::shared_ptr const& _scanner); + ptr parse(std::shared_ptr const& _scanner); private: class ASTNodeFactory; @@ -50,7 +50,7 @@ private: ptr parseVariableDeclaration(); ptr parseTypeName(); ptr parseMapping(); - ptr parseParameterList(); + ptr parseParameterList(bool _permitEmpty = true); ptr parseBlock(); ptr parseStatement(); ptr parseIfStatement(); diff --git a/libsolidity/Scope.h b/libsolidity/Scope.h new file mode 100644 index 000000000..c0aede949 --- /dev/null +++ b/libsolidity/Scope.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +namespace dev { +namespace solidity { + +class Scope +{ +public: + explicit Scope(Scope* _outerScope = nullptr) : m_outerScope(_outerScope) {} + /// Registers the name _name in the scope unless it is already declared. Returns true iff + /// it was not yet declared. + bool registerName(ASTString const& _name, ASTNode& _declaration) + { + if (m_declaredNames.find(_name) != m_declaredNames.end()) + return false; + m_declaredNames[_name] = &_declaration; + return true; + } + ASTNode* resolveName(ASTString const& _name, bool _recursive = false) const + { + auto result = m_declaredNames.find(_name); + if (result != m_declaredNames.end()) + return result->second; + if (_recursive && m_outerScope != nullptr) + return m_outerScope->resolveName(_name, true); + return nullptr; + } + Scope* getOuterScope() const { return m_outerScope; } + +private: + Scope* m_outerScope; + std::map m_declaredNames; +}; + +} } diff --git a/solc/main.cpp b/solc/main.cpp index 6fca11a67..a92f466ec 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -8,11 +8,12 @@ #include #include #include +#include namespace dev { namespace solidity { -ptr parseAST(std::string const& _source) +ptr parseAST(std::string const& _source) { ptr scanner = std::make_shared(CharStream(_source)); Parser parser; @@ -70,9 +71,12 @@ int main(int argc, char** argv) std::cout << "Parsing..." << std::endl; // @todo catch exception - dev::solidity::ptr ast = dev::solidity::parseAST(src); + dev::solidity::ptr ast = dev::solidity::parseAST(src); std::cout << "Syntax tree for the contract:" << std::endl; dev::solidity::ASTPrinter printer(ast, src); printer.print(std::cout); + std::cout << "Resolving identifiers..." << std::endl; + dev::solidity::NameAndTypeResolver resolver; + resolver.resolveNamesAndTypes(*ast.get()); return 0; } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp new file mode 100644 index 000000000..568025e5a --- /dev/null +++ b/test/solidityNameAndTypeResolution.cpp @@ -0,0 +1,113 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Unit tests for the name and type resolution of the solidity parser. + */ + +#include + +#include +#include +#include +#include +#include + +namespace dev { +namespace solidity { +namespace test { + +namespace { + void parseTextAndResolveNames(const std::string& _source) + { + Parser parser; + ptr contract = parser.parse( + std::make_shared(CharStream(_source))); + NameAndTypeResolver resolver; + resolver.resolveNamesAndTypes(*contract); + } +} + +BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + char const* text = "contract test {\n" + " uint256 stateVariable1;\n" + " function fun(uint256 arg1) { var x = 2; uint256 y = 3; x = 1; }" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) +{ + char const* text = "contract test {\n" + " uint256 variable;\n" + " uint128 variable;\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); +} + +BOOST_AUTO_TEST_CASE(double_function_declaration) +{ + char const* text = "contract test {\n" + " function fun() { var x = 2; }\n" + " function fun() { var y = 9; }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration) +{ + char const* text = "contract test {\n" + " function f() { uint256 x = 9; if (true) { uint256 x = 2;} x = 3; }\n" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); +} + +BOOST_AUTO_TEST_CASE(name_shadowing) +{ + char const* text = "contract test {\n" + " uint256 variable;\n" + " function f() { uint8 variable = 2; }" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(name_references) +{ + char const* text = "contract test {\n" + " uint256 variable;\n" + " function f() { variable = 2; f(); test; }" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(undeclared_name) +{ + char const* text = "contract test {\n" + " uint256 variable;\n" + " function f() { notfound = 2; }" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); +} + +BOOST_AUTO_TEST_SUITE_END() + +} } } // end namespaces + From cdf2ff14e6aa9c22bbd04d884d50a9de23c4504e Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 13 Oct 2014 17:13:48 +0200 Subject: [PATCH 08/34] Added copyright blocks. --- libsolidity/ASTForward.h | 22 ++++++++++++++++++++++ libsolidity/ASTPrinter.cpp | 22 ++++++++++++++++++++++ libsolidity/ASTPrinter.h | 22 ++++++++++++++++++++++ libsolidity/ASTVisitor.h | 22 ++++++++++++++++++++++ libsolidity/BaseTypes.h | 22 ++++++++++++++++++++++ libsolidity/NameAndTypeResolver.cpp | 22 ++++++++++++++++++++++ libsolidity/NameAndTypeResolver.h | 22 ++++++++++++++++++++++ libsolidity/Scope.h | 22 ++++++++++++++++++++++ 8 files changed, 176 insertions(+) diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 963e3d47d..e38767955 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Forward-declarations of AST classes. + */ + #pragma once #include diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 129696213..7e57732b5 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. + */ + #include #include diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 8e908a84a..6a864f38a 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. + */ + #pragma once #include diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index f3ae3be71..c28d2330f 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * AST visitor base class. + */ + #pragma once #include diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index a037f6553..6d57b74bd 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Some elementary types for the parser. + */ + #pragma once diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index c4f0612bd..c3932ca93 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Parser part that determines the declarations corresponding to names and the types of expressions. + */ + #include #include diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index ca714ac26..42efd57ab 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Parser part that determines the declarations corresponding to names and the types of expressions. + */ + #pragma once #include diff --git a/libsolidity/Scope.h b/libsolidity/Scope.h index c0aede949..be2514c4d 100644 --- a/libsolidity/Scope.h +++ b/libsolidity/Scope.h @@ -1,3 +1,25 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Scope - object that holds declaration of names. + */ + #pragma once #include From 3030e8f74fbc800341d07e703ce2b45a6a0d62f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 14 Oct 2014 01:03:17 +0200 Subject: [PATCH 09/34] Use compile-time constants instead of runtime-constructed multiprecision objects in FeeStructure. --- libevm/FeeStructure.cpp | 16 +--------------- libevm/FeeStructure.h | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index d29b9fef9..b9974a3e7 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -16,22 +16,8 @@ */ /** @file FeeStructure.cpp * @author Gav Wood + * @author Pawel Bylica * @date 2014 */ #include "FeeStructure.h" - -using namespace std; -using namespace dev; -using namespace dev::eth; - -u256 const dev::eth::c_stepGas = 1; -u256 const dev::eth::c_balanceGas = 20; -u256 const dev::eth::c_sha3Gas = 20; -u256 const dev::eth::c_sloadGas = 20; -u256 const dev::eth::c_sstoreGas = 100; -u256 const dev::eth::c_createGas = 100; -u256 const dev::eth::c_callGas = 20; -u256 const dev::eth::c_memoryGas = 1; -u256 const dev::eth::c_txDataGas = 5; -u256 const dev::eth::c_txGas = 500; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 76be9a398..8c1519e86 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -16,28 +16,34 @@ */ /** @file FeeStructure.h * @author Gav Wood + * @author Pawel Bylica * @date 2014 */ #pragma once -#include - namespace dev { namespace eth { -extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. -extern u256 const c_balanceGas; ///< Once per BALANCE operation. -extern u256 const c_sha3Gas; ///< Once per SHA3 operation. -extern u256 const c_sloadGas; ///< Once per SLOAD operation. -extern u256 const c_sstoreGas; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). -extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction. -extern u256 const c_callGas; ///< Once per CALL operation & message call transaction. -extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. -extern u256 const c_txDataGas; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. -extern u256 const c_txGas; ///< Per transaction. NOTE: Not payable on data of calls between transactions. +enum class Instruction: uint8_t; + +struct FeeStructure +{ + static uint32_t getInstructionFee(Instruction _inst); +}; + +static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. +static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. +static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. +static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. +static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). +static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. +static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. +static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. +static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. +static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. } } From 46e079fb524344b0481758b78c8b9e539b45a461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 14 Oct 2014 09:41:26 +0200 Subject: [PATCH 10/34] Place fee constants inside FeeStructure struct --- libethereum/Executive.cpp | 2 +- libethereum/Interface.h | 2 +- libevm/FeeStructure.h | 22 +++++++++++----------- libevm/VM.h | 20 ++++++++++---------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 93900b51e..a4e844d88 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -66,7 +66,7 @@ bool Executive::setup(bytesConstRef _rlp) } // Check gas cost is enough. - u256 gasCost = m_t.data.size() * c_txDataGas + c_txGas; + u256 gasCost = u256(m_t.data.size()) * FeeStructure::c_txDataGas + FeeStructure::c_txGas; if (m_t.gas < gasCost) { diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 7ae650590..6a32e81c4 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -123,7 +123,7 @@ public: virtual Addresses addresses(int _block) const = 0; /// Get the fee associated for a transaction with the given data. - static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } + static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return FeeStructure::c_txDataGas * u256(_dataCount) + FeeStructure::c_txGas + _gas; } /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const = 0; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 8c1519e86..574021d95 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -31,19 +31,19 @@ enum class Instruction: uint8_t; struct FeeStructure { + static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. + static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. + static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. + static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. + static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). + static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. + static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. + static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. + static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. + static uint32_t getInstructionFee(Instruction _inst); }; -static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. -static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. -static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. -static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. -static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). -static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. -static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. -static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. -static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. -static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. - } } diff --git a/libevm/VM.h b/libevm/VM.h index 401e30baf..6eb57941d 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -101,7 +101,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con Instruction inst = (Instruction)_ext.getCode(m_curPC); // FEES... - bigint runGas = c_stepGas; + bigint runGas = FeeStructure::c_stepGas; bigint newTempSize = m_temp.size(); switch (inst) { @@ -116,15 +116,15 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::SSTORE: require(2); if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) - runGas = c_sstoreGas * 2; + runGas = FeeStructure::c_sstoreGas * 2; else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) runGas = 0; else - runGas = c_sstoreGas; + runGas = FeeStructure::c_sstoreGas; break; case Instruction::SLOAD: - runGas = c_sloadGas; + runGas = FeeStructure::c_sloadGas; break; // These all operate on memory and therefore potentially expand it: @@ -146,7 +146,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::SHA3: require(2); - runGas = c_sha3Gas; + runGas = FeeStructure::c_sha3Gas; newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); break; case Instruction::CALLDATACOPY: @@ -163,18 +163,18 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::BALANCE: - runGas = c_balanceGas; + runGas = FeeStructure::c_balanceGas; break; case Instruction::CALL: require(7); - runGas = c_callGas + m_stack[m_stack.size() - 1]; + runGas = FeeStructure::c_callGas + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; case Instruction::CALLCODE: require(7); - runGas = c_callGas + m_stack[m_stack.size() - 1]; + runGas = FeeStructure::c_callGas + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; @@ -184,7 +184,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con auto inOff = m_stack[m_stack.size() - 2]; auto inSize = m_stack[m_stack.size() - 3]; newTempSize = inOff + inSize; - runGas = c_createGas; + runGas = FeeStructure::c_createGas; break; } @@ -302,7 +302,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con newTempSize = (newTempSize + 31) / 32 * 32; if (newTempSize > m_temp.size()) - runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; + runGas += FeeStructure::c_memoryGas * (newTempSize - m_temp.size()) / 32; if (_onOp) _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); From b1ecd3d7316efd0ff9199dad2060189f275c9fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 14 Oct 2014 10:20:05 +0200 Subject: [PATCH 11/34] Move basic instruction fee selection to a function in FeeStructure --- libevm/FeeStructure.cpp | 156 ++++++++++++++++++++++++++++++++++++++++ libevm/FeeStructure.h | 28 ++++---- libevm/VM.h | 138 +---------------------------------- 3 files changed, 174 insertions(+), 148 deletions(-) diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index b9974a3e7..661f7af6f 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -21,3 +21,159 @@ */ #include "FeeStructure.h" + +#include + +namespace dev +{ +namespace eth +{ + +uint32_t FeeStructure::getInstructionFee(Instruction _inst) BOOST_NOEXCEPT_OR_NOTHROW +{ + switch (_inst) + { + case Instruction::STOP: + case Instruction::SUICIDE: + default: // In case of bad instruction return 0 + return 0; + + case Instruction::SSTORE: + return c_sstoreGas; + + case Instruction::SLOAD: + return c_sloadGas; + + case Instruction::SHA3: + return c_sha3Gas; + + case Instruction::BALANCE: + return c_sha3Gas; + + case Instruction::CALL: + case Instruction::CALLCODE: + return c_callGas; + + case Instruction::CREATE: + return c_createGas; + + case Instruction::ADD: + case Instruction::MUL: + case Instruction::SUB: + case Instruction::DIV: + case Instruction::SDIV: + case Instruction::MOD: + case Instruction::SMOD: + case Instruction::EXP: + case Instruction::NEG: + case Instruction::LT: + case Instruction::GT: + case Instruction::SLT: + case Instruction::SGT: + case Instruction::EQ: + case Instruction::NOT: + case Instruction::AND: + case Instruction::OR: + case Instruction::XOR: + case Instruction::BYTE: + case Instruction::ADDMOD: + case Instruction::MULMOD: + case Instruction::ADDRESS: + case Instruction::ORIGIN: + case Instruction::CALLER: + case Instruction::CALLVALUE: + case Instruction::CALLDATALOAD: + case Instruction::CALLDATASIZE: + case Instruction::CODESIZE: + case Instruction::EXTCODESIZE: + case Instruction::GASPRICE: + case Instruction::PREVHASH: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::PUSH1: + case Instruction::PUSH2: + case Instruction::PUSH3: + case Instruction::PUSH4: + case Instruction::PUSH5: + case Instruction::PUSH6: + case Instruction::PUSH7: + case Instruction::PUSH8: + case Instruction::PUSH9: + case Instruction::PUSH10: + case Instruction::PUSH11: + case Instruction::PUSH12: + case Instruction::PUSH13: + case Instruction::PUSH14: + case Instruction::PUSH15: + case Instruction::PUSH16: + case Instruction::PUSH17: + case Instruction::PUSH18: + case Instruction::PUSH19: + case Instruction::PUSH20: + case Instruction::PUSH21: + case Instruction::PUSH22: + case Instruction::PUSH23: + case Instruction::PUSH24: + case Instruction::PUSH25: + case Instruction::PUSH26: + case Instruction::PUSH27: + case Instruction::PUSH28: + case Instruction::PUSH29: + case Instruction::PUSH30: + case Instruction::PUSH31: + case Instruction::PUSH32: + case Instruction::POP: + case Instruction::DUP1: + case Instruction::DUP2: + case Instruction::DUP3: + case Instruction::DUP4: + case Instruction::DUP5: + case Instruction::DUP6: + case Instruction::DUP7: + case Instruction::DUP8: + case Instruction::DUP9: + case Instruction::DUP10: + case Instruction::DUP11: + case Instruction::DUP12: + case Instruction::DUP13: + case Instruction::DUP14: + case Instruction::DUP15: + case Instruction::DUP16: + case Instruction::SWAP1: + case Instruction::SWAP2: + case Instruction::SWAP3: + case Instruction::SWAP4: + case Instruction::SWAP5: + case Instruction::SWAP6: + case Instruction::SWAP7: + case Instruction::SWAP8: + case Instruction::SWAP9: + case Instruction::SWAP10: + case Instruction::SWAP11: + case Instruction::SWAP12: + case Instruction::SWAP13: + case Instruction::SWAP14: + case Instruction::SWAP15: + case Instruction::SWAP16: + case Instruction::JUMP: + case Instruction::JUMPI: + case Instruction::PC: + case Instruction::MSIZE: + case Instruction::GAS: + case Instruction::JUMPDEST: + case Instruction::RETURN: + case Instruction::MSTORE: + case Instruction::MSTORE8: + case Instruction::MLOAD: + case Instruction::CALLDATACOPY: + case Instruction::CODECOPY: + case Instruction::EXTCODECOPY: + return c_stepGas; + } +} + +} +} \ No newline at end of file diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 574021d95..73ea48156 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -22,6 +22,8 @@ #pragma once +#include + namespace dev { namespace eth @@ -31,18 +33,20 @@ enum class Instruction: uint8_t; struct FeeStructure { - static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. - static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. - static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. - static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. - static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). - static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. - static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. - static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. - static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. - - static uint32_t getInstructionFee(Instruction _inst); + static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. + static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. + static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. + static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. + static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). + static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. + static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. + static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. + static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. + + /// Returns step fee of the instruction. + /// In case of bad instruction code, returns 0. + static uint32_t getInstructionFee(Instruction _inst) BOOST_NOEXCEPT_OR_NOTHROW; }; } diff --git a/libevm/VM.h b/libevm/VM.h index 6eb57941d..7c1e66ed5 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -101,17 +101,10 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con Instruction inst = (Instruction)_ext.getCode(m_curPC); // FEES... - bigint runGas = FeeStructure::c_stepGas; + bigint runGas = FeeStructure::getInstructionFee(inst); bigint newTempSize = m_temp.size(); switch (inst) { - case Instruction::STOP: - runGas = 0; - break; - - case Instruction::SUICIDE: - runGas = 0; - break; case Instruction::SSTORE: require(2); @@ -119,12 +112,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con runGas = FeeStructure::c_sstoreGas * 2; else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) runGas = 0; - else - runGas = FeeStructure::c_sstoreGas; - break; - - case Instruction::SLOAD: - runGas = FeeStructure::c_sloadGas; break; // These all operate on memory and therefore potentially expand it: @@ -146,7 +133,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::SHA3: require(2); - runGas = FeeStructure::c_sha3Gas; newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); break; case Instruction::CALLDATACOPY: @@ -161,20 +147,11 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con require(4); newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); break; - - case Instruction::BALANCE: - runGas = FeeStructure::c_balanceGas; - break; case Instruction::CALL: - require(7); - runGas = FeeStructure::c_callGas + m_stack[m_stack.size() - 1]; - newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); - break; - case Instruction::CALLCODE: require(7); - runGas = FeeStructure::c_callGas + m_stack[m_stack.size() - 1]; + runGas += m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; @@ -184,120 +161,9 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con auto inOff = m_stack[m_stack.size() - 2]; auto inSize = m_stack[m_stack.size() - 3]; newTempSize = inOff + inSize; - runGas = FeeStructure::c_createGas; break; } - case Instruction::ADD: - case Instruction::MUL: - case Instruction::SUB: - case Instruction::DIV: - case Instruction::SDIV: - case Instruction::MOD: - case Instruction::SMOD: - case Instruction::EXP: - case Instruction::NEG: - case Instruction::LT: - case Instruction::GT: - case Instruction::SLT: - case Instruction::SGT: - case Instruction::EQ: - case Instruction::NOT: - case Instruction::AND: - case Instruction::OR: - case Instruction::XOR: - case Instruction::BYTE: - case Instruction::ADDMOD: - case Instruction::MULMOD: - case Instruction::ADDRESS: - case Instruction::ORIGIN: - case Instruction::CALLER: - case Instruction::CALLVALUE: - case Instruction::CALLDATALOAD: - case Instruction::CALLDATASIZE: - case Instruction::CODESIZE: - case Instruction::EXTCODESIZE: - case Instruction::GASPRICE: - case Instruction::PREVHASH: - case Instruction::COINBASE: - case Instruction::TIMESTAMP: - case Instruction::NUMBER: - case Instruction::DIFFICULTY: - case Instruction::GASLIMIT: - case Instruction::PUSH1: - case Instruction::PUSH2: - case Instruction::PUSH3: - case Instruction::PUSH4: - case Instruction::PUSH5: - case Instruction::PUSH6: - case Instruction::PUSH7: - case Instruction::PUSH8: - case Instruction::PUSH9: - case Instruction::PUSH10: - case Instruction::PUSH11: - case Instruction::PUSH12: - case Instruction::PUSH13: - case Instruction::PUSH14: - case Instruction::PUSH15: - case Instruction::PUSH16: - case Instruction::PUSH17: - case Instruction::PUSH18: - case Instruction::PUSH19: - case Instruction::PUSH20: - case Instruction::PUSH21: - case Instruction::PUSH22: - case Instruction::PUSH23: - case Instruction::PUSH24: - case Instruction::PUSH25: - case Instruction::PUSH26: - case Instruction::PUSH27: - case Instruction::PUSH28: - case Instruction::PUSH29: - case Instruction::PUSH30: - case Instruction::PUSH31: - case Instruction::PUSH32: - case Instruction::POP: - case Instruction::DUP1: - case Instruction::DUP2: - case Instruction::DUP3: - case Instruction::DUP4: - case Instruction::DUP5: - case Instruction::DUP6: - case Instruction::DUP7: - case Instruction::DUP8: - case Instruction::DUP9: - case Instruction::DUP10: - case Instruction::DUP11: - case Instruction::DUP12: - case Instruction::DUP13: - case Instruction::DUP14: - case Instruction::DUP15: - case Instruction::DUP16: - case Instruction::SWAP1: - case Instruction::SWAP2: - case Instruction::SWAP3: - case Instruction::SWAP4: - case Instruction::SWAP5: - case Instruction::SWAP6: - case Instruction::SWAP7: - case Instruction::SWAP8: - case Instruction::SWAP9: - case Instruction::SWAP10: - case Instruction::SWAP11: - case Instruction::SWAP12: - case Instruction::SWAP13: - case Instruction::SWAP14: - case Instruction::SWAP15: - case Instruction::SWAP16: - case Instruction::JUMP: - case Instruction::JUMPI: - case Instruction::PC: - case Instruction::MSIZE: - case Instruction::GAS: - case Instruction::JUMPDEST: - break; - default: - BOOST_THROW_EXCEPTION(BadInstruction()); } newTempSize = (newTempSize + 31) / 32 * 32; From ddb6392338047ea565dbb44ffbd6064d35dfc9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 14 Oct 2014 10:30:39 +0200 Subject: [PATCH 12/34] Throw bad instruction exception in case instruction is bad --- libevm/VM.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libevm/VM.h b/libevm/VM.h index 7c1e66ed5..a1cb3eccb 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -188,6 +188,10 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con // EXECUTE... switch (inst) { + default: + BOOST_THROW_EXCEPTION(BadInstruction()); + break; + case Instruction::ADD: //pops two items and pushes S[-1] + S[-2] mod 2^256. require(2); From 2f9d8219677b412e4198cd82d66ffbfe96cafc93 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 13 Oct 2014 18:22:15 +0200 Subject: [PATCH 13/34] Type system, not yet complete. --- libsolidity/AST.cpp | 244 +++++++++++++++++++++++-- libsolidity/AST.h | 123 +++++++++---- libsolidity/ASTForward.h | 1 + libsolidity/ASTPrinter.cpp | 2 +- libsolidity/NameAndTypeResolver.cpp | 103 +++++++---- libsolidity/NameAndTypeResolver.h | 15 +- libsolidity/Parser.cpp | 37 ++-- libsolidity/Parser.h | 6 +- libsolidity/Scope.cpp | 48 +++++ libsolidity/Scope.h | 24 +-- libsolidity/Token.h | 13 +- libsolidity/Types.cpp | 152 +++++++++++++++ libsolidity/Types.h | 171 +++++++++++++++++ test/solidityNameAndTypeResolution.cpp | 14 +- 14 files changed, 823 insertions(+), 130 deletions(-) create mode 100644 libsolidity/Scope.cpp create mode 100644 libsolidity/Types.cpp create mode 100644 libsolidity/Types.h diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index e0775c6ce..325ef50b3 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -20,6 +20,8 @@ * Solidity abstract syntax tree. */ +#include + #include #include @@ -66,8 +68,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) void VariableDeclaration::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { - if (m_type) - m_type->accept(_visitor); + if (m_typeName) + m_typeName->accept(_visitor); } _visitor.endVisit(*this); } @@ -170,12 +172,6 @@ void VariableDefinition::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } -void Expression::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - void Assignment::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { @@ -228,12 +224,6 @@ void IndexAccess::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } -void PrimaryExpression::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - void Identifier::accept(ASTVisitor& _visitor) { _visitor.visit(*this); @@ -252,4 +242,230 @@ void Literal::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } +void Statement::expectType(Expression& _expression, const Type& _expectedType) +{ + if (!_expression.checkTypeRequirements()->isImplicitlyConvertibleTo(_expectedType)) + throw std::exception(); // @todo +} + +ptr Block::checkTypeRequirements() +{ + for (ptr const& statement : m_statements) + statement->checkTypeRequirements(); + return ptr(); +} + +ptr IfStatement::checkTypeRequirements() +{ + expectType(*m_condition, BoolType()); + m_trueBody->checkTypeRequirements(); + if (m_falseBody) m_falseBody->checkTypeRequirements(); + return ptr(); +} + +ptr WhileStatement::checkTypeRequirements() +{ + expectType(*m_condition, BoolType()); + m_body->checkTypeRequirements(); + return ptr(); +} + +ptr Continue::checkTypeRequirements() +{ + return ptr(); +} + +ptr Break::checkTypeRequirements() +{ + return ptr(); +} + +ptr Return::checkTypeRequirements() +{ + BOOST_ASSERT(m_returnParameters != nullptr); + if (m_returnParameters->getParameters().size() != 1) + throw std::exception(); // @todo + // this could later be changed such that the paramaters type is an anonymous struct type, + // but for now, we only allow one return parameter + + expectType(*m_expression, *m_returnParameters->getParameters().front()->getType()); + return ptr(); +} + +ptr VariableDefinition::checkTypeRequirements() +{ + // Variables can be declared without type (with "var"), in which case the first assignment + // setsthe type. + // Note that assignments before the first declaration are legal because of the special scoping + // rules inherited from JavaScript. + if (m_value) { + if (m_variable->getType()) { + expectType(*m_value, *m_variable->getType()); + } else { + // no type declared and no previous assignment, infer the type + m_variable->setType(m_value->checkTypeRequirements()); + } + } + return ptr(); +} + +ptr Assignment::checkTypeRequirements() +{ + //@todo lefthandside actually has to be assignable + // add a feature to the type system to check that + expectType(*m_rightHandSide, *m_leftHandSide->checkTypeRequirements()); + m_type = m_leftHandSide->getType(); + if (m_assigmentOperator != Token::ASSIGN) { + // complex assignment + if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) + throw std::exception(); + } + return m_type; +} + +ptr UnaryOperation::checkTypeRequirements() +{ + // INC, DEC, NOT, BIT_NOT, DELETE + m_type = m_subExpression->checkTypeRequirements(); + if (m_type->acceptsUnaryOperator(m_operator)) + throw std::exception(); + return m_type; +} + +ptr BinaryOperation::checkTypeRequirements() +{ + m_right->checkTypeRequirements(); + m_left->checkTypeRequirements(); + + if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) + m_commonType = m_left->getType(); + else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) + m_commonType = m_right->getType(); + else + throw std::exception(); + + if (Token::IsCompareOp(m_operator)) { + m_type = std::make_shared(); + } else { + BOOST_ASSERT(Token::IsBinaryOp(m_operator)); + m_type = m_commonType; + if (!m_commonType->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_operator))) + throw std::exception(); + } + return m_type; +} + +ptr FunctionCall::checkTypeRequirements() +{ + m_expression->checkTypeRequirements(); + for (ptr const& argument : m_arguments) + argument->checkTypeRequirements(); + + ptr expressionType = m_expression->getType(); + Type::Category const category = expressionType->getCategory(); + if (category == Type::Category::TYPE) { + TypeType* type = dynamic_cast(expressionType.get()); + BOOST_ASSERT(type != nullptr); + //@todo for structs, we have to check the number of arguments to be equal to the + // number of non-mapping members + if (m_arguments.size() != 1) + throw std::exception(); + if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType())) + throw std::exception(); + m_type = type->getActualType(); + } else if (category == Type::Category::FUNCTION) { + //@todo would be nice to create a struct type from the arguments + // and then ask if that is implicitly convertible to the struct represented by the + // function parameters + FunctionType* function = dynamic_cast(expressionType.get()); + BOOST_ASSERT(function != nullptr); + FunctionDefinition const& fun = function->getFunction(); + vecptr const& parameters = fun.getParameters(); + if (parameters.size() != m_arguments.size()) + throw std::exception(); + for (size_t i = 0; i < m_arguments.size(); ++i) { + if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) + throw std::exception(); + } + + // @todo actually the return type should be an anonymous struct, + // but we change it to the type of the first return value until we have structs + if (fun.getReturnParameterList()->getParameters().empty()) + m_type = std::make_shared(); + else + m_type = fun.getReturnParameterList()->getParameters().front()->getType(); + } else { + throw std::exception(); // type does not support invocation + } + return m_type; +} + +ptr MemberAccess::checkTypeRequirements() +{ + BOOST_ASSERT(false); // not yet implemented + // m_type = ; + return m_type; +} + +ptr IndexAccess::checkTypeRequirements() +{ + BOOST_ASSERT(false); // not yet implemented + // m_type = ; + return m_type; +} + +ptr Identifier::checkTypeRequirements() +{ + BOOST_ASSERT(m_referencedDeclaration != nullptr); + //@todo these dynamic casts here are not really nice... + // is i useful to have an AST visitor here? + // or can this already be done in NameAndTypeResolver? + // the only problem we get there is that in + // var x; + // x = 2; + // var y = x; + // the type of x is not yet determined. + VariableDeclaration* variable = dynamic_cast(m_referencedDeclaration); + if (variable != nullptr) { + if (variable->getType().get() == nullptr) + throw std::exception(); // variable used before type could be determined + m_type = variable->getType(); + return m_type; + } + //@todo can we unify these with TypeName::toType()? + StructDefinition* structDef = dynamic_cast(m_referencedDeclaration); + if (structDef != nullptr) { + // note that we do not have a struct type here + m_type = std::make_shared(std::make_shared(*structDef)); + return m_type; + } + FunctionDefinition* functionDef = dynamic_cast(m_referencedDeclaration); + if (functionDef != nullptr) { + // a function reference is not a TypeType, because calling a TypeType converts to the type. + // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type + // conversion. + m_type = std::make_shared(*functionDef); + return m_type; + } + ContractDefinition* contractDef = dynamic_cast(m_referencedDeclaration); + if (contractDef != nullptr) { + m_type = std::make_shared(std::make_shared(*contractDef)); + return m_type; + } + throw std::exception(); // declaration reference of unknown/forbidden type + return m_type; +} + +ptr ElementaryTypeNameExpression::checkTypeRequirements() +{ + m_type = std::make_shared(Type::fromElementaryTypeName(m_typeToken)); + return m_type; +} + +ptr Literal::checkTypeRequirements() +{ + m_type = Type::forLiteral(*this); + return m_type; +} + } } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index cbb780871..c1509b85d 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -22,6 +22,8 @@ #pragma once +#include + #include #include #include @@ -29,13 +31,14 @@ #include #include #include +#include namespace dev { namespace solidity { class ASTVisitor; -class ASTNode +class ASTNode : private boost::noncopyable { public: explicit ASTNode(Location const& _location) @@ -55,7 +58,18 @@ private: Location m_location; }; -class ContractDefinition : public ASTNode +class Declaration : public ASTNode +{ +public: + Declaration(Location const& _location, ptr const& _name) + : ASTNode(_location), m_name(_name) {} + + const ASTString& getName() const { return *m_name; } +private: + ptr m_name; +}; + +class ContractDefinition : public Declaration { public: ContractDefinition(Location const& _location, @@ -63,7 +77,7 @@ public: vecptr const& _definedStructs, vecptr const& _stateVariables, vecptr const& _definedFunctions) - : ASTNode(_location), m_name(_name), + : Declaration(_location, _name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions) @@ -71,30 +85,26 @@ public: virtual void accept(ASTVisitor& _visitor) override; - const ASTString& getName() const { return *m_name; } vecptr const& getDefinedStructs() { return m_definedStructs; } vecptr const& getStateVariables() { return m_stateVariables; } vecptr const& getDefinedFunctions() { return m_definedFunctions; } private: - ptr m_name; vecptr m_definedStructs; vecptr m_stateVariables; vecptr m_definedFunctions; }; -class StructDefinition : public ASTNode +class StructDefinition : public Declaration { public: StructDefinition(Location const& _location, ptr const& _name, vecptr const& _members) - : ASTNode(_location), m_name(_name), m_members(_members) + : Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; - const ASTString& getName() const { return *m_name; } private: - ptr m_name; vecptr m_members; }; @@ -114,7 +124,7 @@ private: vecptr m_parameters; }; -class FunctionDefinition : public ASTNode +class FunctionDefinition : public Declaration { public: FunctionDefinition(Location const& _location, ptr const& _name, bool _isPublic, @@ -122,43 +132,47 @@ public: bool _isDeclaredConst, ptr const& _returnParameters, ptr const& _body) - : ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters), + : Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; - const ASTString& getName() const { return *m_name; } bool isPublic() const { return m_isPublic; } bool isDeclaredConst() const { return m_isDeclaredConst; } - vecptr const& getParameters() { return m_parameters->getParameters(); } - bool hasReturnParameters() const { return m_returnParameters.get() != nullptr; } - vecptr const& getReturnParameters() { return m_returnParameters->getParameters(); } + vecptr const& getParameters() const { return m_parameters->getParameters(); } + ParameterList& getParameterList() { return *m_parameters; } + ptr const& getReturnParameterList() const { return m_returnParameters; } Block& getBody() { return *m_body; } private: - ptr m_name; bool m_isPublic; ptr m_parameters; bool m_isDeclaredConst; - ptr m_returnParameters; //< either "null"pointer or pointer to non-empty parameter list + ptr m_returnParameters; ptr m_body; }; -class VariableDeclaration : public ASTNode +class VariableDeclaration : public Declaration { public: VariableDeclaration(Location const& _location, ptr const& _type, ptr const& _name) - : ASTNode(_location), m_type(_type), m_name(_name) + : Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; - TypeName* getTypeName() const { return m_type.get(); } - const ASTString& getName() const { return *m_name; } + bool isTypeGivenExplicitly() const { return m_typeName.get() != nullptr; } + TypeName* getTypeName() const { return m_typeName.get(); } + + //! Returns the declared or inferred type. Can be an empty pointer if no type was explicitly + //! declared and there is no assignment to the variable that fixes the type. + ptr const& getType() const { return m_type; } + void setType(ptr const& _type) { m_type = _type; } private: - ptr m_type; ///< can be empty ("var") - ptr m_name; + ptr m_typeName; ///< can be empty ("var") + + ptr m_type; }; /// types @@ -169,6 +183,8 @@ class TypeName : public ASTNode public: explicit TypeName(Location const& _location) : ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; + + virtual ptr toType() = 0; }; /// any pre-defined type that is not a mapping @@ -179,6 +195,7 @@ public: : TypeName(_location), m_type(_type) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr toType() override { return Type::fromElementaryTypeName(m_type); } Token::Value getType() const { return m_type; } private: @@ -192,10 +209,15 @@ public: : TypeName(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr toType() override { return Type::fromUserDefinedTypeName(*this); } const ASTString& getName() const { return *m_name; } + void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } + StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } private: ptr m_name; + + StructDefinition* m_referencedStruct; }; class Mapping : public TypeName @@ -206,6 +228,7 @@ public: : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr toType() override { return Type::fromMapping(*this); } private: ptr m_keyType; ptr m_valueType; @@ -221,6 +244,15 @@ class Statement : public ASTNode public: explicit Statement(Location const& _location) : ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; + + //! Check all type requirements, throws exception if some requirement is not met. + //! For expressions, this also returns the inferred type of the expression. For other + //! statements, returns the empty pointer. + virtual ptr checkTypeRequirements() = 0; +protected: + //! Check that the inferred type for _expression is _expectedType or at least implicitly + //! convertible to _expectedType. If not, throw exception. + void expectType(Expression& _expression, Type const& _expectedType); }; class Block : public Statement @@ -230,6 +262,8 @@ public: : Statement(_location), m_statements(_statements) {} virtual void accept(ASTVisitor& _visitor) override; + + virtual ptr checkTypeRequirements() override; private: vecptr m_statements; }; @@ -243,6 +277,7 @@ public: m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; private: ptr m_condition; ptr m_trueBody; @@ -264,6 +299,7 @@ public: : BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; private: ptr m_condition; ptr m_body; @@ -274,6 +310,7 @@ class Continue : public Statement public: Continue(Location const& _location) : Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; }; class Break : public Statement @@ -281,6 +318,7 @@ class Break : public Statement public: Break(Location const& _location) : Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; }; class Return : public Statement @@ -290,8 +328,13 @@ public: : Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; + + void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } private: ptr m_expression; //< value to return, optional + + ParameterList* m_returnParameters; //< extracted from the function declaration }; class VariableDefinition : public Statement @@ -302,6 +345,8 @@ public: : Statement(_location), m_variable(_variable), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; + private: ptr m_variable; ptr m_value; ///< can be missing @@ -311,7 +356,9 @@ class Expression : public Statement { public: Expression(Location const& _location) : Statement(_location) {} - virtual void accept(ASTVisitor& _visitor) override; + ptr const& getType() { return m_type; } +protected: + ptr m_type; }; /// @} @@ -328,6 +375,7 @@ public: m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; Token::Value getAssignmentOperator() const { return m_assigmentOperator; } private: @@ -345,6 +393,7 @@ public: m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } @@ -362,12 +411,15 @@ public: : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } private: ptr m_left; Token::Value m_operator; ptr m_right; + + ptr m_commonType; }; /// Can be ordinary function call, type cast or struct construction. @@ -379,6 +431,7 @@ public: : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; private: ptr m_expression; vecptr m_arguments; @@ -393,6 +446,7 @@ public: {} virtual void accept(ASTVisitor& _visitor) override; const ASTString& getMemberName() const { return *m_memberName; } + virtual ptr checkTypeRequirements() override; private: ptr m_expression; ptr m_memberName; @@ -406,6 +460,7 @@ public: : Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; private: ptr m_base; ptr m_index; @@ -415,7 +470,6 @@ class PrimaryExpression : public Expression { public: PrimaryExpression(Location const& _location) : Expression(_location) {} - virtual void accept(ASTVisitor& _visitor) override; }; class Identifier : public PrimaryExpression @@ -424,27 +478,29 @@ public: Identifier(Location const& _location, ptr const& _name) : PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; ASTString const& getName() const { return *m_name; } - void setReferencedObject(ASTNode& _referencedObject) { m_referencedObject = &_referencedObject; } - ASTNode* getReferencedVariable() { return m_referencedObject; } + void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } + Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } private: ptr m_name; - //! Node the name refers to. Has to be a declaration of some sort. - ASTNode* m_referencedObject; + //! Declaration the name refers to. + Declaration* m_referencedDeclaration; }; class ElementaryTypeNameExpression : public PrimaryExpression { public: - ElementaryTypeNameExpression(Location const& _location, Token::Value _type) - : PrimaryExpression(_location), m_type(_type) {} + ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken) + : PrimaryExpression(_location), m_typeToken(_typeToken) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; - Token::Value getType() const { return m_type; } + Token::Value getTypeToken() const { return m_typeToken; } private: - Token::Value m_type; + Token::Value m_typeToken; }; class Literal : public PrimaryExpression @@ -454,6 +510,7 @@ public: : PrimaryExpression(_location), m_token(_token), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr checkTypeRequirements() override; Token::Value getToken() const { return m_token; } ASTString const& getValue() const { return *m_value; } diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index e38767955..4963776b3 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -32,6 +32,7 @@ namespace dev { namespace solidity { class ASTNode; +class Declaration; class ContractDefinition; class StructDefinition; class ParameterList; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 7e57732b5..bbaa2e0af 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -233,7 +233,7 @@ bool ASTPrinter::visit(Identifier& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) { - writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getType())); + writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getTypeToken())); printSourcePart(_node); return goDeeper(); } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index c3932ca93..33b550eb6 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -31,10 +31,10 @@ namespace solidity { class NameAndTypeResolver::ScopeHelper { public: - ScopeHelper(NameAndTypeResolver& _resolver, ASTString const& _name, ASTNode& _declaration) + ScopeHelper(NameAndTypeResolver& _resolver, Declaration& _declaration) : m_resolver(_resolver) { - m_resolver.registerName(_name, _declaration); + m_resolver.registerDeclaration(_declaration); m_resolver.enterNewSubScope(_declaration); } ~ScopeHelper() @@ -60,16 +60,15 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) void NameAndTypeResolver::handleContract(ContractDefinition& _contract) { - ScopeHelper scopeHelper(*this, _contract.getName(), _contract); + ScopeHelper scopeHelper(*this, _contract); + + // @todo structs (definition and usage) for (ptr const& variable : _contract.getStateVariables()) - registerName(variable->getName(), *variable); - // @todo structs + registerVariableDeclarationAndResolveType(*variable); for (ptr const& function : _contract.getDefinedFunctions()) handleFunction(*function); - - // @todo resolve names used in mappings } void NameAndTypeResolver::reset() @@ -81,30 +80,20 @@ void NameAndTypeResolver::reset() void NameAndTypeResolver::handleFunction(FunctionDefinition& _function) { - ScopeHelper scopeHelper(*this, _function.getName(), _function); - - // @todo resolve names used in mappings - for (ptr const& variable : _function.getParameters()) - registerName(variable->getName(), *variable); - if (_function.hasReturnParameters()) - for (ptr const& variable : _function.getReturnParameters()) - registerName(variable->getName(), *variable); - handleFunctionBody(_function.getBody()); -} + ScopeHelper scopeHelper(*this, _function); -void NameAndTypeResolver::handleFunctionBody(Block& _functionBody) -{ - registerVariablesInFunction(_functionBody); - resolveReferencesInFunction(_functionBody); + registerVariablesInFunction(_function); + resolveReferencesInFunction(*_function.getReturnParameterList(), _function.getBody()); + _function.getBody().checkTypeRequirements(); } -void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody) +void NameAndTypeResolver::registerVariablesInFunction(FunctionDefinition& _function) { class VariableDeclarationFinder : public ASTVisitor { public: VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} virtual bool visit(VariableDeclaration& _variable) override { - m_resolver.registerName(_variable.getName(), _variable); + m_resolver.registerVariableDeclarationAndResolveType(_variable); return false; } private: @@ -112,37 +101,85 @@ void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody) }; VariableDeclarationFinder declarationFinder(*this); - _functionBody.accept(declarationFinder); + _function.accept(declarationFinder); } -void NameAndTypeResolver::resolveReferencesInFunction(Block& _functionBody) +void NameAndTypeResolver::resolveReferencesInFunction(ParameterList& _returnParameters, + Block& _functionBody) { class ReferencesResolver : public ASTVisitor { public: - ReferencesResolver(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} + ReferencesResolver(NameAndTypeResolver& _resolver, + ParameterList& _returnParameters) + : m_resolver(_resolver), m_returnParameters(_returnParameters) {} virtual bool visit(Identifier& _identifier) override { - ASTNode* node = m_resolver.getNameFromCurrentScope(_identifier.getName()); - if (node == nullptr) + Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); + if (declaration == nullptr) throw std::exception(); // @todo - _identifier.setReferencedObject(*node); + _identifier.setReferencedDeclaration(*declaration); return false; } + virtual bool visit(Return& _return) override { + _return.setFunctionReturnParameters(m_returnParameters); + return true; + } private: NameAndTypeResolver& m_resolver; + ParameterList& m_returnParameters; }; - ReferencesResolver referencesResolver(*this); + ReferencesResolver referencesResolver(*this, _returnParameters); _functionBody.accept(referencesResolver); } +void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDeclaration& _variable) +{ + registerDeclaration(_variable); + TypeName* typeName = _variable.getTypeName(); + if (typeName == nullptr) // unknown type, to be resolved by first assignment + return; + + // walk the AST to resolve user defined type references + // (walking is necessory because of mappings) + // @todo this could probably also be done at an earlier stage where we anyway + // walk the AST + + class UserDefinedTypeNameResolver : public ASTVisitor { + public: + UserDefinedTypeNameResolver(NameAndTypeResolver& _resolver) + : m_resolver(_resolver) {} + virtual bool visit(UserDefinedTypeName& _typeName) override { + Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); + if (declaration == nullptr) + throw std::exception(); // @todo + StructDefinition* referencedStruct = dynamic_cast(declaration); + if (referencedStruct == nullptr) + throw std::exception(); // @todo we only allow structs as user defined types (later also contracts) + _typeName.setReferencedStruct(*referencedStruct); + return false; + } + virtual bool visit(Mapping&) override { + // @todo + return true; + } + private: + NameAndTypeResolver& m_resolver; + }; + + UserDefinedTypeNameResolver resolver(*this); + _variable.accept(resolver); + + _variable.setType(typeName->toType()); +} + -void NameAndTypeResolver::registerName(ASTString const& _name, ASTNode& _declaration) +void NameAndTypeResolver::registerDeclaration(Declaration& _declaration) { - if (!m_currentScope->registerName(_name, _declaration)) + if (!m_currentScope->registerDeclaration(_declaration)) throw std::exception(); // @todo } -ASTNode* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) { return m_currentScope->resolveName(_name, _recursive); } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 42efd57ab..036c3fba2 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -24,13 +24,15 @@ #include +#include + #include #include namespace dev { namespace solidity { -class NameAndTypeResolver +class NameAndTypeResolver : private boost::noncopyable { public: NameAndTypeResolver(); @@ -43,12 +45,13 @@ private: void handleContract(ContractDefinition& _contract); void handleFunction(FunctionDefinition& _function); - void handleFunctionBody(Block& _functionBody); - void registerVariablesInFunction(Block& _functionBody); - void resolveReferencesInFunction(Block& _functionBody); + void registerVariablesInFunction(FunctionDefinition& _function); + void resolveReferencesInFunction(ParameterList& _returnParameters, + Block& _functionBody); - void registerName(ASTString const& _name, ASTNode& _declaration); - ASTNode* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + void registerVariableDeclarationAndResolveType(VariableDeclaration& _variable); + void registerDeclaration(Declaration& _declaration); + Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); void enterNewSubScope(ASTNode& _node); void closeCurrentScope(); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index eb171cbca..ab72bf7fc 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -47,6 +47,8 @@ public: void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + void setLocationEmpty() { m_location.end = m_location.start; } + /// Set the end position to the one of the given node. void setEndPositionFromNode(const ptr& _node) { @@ -104,7 +106,8 @@ ptr Parser::parseContractDefinition() structs.push_back(parseStructDefinition()); } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::IsElementaryTypeName(currentToken)) { - stateVariables.push_back(parseVariableDeclaration()); + bool const allowVar = false; + stateVariables.push_back(parseVariableDeclaration(allowVar)); expectToken(Token::SEMICOLON); } else { throwExpectationError("Function, variable or struct declaration expected."); @@ -135,6 +138,11 @@ ptr Parser::parseFunctionDefinition(bool _isPublic) const bool permitEmptyParameterList = false; m_scanner->next(); returnParameters = parseParameterList(permitEmptyParameterList); + } else { + // create an empty parameter list at a zero-length location + ASTNodeFactory nodeFactory(*this); + nodeFactory.setLocationEmpty(); + returnParameters = nodeFactory.createNode(vecptr()); } ptr block = parseBlock(); nodeFactory.setEndPositionFromNode(block); @@ -151,7 +159,8 @@ ptr Parser::parseStructDefinition() vecptr members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { - members.push_back(parseVariableDeclaration()); + bool const allowVar = false; + members.push_back(parseVariableDeclaration(allowVar)); expectToken(Token::SEMICOLON); } nodeFactory.markEndPosition(); @@ -160,16 +169,16 @@ ptr Parser::parseStructDefinition() return nodeFactory.createNode(name, members); } -ptr Parser::parseVariableDeclaration() +ptr Parser::parseVariableDeclaration(bool _allowVar) { ASTNodeFactory nodeFactory(*this); - ptr type = parseTypeName(); + ptr type = parseTypeName(_allowVar); nodeFactory.markEndPosition(); return nodeFactory.createNode(type, expectIdentifierToken()); } -ptr Parser::parseTypeName() +ptr Parser::parseTypeName(bool _allowVar) { ptr type; Token::Value token = m_scanner->getCurrentToken(); @@ -177,7 +186,8 @@ ptr Parser::parseTypeName() type = ASTNodeFactory(*this).createNode(token); m_scanner->next(); } else if (token == Token::VAR) { - type = ASTNodeFactory(*this).createNode(); + if (!_allowVar) + throwExpectationError("Expected explicit type name."); m_scanner->next(); } else if (token == Token::MAPPING) { type = parseMapping(); @@ -206,24 +216,26 @@ ptr Parser::parseMapping() m_scanner->next(); expectToken(Token::ARROW); - ptr valueType = parseTypeName(); + bool const allowVar = false; + ptr valueType = parseTypeName(allowVar); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); return nodeFactory.createNode(keyType, valueType); } -ptr Parser::parseParameterList(bool _permitEmpty) +ptr Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); vecptr parameters; expectToken(Token::LPAREN); - if (!_permitEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { - parameters.push_back(parseVariableDeclaration()); + if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { + bool const allowVar = false; + parameters.push_back(parseVariableDeclaration(allowVar)); while (m_scanner->getCurrentToken() != Token::RPAREN) { expectToken(Token::COMMA); - parameters.push_back(parseVariableDeclaration()); + parameters.push_back(parseVariableDeclaration(allowVar)); } } nodeFactory.markEndPosition(); @@ -328,7 +340,8 @@ ptr Parser::parseWhileStatement() ptr Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); - ptr variable = parseVariableDeclaration(); + bool const allowVar = true; + ptr variable = parseVariableDeclaration(allowVar); ptr value; if (m_scanner->getCurrentToken() == Token::ASSIGN) { m_scanner->next(); diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 88369052e..84d20cf8e 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -47,10 +47,10 @@ private: ptr parseContractDefinition(); ptr parseFunctionDefinition(bool _isPublic); ptr parseStructDefinition(); - ptr parseVariableDeclaration(); - ptr parseTypeName(); + ptr parseVariableDeclaration(bool _allowVar); + ptr parseTypeName(bool _allowVar); ptr parseMapping(); - ptr parseParameterList(bool _permitEmpty = true); + ptr parseParameterList(bool _allowEmpty = true); ptr parseBlock(); ptr parseStatement(); ptr parseIfStatement(); diff --git a/libsolidity/Scope.cpp b/libsolidity/Scope.cpp new file mode 100644 index 000000000..27298f878 --- /dev/null +++ b/libsolidity/Scope.cpp @@ -0,0 +1,48 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Scope - object that holds declaration of names. + */ + +#include +#include + +namespace dev { +namespace solidity { + + +bool Scope::registerDeclaration(Declaration& _declaration) +{ + if (m_declarations.find(_declaration.getName()) != m_declarations.end()) + return false; + m_declarations[_declaration.getName()] = &_declaration; + return true; +} + +Declaration*Scope::resolveName(ASTString const& _name, bool _recursive) const +{ + auto result = m_declarations.find(_name); + if (result != m_declarations.end()) + return result->second; + if (_recursive && m_outerScope != nullptr) + return m_outerScope->resolveName(_name, true); + return nullptr; +} + +} } diff --git a/libsolidity/Scope.h b/libsolidity/Scope.h index be2514c4d..e3b024ece 100644 --- a/libsolidity/Scope.h +++ b/libsolidity/Scope.h @@ -24,6 +24,8 @@ #include +#include + #include namespace dev { @@ -33,29 +35,15 @@ class Scope { public: explicit Scope(Scope* _outerScope = nullptr) : m_outerScope(_outerScope) {} - /// Registers the name _name in the scope unless it is already declared. Returns true iff + /// Registers the declaration in the scope unless its name is already declared. Returns true iff /// it was not yet declared. - bool registerName(ASTString const& _name, ASTNode& _declaration) - { - if (m_declaredNames.find(_name) != m_declaredNames.end()) - return false; - m_declaredNames[_name] = &_declaration; - return true; - } - ASTNode* resolveName(ASTString const& _name, bool _recursive = false) const - { - auto result = m_declaredNames.find(_name); - if (result != m_declaredNames.end()) - return result->second; - if (_recursive && m_outerScope != nullptr) - return m_outerScope->resolveName(_name, true); - return nullptr; - } + bool registerDeclaration(Declaration& _declaration); + Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; Scope* getOuterScope() const { return m_outerScope; } private: Scope* m_outerScope; - std::map m_declaredNames; + std::map m_declarations; }; } } diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 2ff5067bc..d1db4134b 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -93,6 +93,7 @@ namespace solidity { T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \ T(ASSIGN, "=", 2) \ + /* The following have to be in exactly the same order as the simple binary operators*/ \ T(ASSIGN_BIT_OR, "|=", 2) \ T(ASSIGN_BIT_XOR, "^=", 2) \ T(ASSIGN_BIT_AND, "&=", 2) \ @@ -117,7 +118,6 @@ namespace solidity { T(SHL, "<<", 11) \ T(SAR, ">>", 11) \ T(SHR, ">>>", 11) \ - T(ROR, "rotate right", 11) /* only used by Crankshaft */ \ T(ADD, "+", 12) \ T(SUB, "-", 12) \ T(MUL, "*", 13) \ @@ -181,7 +181,9 @@ namespace solidity { K(WHILE, "while", 0) \ K(WITH, "with", 0) \ \ - /* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \ + /* type keywords, keep them in this order, keep int as first keyword + * the implementation in Types.cpp has to be synced to this here + * TODO more to be added */ \ K(INT, "int", 0) \ K(INT32, "int32", 0) \ K(INT64, "int64", 0) \ @@ -274,7 +276,7 @@ public: } static bool IsTruncatingBinaryOp(Value op) { - return BIT_OR <= op && op <= ROR; + return BIT_OR <= op && op <= SHR; } static bool IsCompareOp(Value op) { @@ -332,6 +334,11 @@ public: } } + static Value AssignmentToBinaryOp(Value op) { + BOOST_ASSERT(IsAssignmentOp(op) && op != ASSIGN); + return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); + } + static bool IsBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp new file mode 100644 index 000000000..ee0a653fb --- /dev/null +++ b/libsolidity/Types.cpp @@ -0,0 +1,152 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity data types + */ + +#include +#include + +namespace dev { +namespace solidity { + +ptr Type::fromElementaryTypeName(Token::Value _typeToken) +{ + if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { + int offset = _typeToken - Token::INT; + int bits = offset % 5; + if (bits == 0) + bits = 256; + else + bits = (1 << (bits - 1)) * 32; + int modifier = offset / 5; + return std::make_shared(bits, + modifier == 0 ? IntegerType::Modifier::UNSIGNED : + modifier == 1 ? IntegerType::Modifier::SIGNED : + IntegerType::Modifier::HASH); + } else if (_typeToken == Token::ADDRESS) { + return std::make_shared(0, IntegerType::Modifier::ADDRESS); + } else if (_typeToken == Token::BOOL) { + return std::make_shared(); + } else { + BOOST_ASSERT(false); + // @todo add other tyes + } +} + +ptr Type::fromUserDefinedTypeName(const UserDefinedTypeName& _typeName) +{ + return std::make_shared(*_typeName.getReferencedStruct()); +} + +ptr Type::fromMapping(const Mapping&) +{ + BOOST_ASSERT(false); //@todo not yet implemented + return ptr(); +} + +ptr Type::forLiteral(const Literal& _literal) +{ + switch (_literal.getToken()) { + case Token::TRUE_LITERAL: + case Token::FALSE_LITERAL: + return std::make_shared(); + case Token::NUMBER: + return IntegerType::smallestTypeForLiteral(_literal.getValue()); + case Token::STRING_LITERAL: + return ptr(); // @todo + default: + return ptr(); + } +} + +ptr IntegerType::smallestTypeForLiteral(const std::string&) +{ + //@todo + return std::make_shared(256, Modifier::UNSIGNED); +} + +IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier) + : m_bits(_bits), m_modifier(_modifier) +{ + BOOST_ASSERT(_bits > 0 && _bits <= 256 && _bits % 8 == 0); + if (isAddress()) + _bits = 160; +} + +bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.getCategory() != Category::INTEGER) + return false; + IntegerType const& convertTo = dynamic_cast(_convertTo); + if (convertTo.m_bits < m_bits) + return false; + if (isAddress()) + return convertTo.isAddress(); + else if (isHash()) + return convertTo.isHash(); + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.m_bits > m_bits; +} + +bool IntegerType::isExplicitlyConvertibleTo(const Type& _convertTo) const +{ + // @todo + return false; +} + +bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const +{ + //@todo + return true; +} + +bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const +{ + //@todo + return true; +} + +bool BoolType::isExplicitlyConvertibleTo(const Type& _convertTo) const +{ + //@todo conversion to integer is fine, but not to address + //@todo this is an example of explicit conversions being not transitive (though implicit should) + return isImplicitlyConvertibleTo(_convertTo); +} + +bool ContractType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.getCategory() != Category::CONTRACT) + return false; + ContractType const& convertTo = dynamic_cast(_convertTo); + return &m_contract == &convertTo.m_contract; +} + +bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.getCategory() != Category::STRUCT) + return false; + StructType const& convertTo = dynamic_cast(_convertTo); + return &m_struct == &convertTo.m_struct; +} + + +} } diff --git a/libsolidity/Types.h b/libsolidity/Types.h new file mode 100644 index 000000000..50d00a549 --- /dev/null +++ b/libsolidity/Types.h @@ -0,0 +1,171 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity data types + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace dev { +namespace solidity { + +// AST forward declarations +class ContractDefinition; +class FunctionDefinition; +class StructDefinition; +class Literal; +class ElementaryTypeName; +class UserDefinedTypeName; +class Mapping; + +template +using ptr = std::shared_ptr; + +// @todo realMxN, string, mapping + +class Type : private boost::noncopyable +{ +public: + enum class Category { + INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE + }; + + //! factory functions that convert an AST TypeName to a Type. + static ptr fromElementaryTypeName(Token::Value _typeToken); + static ptr fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); + static ptr fromMapping(Mapping const& _typeName); + + static ptr forLiteral(Literal const& _literal); + + virtual Category getCategory() const = 0; + virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const { return false; } + virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const { return isImplicitlyConvertibleTo(_convertTo); } + virtual bool acceptsBinaryOperator(Token::Value _operator) const { return false; } + virtual bool acceptsUnaryOperator(Token::Value _operator) const { return false; } +}; + +class IntegerType : public Type +{ +public: + enum class Modifier { + UNSIGNED, SIGNED, HASH, ADDRESS + }; + virtual Category getCategory() const { return Category::INTEGER; } + + static ptr smallestTypeForLiteral(std::string const& _literal); + + explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual bool acceptsBinaryOperator(Token::Value _operator) const override; + virtual bool acceptsUnaryOperator(Token::Value _operator) const override; + + int getNumBits() const { return m_bits; } + bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } + bool isAddress() const { return m_modifier == Modifier::ADDRESS; } + int isSigned() const { return m_modifier == Modifier::SIGNED; } +private: + int m_bits; + Modifier m_modifier; +}; + +class BoolType : public Type +{ +public: + virtual Category getCategory() const { return Category::BOOL; } + virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override + { return _convertTo.getCategory() == Category::BOOL; } + virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual bool acceptsBinaryOperator(Token::Value _operator) const override + { return _operator == Token::AND || _operator == Token::OR; } + virtual bool acceptsUnaryOperator(Token::Value _operator) const override + { return _operator == Token::NOT || _operator == Token::DELETE; } +}; + +class ContractType : public Type +{ +public: + virtual Category getCategory() const { return Category::CONTRACT; } + ContractType(ContractDefinition const& _contract) : m_contract(_contract) {} + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; +private: + ContractDefinition const& m_contract; +}; + +class StructType : public Type +{ +public: + virtual Category getCategory() const { return Category::STRUCT; } + StructType(StructDefinition const& _struct) : m_struct(_struct) {} + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; + virtual bool acceptsUnaryOperator(Token::Value _operator) const override + { return _operator == Token::DELETE; } +private: + StructDefinition const& m_struct; +}; + +class FunctionType : public Type +{ +public: + virtual Category getCategory() const { return Category::FUNCTION; } + FunctionType(FunctionDefinition const& _function) : m_function(_function) {} + + FunctionDefinition const& getFunction() const { return m_function; } +private: + FunctionDefinition const& m_function; +}; + +class MappingType : public Type +{ +public: + virtual Category getCategory() const { return Category::MAPPING; } + MappingType() {} +private: + //@todo +}; + +//@todo should be changed into "empty anonymous struct" +class VoidType : public Type +{ +public: + virtual Category getCategory() const { return Category::VOID; } + VoidType() {} +}; + +class TypeType : public Type +{ +public: + virtual Category getCategory() const { return Category::TYPE; } + TypeType(ptr const& _actualType) : m_actualType(_actualType) {} + + ptr const& getActualType() { return m_actualType; } +private: + ptr m_actualType; +}; + + +} } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 568025e5a..5a938e461 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) { char const* text = "contract test {\n" " uint256 stateVariable1;\n" - " function fun(uint256 arg1) { var x = 2; uint256 y = 3; x = 1; }" + " function fun(uint256 arg1) { var x; uint256 y; }" "}\n"; BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } @@ -66,8 +66,8 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) BOOST_AUTO_TEST_CASE(double_function_declaration) { char const* text = "contract test {\n" - " function fun() { var x = 2; }\n" - " function fun() { var y = 9; }\n" + " function fun() { var x; }\n" + " function fun() { var x; }\n" "}\n"; BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); } @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_variable_declaration) { char const* text = "contract test {\n" - " function f() { uint256 x = 9; if (true) { uint256 x = 2;} x = 3; }\n" + " function f() { uint256 x; if (true) { uint256 x; } }\n" "}\n"; BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); } @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(name_shadowing) { char const* text = "contract test {\n" " uint256 variable;\n" - " function f() { uint8 variable = 2; }" + " function f() { uint32 variable ; }" "}\n"; BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } @@ -93,7 +93,7 @@ BOOST_AUTO_TEST_CASE(name_references) { char const* text = "contract test {\n" " uint256 variable;\n" - " function f() { variable = 2; f(); test; }" + " function f(uint256 arg) returns (uint out) { f(variable); test; out; }" "}\n"; BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } @@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name) { char const* text = "contract test {\n" " uint256 variable;\n" - " function f() { notfound = 2; }" + " function f(uint256 arg) { f(notfound); }" "}\n"; BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); } From b71e9932237e05c200b28082d4e85730d5431b38 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 15 Oct 2014 14:45:51 +0200 Subject: [PATCH 14/34] Added meaningful exception types. --- libsolidity/AST.cpp | 36 ++++++++++++++++---------- libsolidity/Exceptions.h | 34 ++++++++++++++++++++++++ libsolidity/NameAndTypeResolver.cpp | 10 ++++--- libsolidity/Parser.cpp | 26 ++++++++++--------- test/solidityNameAndTypeResolution.cpp | 9 ++++--- test/solidityParser.cpp | 4 +-- 6 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 libsolidity/Exceptions.h diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 325ef50b3..926f1c685 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -24,6 +24,7 @@ #include #include +#include namespace dev { namespace solidity { @@ -245,7 +246,9 @@ void Literal::accept(ASTVisitor& _visitor) void Statement::expectType(Expression& _expression, const Type& _expectedType) { if (!_expression.checkTypeRequirements()->isImplicitlyConvertibleTo(_expectedType)) - throw std::exception(); // @todo + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type not implicitly convertible " + "to expected type.")); + //@todo provide more information to the exception } ptr Block::checkTypeRequirements() @@ -284,7 +287,9 @@ ptr Return::checkTypeRequirements() { BOOST_ASSERT(m_returnParameters != nullptr); if (m_returnParameters->getParameters().size() != 1) - throw std::exception(); // @todo + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Different number of arguments in " + "return statement than in returns " + "declaration.")); // this could later be changed such that the paramaters type is an anonymous struct type, // but for now, we only allow one return parameter @@ -318,7 +323,7 @@ ptr Assignment::checkTypeRequirements() if (m_assigmentOperator != Token::ASSIGN) { // complex assignment if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); } return m_type; } @@ -328,7 +333,7 @@ ptr UnaryOperation::checkTypeRequirements() // INC, DEC, NOT, BIT_NOT, DELETE m_type = m_subExpression->checkTypeRequirements(); if (m_type->acceptsUnaryOperator(m_operator)) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Unary operator not compatible with type.")); return m_type; } @@ -342,7 +347,7 @@ ptr BinaryOperation::checkTypeRequirements() else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) m_commonType = m_right->getType(); else - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation.")); if (Token::IsCompareOp(m_operator)) { m_type = std::make_shared(); @@ -350,7 +355,7 @@ ptr BinaryOperation::checkTypeRequirements() BOOST_ASSERT(Token::IsBinaryOp(m_operator)); m_type = m_commonType; if (!m_commonType->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_operator))) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); } return m_type; } @@ -369,9 +374,11 @@ ptr FunctionCall::checkTypeRequirements() //@todo for structs, we have to check the number of arguments to be equal to the // number of non-mapping members if (m_arguments.size() != 1) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("More than one argument for " + "explicit type conersion.")); if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType())) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Explicit type conversion not " + "allowed.")); m_type = type->getActualType(); } else if (category == Type::Category::FUNCTION) { //@todo would be nice to create a struct type from the arguments @@ -382,10 +389,12 @@ ptr FunctionCall::checkTypeRequirements() FunctionDefinition const& fun = function->getFunction(); vecptr const& parameters = fun.getParameters(); if (parameters.size() != m_arguments.size()) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for " + "function call.")); for (size_t i = 0; i < m_arguments.size(); ++i) { if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) - throw std::exception(); + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Invalid type for argument in " + "function call.")); } // @todo actually the return type should be an anonymous struct, @@ -395,7 +404,7 @@ ptr FunctionCall::checkTypeRequirements() else m_type = fun.getReturnParameterList()->getParameters().front()->getType(); } else { - throw std::exception(); // type does not support invocation + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation.")); } return m_type; } @@ -428,7 +437,8 @@ ptr Identifier::checkTypeRequirements() VariableDeclaration* variable = dynamic_cast(m_referencedDeclaration); if (variable != nullptr) { if (variable->getType().get() == nullptr) - throw std::exception(); // variable used before type could be determined + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Variable referenced before type " + "could be determined.")); m_type = variable->getType(); return m_type; } @@ -452,7 +462,7 @@ ptr Identifier::checkTypeRequirements() m_type = std::make_shared(std::make_shared(*contractDef)); return m_type; } - throw std::exception(); // declaration reference of unknown/forbidden type + BOOST_ASSERT(false); // declaration reference of unknown/forbidden type return m_type; } diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h new file mode 100644 index 000000000..5e0b1522b --- /dev/null +++ b/libsolidity/Exceptions.h @@ -0,0 +1,34 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Solidity exception hierarchy. + */ + +#pragma once + +#include + +namespace dev { +namespace solidity { + +struct ParserError : virtual Exception {}; +struct TypeError : virtual Exception {}; +struct DeclarationError : virtual Exception {}; + +} } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 33b550eb6..8e769c475 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -23,6 +23,7 @@ #include #include +#include #include namespace dev { @@ -115,7 +116,7 @@ void NameAndTypeResolver::resolveReferencesInFunction(ParameterList& _returnPara virtual bool visit(Identifier& _identifier) override { Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); if (declaration == nullptr) - throw std::exception(); // @todo + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); _identifier.setReferencedDeclaration(*declaration); return false; } @@ -151,10 +152,11 @@ void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDecl virtual bool visit(UserDefinedTypeName& _typeName) override { Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); if (declaration == nullptr) - throw std::exception(); // @todo + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); StructDefinition* referencedStruct = dynamic_cast(declaration); + //@todo later, contracts are also valid types if (referencedStruct == nullptr) - throw std::exception(); // @todo we only allow structs as user defined types (later also contracts) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Identifier does not name a type name.")); _typeName.setReferencedStruct(*referencedStruct); return false; } @@ -176,7 +178,7 @@ void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDecl void NameAndTypeResolver::registerDeclaration(Declaration& _declaration) { if (!m_currentScope->registerDeclaration(_declaration)) - throw std::exception(); // @todo + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared.")); } Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index ab72bf7fc..b28154112 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -20,10 +20,11 @@ * Solidity parser. */ -#include "libdevcore/Log.h" -#include "libsolidity/BaseTypes.h" -#include "libsolidity/Parser.h" -#include "libsolidity/Scanner.h" +#include +#include +#include +#include +#include namespace dev { namespace solidity { @@ -530,16 +531,17 @@ ptr Parser::getLiteralAndAdvance() void Parser::throwExpectationError(const std::string& _description) { + //@todo put some of this stuff into ParserError int line, column; std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); - cwarn << "Solidity parser error: " << _description - << "at line " << (line + 1) - << ", column " << (column + 1); - cwarn << m_scanner->getLineAtPosition(getPosition()); - cwarn << std::string(column, ' ') << "^"; - - /// @todo make a proper exception hierarchy - throw std::exception(); + std::stringstream buf; + buf << "Solidity parser error: " << _description + << " at line " << (line + 1) + << ", column " << (column + 1) << "\n" + << m_scanner->getLineAtPosition(getPosition()) << "\n" + << std::string(column, ' ') << "^"; + + BOOST_THROW_EXCEPTION(ParserError() << errinfo_comment(buf.str())); } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 5a938e461..c9817eb46 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include namespace dev { @@ -60,7 +61,7 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) " uint256 variable;\n" " uint128 variable;\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } BOOST_AUTO_TEST_CASE(double_function_declaration) @@ -69,7 +70,7 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) " function fun() { var x; }\n" " function fun() { var x; }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } BOOST_AUTO_TEST_CASE(double_variable_declaration) @@ -77,7 +78,7 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) char const* text = "contract test {\n" " function f() { uint256 x; if (true) { uint256 x; } }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } BOOST_AUTO_TEST_CASE(name_shadowing) @@ -104,7 +105,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name) " uint256 variable;\n" " function f(uint256 arg) { f(notfound); }" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); + BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 86d09f170..b1f27bcb6 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace dev { @@ -54,8 +55,7 @@ BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration) char const* text = "contract test {\n" " uint256 ;\n" "}\n"; - cwarn << "The next error is expected."; - BOOST_CHECK_THROW(parseText(text), std::exception); + BOOST_CHECK_THROW(parseText(text), ParserError); } BOOST_AUTO_TEST_CASE(empty_function) From df43644111ae8baa57c2fcb5666f5516e607b556 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 15 Oct 2014 15:54:41 +0200 Subject: [PATCH 15/34] Some fixes for the type system, should be quite usable now. --- libsolidity/AST.cpp | 2 +- libsolidity/NameAndTypeResolver.cpp | 268 ++++++++++++------------- libsolidity/NameAndTypeResolver.h | 56 ++++-- libsolidity/Types.cpp | 28 ++- test/solidityNameAndTypeResolution.cpp | 58 ++++++ 5 files changed, 253 insertions(+), 159 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 926f1c685..4fb4c770f 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -354,7 +354,7 @@ ptr BinaryOperation::checkTypeRequirements() } else { BOOST_ASSERT(Token::IsBinaryOp(m_operator)); m_type = m_commonType; - if (!m_commonType->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_operator))) + if (!m_commonType->acceptsBinaryOperator(m_operator)) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); } return m_type; diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 8e769c475..7208d7ac2 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -30,24 +30,6 @@ namespace dev { namespace solidity { -class NameAndTypeResolver::ScopeHelper { -public: - ScopeHelper(NameAndTypeResolver& _resolver, Declaration& _declaration) - : m_resolver(_resolver) - { - m_resolver.registerDeclaration(_declaration); - m_resolver.enterNewSubScope(_declaration); - } - ~ScopeHelper() - { - m_resolver.closeCurrentScope(); - } - -private: - NameAndTypeResolver& m_resolver; -}; - - NameAndTypeResolver::NameAndTypeResolver() { } @@ -55,130 +37,33 @@ NameAndTypeResolver::NameAndTypeResolver() void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { reset(); + DeclarationRegistrationHelper registrar(m_scopes, _contract); - handleContract(_contract); -} - -void NameAndTypeResolver::handleContract(ContractDefinition& _contract) -{ - ScopeHelper scopeHelper(*this, _contract); + m_currentScope = &m_scopes[&_contract]; - // @todo structs (definition and usage) + //@todo structs for (ptr const& variable : _contract.getStateVariables()) - registerVariableDeclarationAndResolveType(*variable); + ReferencesResolver resolver(*variable, *this, nullptr); - for (ptr const& function : _contract.getDefinedFunctions()) - handleFunction(*function); + for (ptr const& function : _contract.getDefinedFunctions()) { + m_currentScope = &m_scopes[function.get()]; + ReferencesResolver referencesResolver(*function, *this, + function->getReturnParameterList().get()); + } + // First, all function parameter types need to be resolved before we can check + // the types, since it is possible to call functions that are only defined later + // in the source. + for (ptr const& function : _contract.getDefinedFunctions()) { + m_currentScope = &m_scopes[function.get()]; + function->getBody().checkTypeRequirements(); + } } void NameAndTypeResolver::reset() { m_scopes.clear(); - m_globalScope = Scope(); - m_currentScope = &m_globalScope; -} - -void NameAndTypeResolver::handleFunction(FunctionDefinition& _function) -{ - ScopeHelper scopeHelper(*this, _function); - - registerVariablesInFunction(_function); - resolveReferencesInFunction(*_function.getReturnParameterList(), _function.getBody()); - _function.getBody().checkTypeRequirements(); -} - -void NameAndTypeResolver::registerVariablesInFunction(FunctionDefinition& _function) -{ - class VariableDeclarationFinder : public ASTVisitor { - public: - VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} - virtual bool visit(VariableDeclaration& _variable) override { - m_resolver.registerVariableDeclarationAndResolveType(_variable); - return false; - } - private: - NameAndTypeResolver& m_resolver; - }; - - VariableDeclarationFinder declarationFinder(*this); - _function.accept(declarationFinder); -} - -void NameAndTypeResolver::resolveReferencesInFunction(ParameterList& _returnParameters, - Block& _functionBody) -{ - class ReferencesResolver : public ASTVisitor { - public: - ReferencesResolver(NameAndTypeResolver& _resolver, - ParameterList& _returnParameters) - : m_resolver(_resolver), m_returnParameters(_returnParameters) {} - virtual bool visit(Identifier& _identifier) override { - Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); - if (declaration == nullptr) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); - _identifier.setReferencedDeclaration(*declaration); - return false; - } - virtual bool visit(Return& _return) override { - _return.setFunctionReturnParameters(m_returnParameters); - return true; - } - private: - NameAndTypeResolver& m_resolver; - ParameterList& m_returnParameters; - }; - - ReferencesResolver referencesResolver(*this, _returnParameters); - _functionBody.accept(referencesResolver); -} - -void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDeclaration& _variable) -{ - registerDeclaration(_variable); - TypeName* typeName = _variable.getTypeName(); - if (typeName == nullptr) // unknown type, to be resolved by first assignment - return; - - // walk the AST to resolve user defined type references - // (walking is necessory because of mappings) - // @todo this could probably also be done at an earlier stage where we anyway - // walk the AST - - class UserDefinedTypeNameResolver : public ASTVisitor { - public: - UserDefinedTypeNameResolver(NameAndTypeResolver& _resolver) - : m_resolver(_resolver) {} - virtual bool visit(UserDefinedTypeName& _typeName) override { - Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); - if (declaration == nullptr) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); - StructDefinition* referencedStruct = dynamic_cast(declaration); - //@todo later, contracts are also valid types - if (referencedStruct == nullptr) - BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Identifier does not name a type name.")); - _typeName.setReferencedStruct(*referencedStruct); - return false; - } - virtual bool visit(Mapping&) override { - // @todo - return true; - } - private: - NameAndTypeResolver& m_resolver; - }; - - UserDefinedTypeNameResolver resolver(*this); - _variable.accept(resolver); - - _variable.setType(typeName->toType()); -} - - -void NameAndTypeResolver::registerDeclaration(Declaration& _declaration) -{ - if (!m_currentScope->registerDeclaration(_declaration)) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared.")); + m_currentScope = nullptr; } Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) @@ -186,18 +71,131 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name return m_currentScope->resolveName(_name, _recursive); } -void NameAndTypeResolver::enterNewSubScope(ASTNode& _node) + +DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map& _scopes, ASTNode& _astRoot) + : m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) +{ + _astRoot.accept(*this); +} + +bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) +{ + registerDeclaration(_contract, true); + return true; +} + +void DeclarationRegistrationHelper::endVisit(ContractDefinition&) +{ + closeCurrentScope(); +} + +bool DeclarationRegistrationHelper::visit(StructDefinition& _struct) +{ + registerDeclaration(_struct, true); + return true; +} + +void DeclarationRegistrationHelper::endVisit(StructDefinition&) +{ + closeCurrentScope(); +} + +bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function) +{ + registerDeclaration(_function, true); + return true; +} + +void DeclarationRegistrationHelper::endVisit(FunctionDefinition&) +{ + closeCurrentScope(); +} + +bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) +{ + registerDeclaration(_declaration, false); + return true; +} + +void DeclarationRegistrationHelper::endVisit(VariableDeclaration&) { - decltype(m_scopes)::iterator iter; +} + +void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node) +{ + std::map::iterator iter; bool newlyAdded; std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope)); BOOST_ASSERT(newlyAdded); m_currentScope = &iter->second; } -void NameAndTypeResolver::closeCurrentScope() +void DeclarationRegistrationHelper::closeCurrentScope() { + BOOST_ASSERT(m_currentScope != nullptr); m_currentScope = m_currentScope->getOuterScope(); } +void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) +{ + BOOST_ASSERT(m_currentScope != nullptr); + if (!m_currentScope->registerDeclaration(_declaration)) + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared.")); + + if (_opensScope) + enterNewSubScope(_declaration); +} + +ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, + ParameterList* _returnParameters) + : m_resolver(_resolver), m_returnParameters(_returnParameters) +{ + _root.accept(*this); +} + +void ReferencesResolver::endVisit(VariableDeclaration& _variable) +{ + // endVisit because the internal type needs resolving if it is a user defined type + // or mapping + if (_variable.getTypeName() != nullptr) + _variable.setType(_variable.getTypeName()->toType()); + // otherwise we have a "var"-declaration whose type is resolved by the first assignment +} + +bool ReferencesResolver::visit(Return& _return) +{ + BOOST_ASSERT(m_returnParameters != nullptr); + _return.setFunctionReturnParameters(*m_returnParameters); + return true; +} + +bool ReferencesResolver::visit(Mapping&) +{ + // @todo + return true; +} + +bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) +{ + Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); + if (declaration == nullptr) + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); + StructDefinition* referencedStruct = dynamic_cast(declaration); + //@todo later, contracts are also valid types + if (referencedStruct == nullptr) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Identifier does not name a type name.")); + _typeName.setReferencedStruct(*referencedStruct); + return false; +} + +bool ReferencesResolver::visit(Identifier& _identifier) +{ + Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); + if (declaration == nullptr) + BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); + _identifier.setReferencedDeclaration(*declaration); + return false; +} + + } } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 036c3fba2..f5a3c84ea 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -32,34 +32,64 @@ namespace dev { namespace solidity { + class NameAndTypeResolver : private boost::noncopyable { public: NameAndTypeResolver(); void resolveNamesAndTypes(ContractDefinition& _contract); + Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); private: - class ScopeHelper; //< RIIA helper to open and close scopes - void reset(); - void handleContract(ContractDefinition& _contract); - void handleFunction(FunctionDefinition& _function); - void registerVariablesInFunction(FunctionDefinition& _function); - void resolveReferencesInFunction(ParameterList& _returnParameters, - Block& _functionBody); + //! Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and + //! StructDefinition (@todo not yet implemented), where nullptr denotes the global scope. + std::map m_scopes; - void registerVariableDeclarationAndResolveType(VariableDeclaration& _variable); - void registerDeclaration(Declaration& _declaration); - Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + Scope* m_currentScope; +}; + +//! Traverses the given AST upon construction and fills _scopes with all declarations inside the +//! AST. +class DeclarationRegistrationHelper : private ASTVisitor +{ +public: + DeclarationRegistrationHelper(std::map& _scopes, ASTNode& _astRoot); + +private: + bool visit(ContractDefinition& _contract); + void endVisit(ContractDefinition& _contract); + bool visit(StructDefinition& _struct); + void endVisit(StructDefinition& _struct); + bool visit(FunctionDefinition& _function); + void endVisit(FunctionDefinition& _function); + bool visit(VariableDeclaration& _declaration); + void endVisit(VariableDeclaration& _declaration); void enterNewSubScope(ASTNode& _node); void closeCurrentScope(); + void registerDeclaration(Declaration& _declaration, bool _opensScope); - Scope m_globalScope; // not part of the map - std::map m_scopes; - + std::map& m_scopes; Scope* m_currentScope; }; +//! Resolves references to declarations (of variables and types) and also establishes the link +//! between a return statement and the return parameter list. +class ReferencesResolver : private ASTVisitor +{ +public: + ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ParameterList* _returnParameters); +private: + virtual void endVisit(VariableDeclaration& _variable) override; + virtual bool visit(Identifier& _identifier) override; + virtual bool visit(UserDefinedTypeName& _typeName) override; + virtual bool visit(Mapping&) override; + virtual bool visit(Return& _return) override; +private: + NameAndTypeResolver& m_resolver; + ParameterList* m_returnParameters; +}; + } } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index ee0a653fb..8e8a9d979 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -37,8 +37,8 @@ ptr Type::fromElementaryTypeName(Token::Value _typeToken) bits = (1 << (bits - 1)) * 32; int modifier = offset / 5; return std::make_shared(bits, - modifier == 0 ? IntegerType::Modifier::UNSIGNED : - modifier == 1 ? IntegerType::Modifier::SIGNED : + modifier == 0 ? IntegerType::Modifier::SIGNED : + modifier == 1 ? IntegerType::Modifier::UNSIGNED : IntegerType::Modifier::HASH); } else if (_typeToken == Token::ADDRESS) { return std::make_shared(0, IntegerType::Modifier::ADDRESS); @@ -109,26 +109,34 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(const Type& _convertTo) const { - // @todo - return false; + return _convertTo.getCategory() == Category::INTEGER; } bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const { - //@todo - return true; + if (isAddress()) { + return Token::IsCompareOp(_operator); + } else if (isHash()) { + return Token::IsCompareOp(_operator) || Token::IsBitOp(_operator); + } else { + return true; + } } bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const { - //@todo - return true; + return _operator == Token::DELETE || (!isAddress() && _operator == Token::BIT_NOT); } bool BoolType::isExplicitlyConvertibleTo(const Type& _convertTo) const { - //@todo conversion to integer is fine, but not to address - //@todo this is an example of explicit conversions being not transitive (though implicit should) + // conversion to integer is fine, but not to address + // this is an example of explicit conversions being not transitive (though implicit should be) + if (_convertTo.getCategory() == Category::INTEGER) { + IntegerType const& convertTo = dynamic_cast(_convertTo); + if (!convertTo.isAddress()) + return true; + } return isImplicitlyConvertibleTo(_convertTo); } diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index c9817eb46..d2f99e83e 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -108,6 +108,64 @@ BOOST_AUTO_TEST_CASE(undeclared_name) BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); } +BOOST_AUTO_TEST_CASE(reference_to_later_declaration) +{ + char const* text = "contract test {\n" + " function g() { f(); }" + " function f() { }" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(type_inference_smoke_test) +{ + char const* text = "contract test {\n" + " function f(uint256 arg1, uint32 arg2) returns (bool ret) { var x = arg1 + arg2 == 8; ret = x; }" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(type_checking_return) +{ + char const* text = "contract test {\n" + " function f() returns (bool r) { return 1 >= 2; }" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number) +{ + char const* text = "contract test {\n" + " function f() returns (bool r1, bool r2) { return 1 >= 2; }" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) +{ + char const* text = "contract test {\n" + " function f() returns (uint256 r) { return 1 >= 2; }" + "}\n"; + BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); +} + +BOOST_AUTO_TEST_CASE(type_checking_function_call) +{ + char const* text = "contract test {\n" + " function f() returns (bool r) { return g(12, true) == 3; }\n" + " function g(uint256 a, bool b) returns (uint256 r) { }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + +BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) +{ + char const* text = "contract test {\n" + " function f() returns (int256 r) { var x = int256(uint32(2)); return x; }" + "}\n"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_SUITE_END() } } } // end namespaces From 77852f5f0af793f1eaab19ff59769233f0b4d605 Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 15 Oct 2014 18:50:15 +0200 Subject: [PATCH 16/34] Remove unused parameters. --- libsolidity/Types.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 50d00a549..f73357a2f 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -61,10 +61,10 @@ public: static ptr forLiteral(Literal const& _literal); virtual Category getCategory() const = 0; - virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const { return false; } + virtual bool isImplicitlyConvertibleTo(const Type&) const { return false; } virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const { return isImplicitlyConvertibleTo(_convertTo); } - virtual bool acceptsBinaryOperator(Token::Value _operator) const { return false; } - virtual bool acceptsUnaryOperator(Token::Value _operator) const { return false; } + virtual bool acceptsBinaryOperator(Token::Value) const { return false; } + virtual bool acceptsUnaryOperator(Token::Value) const { return false; } }; class IntegerType : public Type From dea94c669bbfe28b2fa360c167cefcc1a63bdef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 13:49:13 +0200 Subject: [PATCH 17/34] Throw BadInstruction exception in FeeStructure::getInstructionFee() when instruction code is ivalid --- libevm/FeeStructure.cpp | 8 ++++++-- libevm/FeeStructure.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index 661f7af6f..aeeb19e27 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -24,18 +24,22 @@ #include +#include "VM.h" + namespace dev { namespace eth { -uint32_t FeeStructure::getInstructionFee(Instruction _inst) BOOST_NOEXCEPT_OR_NOTHROW +uint32_t FeeStructure::getInstructionFee(Instruction _inst) { switch (_inst) { + default: + BOOST_THROW_EXCEPTION(BadInstruction()); + case Instruction::STOP: case Instruction::SUICIDE: - default: // In case of bad instruction return 0 return 0; case Instruction::SSTORE: diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 73ea48156..8591afa14 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -45,8 +45,8 @@ struct FeeStructure static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. /// Returns step fee of the instruction. - /// In case of bad instruction code, returns 0. - static uint32_t getInstructionFee(Instruction _inst) BOOST_NOEXCEPT_OR_NOTHROW; + /// In case of bad instruction code, throws BadInstruction exception. + static uint32_t getInstructionFee(Instruction _inst); }; } From 8080038691def2d3459603040fad6d3f37e63942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 13:50:28 +0200 Subject: [PATCH 18/34] Revert "Throw bad instruction exception in case instruction is bad" This reverts commit ddb6392338047ea565dbb44ffbd6064d35dfc9dc. --- libevm/VM.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libevm/VM.h b/libevm/VM.h index a1cb3eccb..7c1e66ed5 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -188,10 +188,6 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con // EXECUTE... switch (inst) { - default: - BOOST_THROW_EXCEPTION(BadInstruction()); - break; - case Instruction::ADD: //pops two items and pushes S[-1] + S[-2] mod 2^256. require(2); From b01d1ea8a306e6b8e4b9d659224c77e88e699d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 13:52:32 +0200 Subject: [PATCH 19/34] Add comment about BadInstruction exception --- libevm/VM.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevm/VM.h b/libevm/VM.h index 7c1e66ed5..e97e5c4eb 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -101,7 +101,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con Instruction inst = (Instruction)_ext.getCode(m_curPC); // FEES... - bigint runGas = FeeStructure::getInstructionFee(inst); + bigint runGas = FeeStructure::getInstructionFee(inst); // throws BadInstruction bigint newTempSize = m_temp.size(); switch (inst) { From 68a85f4f809c9981d4ac23f3d7dbcc2bc05b17dd Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 16 Oct 2014 14:08:54 +0200 Subject: [PATCH 20/34] Corrected coding style. --- libsolidity/AST.cpp | 143 ++-- libsolidity/AST.h | 37 +- libsolidity/ASTForward.h | 31 +- libsolidity/ASTPrinter.cpp | 46 +- libsolidity/ASTPrinter.h | 34 +- libsolidity/ASTVisitor.h | 34 +- libsolidity/BaseTypes.h | 34 +- libsolidity/Exceptions.h | 31 +- libsolidity/NameAndTypeResolver.cpp | 44 +- libsolidity/NameAndTypeResolver.h | 31 +- libsolidity/Parser.cpp | 288 ++++---- libsolidity/Parser.h | 31 +- libsolidity/Scanner.cpp | 911 ++++++++++++------------- libsolidity/Scanner.h | 98 ++- libsolidity/Scope.cpp | 33 +- libsolidity/Scope.h | 31 +- libsolidity/Token.cpp | 21 +- libsolidity/Token.h | 194 +++--- libsolidity/Types.cpp | 64 +- libsolidity/Types.h | 58 +- solc/main.cpp | 24 +- test/solidityNameAndTypeResolution.cpp | 32 +- test/solidityParser.cpp | 26 +- test/solidityScanner.cpp | 155 +++-- 24 files changed, 1273 insertions(+), 1158 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 4fb4c770f..355ff1bd1 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -26,12 +26,15 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ void ContractDefinition::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { listAccept(m_definedStructs, _visitor); listAccept(m_stateVariables, _visitor); listAccept(m_definedFunctions, _visitor); @@ -41,7 +44,8 @@ void ContractDefinition::accept(ASTVisitor& _visitor) void StructDefinition::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { listAccept(m_members, _visitor); } _visitor.endVisit(*this); @@ -49,7 +53,8 @@ void StructDefinition::accept(ASTVisitor& _visitor) void ParameterList::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { listAccept(m_parameters, _visitor); } _visitor.endVisit(*this); @@ -57,7 +62,8 @@ void ParameterList::accept(ASTVisitor& _visitor) void FunctionDefinition::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_parameters->accept(_visitor); if (m_returnParameters) m_returnParameters->accept(_visitor); @@ -68,7 +74,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) void VariableDeclaration::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { if (m_typeName) m_typeName->accept(_visitor); } @@ -95,7 +102,8 @@ void UserDefinedTypeName::accept(ASTVisitor& _visitor) void Mapping::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_keyType->accept(_visitor); m_valueType->accept(_visitor); } @@ -110,7 +118,8 @@ void Statement::accept(ASTVisitor& _visitor) void Block::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { listAccept(m_statements, _visitor); } _visitor.endVisit(*this); @@ -118,7 +127,8 @@ void Block::accept(ASTVisitor& _visitor) void IfStatement::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_condition->accept(_visitor); m_trueBody->accept(_visitor); if (m_falseBody) @@ -135,7 +145,8 @@ void BreakableStatement::accept(ASTVisitor& _visitor) void WhileStatement::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_condition->accept(_visitor); m_body->accept(_visitor); } @@ -156,7 +167,8 @@ void Break::accept(ASTVisitor& _visitor) void Return::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { if (m_expression) m_expression->accept(_visitor); } @@ -165,7 +177,8 @@ void Return::accept(ASTVisitor& _visitor) void VariableDefinition::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_variable->accept(_visitor); if (m_value) m_value->accept(_visitor); @@ -175,7 +188,8 @@ void VariableDefinition::accept(ASTVisitor& _visitor) void Assignment::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_leftHandSide->accept(_visitor); m_rightHandSide->accept(_visitor); } @@ -184,7 +198,8 @@ void Assignment::accept(ASTVisitor& _visitor) void UnaryOperation::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_subExpression->accept(_visitor); } _visitor.endVisit(*this); @@ -192,7 +207,8 @@ void UnaryOperation::accept(ASTVisitor& _visitor) void BinaryOperation::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_left->accept(_visitor); m_right->accept(_visitor); } @@ -201,7 +217,8 @@ void BinaryOperation::accept(ASTVisitor& _visitor) void FunctionCall::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_expression->accept(_visitor); listAccept(m_arguments, _visitor); } @@ -210,7 +227,8 @@ void FunctionCall::accept(ASTVisitor& _visitor) void MemberAccess::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_expression->accept(_visitor); } _visitor.endVisit(*this); @@ -218,7 +236,8 @@ void MemberAccess::accept(ASTVisitor& _visitor) void IndexAccess::accept(ASTVisitor& _visitor) { - if (_visitor.visit(*this)) { + if (_visitor.visit(*this)) + { m_base->accept(_visitor); m_index->accept(_visitor); } @@ -253,7 +272,7 @@ void Statement::expectType(Expression& _expression, const Type& _expectedType) ptr Block::checkTypeRequirements() { - for (ptr const& statement : m_statements) + for (ptr const & statement : m_statements) statement->checkTypeRequirements(); return ptr(); } @@ -292,7 +311,6 @@ ptr Return::checkTypeRequirements() "declaration.")); // this could later be changed such that the paramaters type is an anonymous struct type, // but for now, we only allow one return parameter - expectType(*m_expression, *m_returnParameters->getParameters().front()->getType()); return ptr(); } @@ -303,10 +321,14 @@ ptr VariableDefinition::checkTypeRequirements() // setsthe type. // Note that assignments before the first declaration are legal because of the special scoping // rules inherited from JavaScript. - if (m_value) { - if (m_variable->getType()) { + if (m_value) + { + if (m_variable->getType()) + { expectType(*m_value, *m_variable->getType()); - } else { + } + else + { // no type declared and no previous assignment, infer the type m_variable->setType(m_value->checkTypeRequirements()); } @@ -320,7 +342,8 @@ ptr Assignment::checkTypeRequirements() // add a feature to the type system to check that expectType(*m_rightHandSide, *m_leftHandSide->checkTypeRequirements()); m_type = m_leftHandSide->getType(); - if (m_assigmentOperator != Token::ASSIGN) { + if (m_assigmentOperator != Token::ASSIGN) + { // complex assignment if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); @@ -341,18 +364,19 @@ ptr BinaryOperation::checkTypeRequirements() { m_right->checkTypeRequirements(); m_left->checkTypeRequirements(); - if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) m_commonType = m_left->getType(); else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) m_commonType = m_right->getType(); else BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation.")); - - if (Token::IsCompareOp(m_operator)) { + if (Token::isCompareOp(m_operator)) + { m_type = std::make_shared(); - } else { - BOOST_ASSERT(Token::IsBinaryOp(m_operator)); + } + else + { + BOOST_ASSERT(Token::isBinaryOp(m_operator)); m_type = m_commonType; if (!m_commonType->acceptsBinaryOperator(m_operator)) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); @@ -363,12 +387,12 @@ ptr BinaryOperation::checkTypeRequirements() ptr FunctionCall::checkTypeRequirements() { m_expression->checkTypeRequirements(); - for (ptr const& argument : m_arguments) + for (ptr const & argument : m_arguments) argument->checkTypeRequirements(); - ptr expressionType = m_expression->getType(); Type::Category const category = expressionType->getCategory(); - if (category == Type::Category::TYPE) { + if (category == Type::Category::TYPE) + { TypeType* type = dynamic_cast(expressionType.get()); BOOST_ASSERT(type != nullptr); //@todo for structs, we have to check the number of arguments to be equal to the @@ -378,9 +402,11 @@ ptr FunctionCall::checkTypeRequirements() "explicit type conersion.")); if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType())) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Explicit type conversion not " - "allowed.")); + "allowed.")); m_type = type->getActualType(); - } else if (category == Type::Category::FUNCTION) { + } + else if (category == Type::Category::FUNCTION) + { //@todo would be nice to create a struct type from the arguments // and then ask if that is implicitly convertible to the struct represented by the // function parameters @@ -390,20 +416,22 @@ ptr FunctionCall::checkTypeRequirements() vecptr const& parameters = fun.getParameters(); if (parameters.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for " - "function call.")); - for (size_t i = 0; i < m_arguments.size(); ++i) { + "function call.")); + for (size_t i = 0; i < m_arguments.size(); ++i) + { if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Invalid type for argument in " "function call.")); } - // @todo actually the return type should be an anonymous struct, // but we change it to the type of the first return value until we have structs if (fun.getReturnParameterList()->getParameters().empty()) m_type = std::make_shared(); else m_type = fun.getReturnParameterList()->getParameters().front()->getType(); - } else { + } + else + { BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation.")); } return m_type; @@ -435,7 +463,8 @@ ptr Identifier::checkTypeRequirements() // var y = x; // the type of x is not yet determined. VariableDeclaration* variable = dynamic_cast(m_referencedDeclaration); - if (variable != nullptr) { + if (variable != nullptr) + { if (variable->getType().get() == nullptr) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Variable referenced before type " "could be determined.")); @@ -444,13 +473,15 @@ ptr Identifier::checkTypeRequirements() } //@todo can we unify these with TypeName::toType()? StructDefinition* structDef = dynamic_cast(m_referencedDeclaration); - if (structDef != nullptr) { + if (structDef != nullptr) + { // note that we do not have a struct type here m_type = std::make_shared(std::make_shared(*structDef)); return m_type; } FunctionDefinition* functionDef = dynamic_cast(m_referencedDeclaration); - if (functionDef != nullptr) { + if (functionDef != nullptr) + { // a function reference is not a TypeType, because calling a TypeType converts to the type. // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type // conversion. @@ -458,7 +489,8 @@ ptr Identifier::checkTypeRequirements() return m_type; } ContractDefinition* contractDef = dynamic_cast(m_referencedDeclaration); - if (contractDef != nullptr) { + if (contractDef != nullptr) + { m_type = std::make_shared(std::make_shared(*contractDef)); return m_type; } @@ -478,4 +510,5 @@ ptr Literal::checkTypeRequirements() return m_type; } -} } +} +} diff --git a/libsolidity/AST.h b/libsolidity/AST.h index c1509b85d..a289455cb 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -33,8 +33,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ class ASTVisitor; @@ -49,8 +51,10 @@ public: virtual void accept(ASTVisitor& _visitor) = 0; template - static void listAccept(vecptr& _list, ASTVisitor& _visitor) { - for (ptr& element : _list) element->accept(_visitor); + static void listAccept(vecptr& _list, ASTVisitor& _visitor) + { + for (ptr& element : _list) + element->accept(_visitor); } Location const& getLocation() const { return m_location; } @@ -521,4 +525,5 @@ private: /// @} -} } +} +} diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 4963776b3..0397cadda 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -28,8 +28,10 @@ // Forward-declare all AST node types -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ class ASTNode; class Declaration; @@ -74,4 +76,5 @@ using vecptr = std::vector>; using ASTString = std::string; -} } +} +} diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index bbaa2e0af..7c48dacb3 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -23,8 +23,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ ASTPrinter::ASTPrinter(ptr _ast, const std::string& _source) : m_indentation(0), m_source(_source), m_ast(_ast) @@ -85,7 +87,7 @@ bool ASTPrinter::visit(TypeName& _node) bool ASTPrinter::visit(ElementaryTypeName& _node) { - writeLine(std::string("ElementaryTypeName ") + Token::String(_node.getType())); + writeLine(std::string("ElementaryTypeName ") + Token::toString(_node.getType())); printSourcePart(_node); return goDeeper(); } @@ -176,7 +178,7 @@ bool ASTPrinter::visit(Expression& _node) bool ASTPrinter::visit(Assignment& _node) { - writeLine(std::string("Assignment using operator ") + Token::String(_node.getAssignmentOperator())); + writeLine(std::string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); printSourcePart(_node); return goDeeper(); } @@ -184,14 +186,14 @@ bool ASTPrinter::visit(Assignment& _node) bool ASTPrinter::visit(UnaryOperation& _node) { writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + - ") " + Token::String(_node.getOperator())); + ") " + Token::toString(_node.getOperator())); printSourcePart(_node); return goDeeper(); } bool ASTPrinter::visit(BinaryOperation& _node) { - writeLine(std::string("BinaryOperation using operator ") + Token::String(_node.getOperator())); + writeLine(std::string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); printSourcePart(_node); return goDeeper(); } @@ -233,14 +235,14 @@ bool ASTPrinter::visit(Identifier& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) { - writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getTypeToken())); + writeLine(std::string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); printSourcePart(_node); return goDeeper(); } bool ASTPrinter::visit(Literal& _node) { - const char* tokenString = Token::String(_node.getToken()); + const char* tokenString = Token::toString(_node.getToken()); if (tokenString == nullptr) tokenString = "----"; writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); @@ -402,7 +404,8 @@ void ASTPrinter::endVisit(Literal&) void ASTPrinter::printSourcePart(ASTNode const& _node) { - if (!m_source.empty()) { + if (!m_source.empty()) + { Location const& location(_node.getLocation()); *m_ostream << getIndentation() << " Source: |" << m_source.substr(location.start, location.end - location.start) << "|\n"; @@ -419,4 +422,5 @@ void ASTPrinter::writeLine(const std::string& _line) *m_ostream << getIndentation() << _line << '\n'; } -} } +} +} diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 6a864f38a..52f349919 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -25,8 +25,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ class ASTPrinter : public ASTVisitor { @@ -67,7 +69,7 @@ public: bool visit(ElementaryTypeNameExpression& _node) override; bool visit(Literal& _node) override; - void endVisit(ASTNode & _node) override; + void endVisit(ASTNode& _node) override; void endVisit(ContractDefinition&) override; void endVisit(StructDefinition&) override; void endVisit(ParameterList&) override; @@ -103,10 +105,12 @@ private: std::string getIndentation() const; void writeLine(std::string const& _line); bool goDeeper() { m_indentation++; return true; } + int m_indentation; std::string m_source; ptr m_ast; std::ostream* m_ostream; }; -} } +} +} diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index c28d2330f..72f287683 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -25,10 +25,13 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ -class ASTVisitor { +class ASTVisitor +{ public: /// These functions are called after a call to ASTNode::accept, /// first visit, then (if visit returns true) recursively for all @@ -97,4 +100,5 @@ public: virtual void endVisit(Literal&) { } }; -} } +} +} diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index 6d57b74bd..c8926b6a7 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -23,12 +23,15 @@ #pragma once -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ /// Representation of an interval of source positions. /// The interval includes start and excludes end. -struct Location { +struct Location +{ Location(int _start, int _end) : start(_start), end(_end) { } Location() : start(-1), end(-1) { } @@ -38,4 +41,5 @@ struct Location { int end; }; -} } +} +} diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h index 5e0b1522b..c14e0d797 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -24,11 +24,14 @@ #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ struct ParserError : virtual Exception {}; struct TypeError : virtual Exception {}; struct DeclarationError : virtual Exception {}; -} } +} +} diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 7208d7ac2..ada987b08 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -26,8 +26,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ NameAndTypeResolver::NameAndTypeResolver() @@ -38,15 +40,12 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { reset(); DeclarationRegistrationHelper registrar(m_scopes, _contract); - m_currentScope = &m_scopes[&_contract]; - //@todo structs - - for (ptr const& variable : _contract.getStateVariables()) + for (ptr const & variable : _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, nullptr); - - for (ptr const& function : _contract.getDefinedFunctions()) { + for (ptr const & function : _contract.getDefinedFunctions()) + { m_currentScope = &m_scopes[function.get()]; ReferencesResolver referencesResolver(*function, *this, function->getReturnParameterList().get()); @@ -54,7 +53,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) // First, all function parameter types need to be resolved before we can check // the types, since it is possible to call functions that are only defined later // in the source. - for (ptr const& function : _contract.getDefinedFunctions()) { + for (ptr const & function : _contract.getDefinedFunctions()) + { m_currentScope = &m_scopes[function.get()]; function->getBody().checkTypeRequirements(); } @@ -141,7 +141,6 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio BOOST_ASSERT(m_currentScope != nullptr); if (!m_currentScope->registerDeclaration(_declaration)) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared.")); - if (_opensScope) enterNewSubScope(_declaration); } @@ -198,4 +197,5 @@ bool ReferencesResolver::visit(Identifier& _identifier) } -} } +} +} diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index f5a3c84ea..346c9e644 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -29,8 +29,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ class NameAndTypeResolver : private boost::noncopyable @@ -92,4 +94,5 @@ private: ParameterList* m_returnParameters; }; -} } +} +} diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index b28154112..5a329ae71 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -26,13 +26,14 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ ptr Parser::parse(std::shared_ptr const& _scanner) { m_scanner = _scanner; - return parseContractDefinition(); } @@ -46,9 +47,15 @@ public: : m_parser(_parser), m_location(_parser.getPosition(), -1) {} - void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + void markEndPosition() + { + m_location.end = m_parser.getEndPosition(); + } - void setLocationEmpty() { m_location.end = m_location.start; } + void setLocationEmpty() + { + m_location.end = m_location.start; + } /// Set the end position to the one of the given node. void setEndPositionFromNode(const ptr& _node) @@ -58,7 +65,7 @@ public: /// @todo: check that this actually uses perfect forwarding template - ptr createNode(Args&&... _args) + ptr createNode(Args&& ... _args) { if (m_location.end < 0) markEndPosition(); @@ -84,62 +91,73 @@ int Parser::getEndPosition() const ptr Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::CONTRACT); ptr name = expectIdentifierToken(); expectToken(Token::LBRACE); - vecptr structs; vecptr stateVariables; vecptr functions; bool visibilityIsPublic = true; - while (true) { + while (true) + { Token::Value currentToken = m_scanner->getCurrentToken(); - if (currentToken == Token::RBRACE) { + if (currentToken == Token::RBRACE) + { break; - } else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) { + } + else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) + { visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); m_scanner->next(); expectToken(Token::COLON); - } else if (currentToken == Token::FUNCTION) { + } + else if (currentToken == Token::FUNCTION) + { functions.push_back(parseFunctionDefinition(visibilityIsPublic)); - } else if (currentToken == Token::STRUCT) { + } + else if (currentToken == Token::STRUCT) + { structs.push_back(parseStructDefinition()); - } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || - Token::IsElementaryTypeName(currentToken)) { + } + else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || + Token::isElementaryTypeName(currentToken)) + { bool const allowVar = false; stateVariables.push_back(parseVariableDeclaration(allowVar)); expectToken(Token::SEMICOLON); - } else { + } + else + { throwExpectationError("Function, variable or struct declaration expected."); } } nodeFactory.markEndPosition(); - expectToken(Token::RBRACE); expectToken(Token::EOS); - return nodeFactory.createNode(name, structs, stateVariables, functions); } ptr Parser::parseFunctionDefinition(bool _isPublic) { ASTNodeFactory nodeFactory(*this); - expectToken(Token::FUNCTION); ptr name(expectIdentifierToken()); ptr parameters(parseParameterList()); bool isDeclaredConst = false; - if (m_scanner->getCurrentToken() == Token::CONST) { + if (m_scanner->getCurrentToken() == Token::CONST) + { isDeclaredConst = true; m_scanner->next(); } ptr returnParameters; - if (m_scanner->getCurrentToken() == Token::RETURNS) { + if (m_scanner->getCurrentToken() == Token::RETURNS) + { const bool permitEmptyParameterList = false; m_scanner->next(); returnParameters = parseParameterList(permitEmptyParameterList); - } else { + } + else + { // create an empty parameter list at a zero-length location ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); @@ -148,32 +166,30 @@ ptr Parser::parseFunctionDefinition(bool _isPublic) ptr block = parseBlock(); nodeFactory.setEndPositionFromNode(block); return nodeFactory.createNode(name, _isPublic, parameters, - isDeclaredConst, returnParameters, block); + isDeclaredConst, returnParameters, block); } ptr Parser::parseStructDefinition() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::STRUCT); ptr name = expectIdentifierToken(); vecptr members; expectToken(Token::LBRACE); - while (m_scanner->getCurrentToken() != Token::RBRACE) { + while (m_scanner->getCurrentToken() != Token::RBRACE) + { bool const allowVar = false; members.push_back(parseVariableDeclaration(allowVar)); expectToken(Token::SEMICOLON); } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); - return nodeFactory.createNode(name, members); } ptr Parser::parseVariableDeclaration(bool _allowVar) { ASTNodeFactory nodeFactory(*this); - ptr type = parseTypeName(_allowVar); nodeFactory.markEndPosition(); return nodeFactory.createNode(type, expectIdentifierToken()); @@ -183,58 +199,63 @@ ptr Parser::parseTypeName(bool _allowVar) { ptr type; Token::Value token = m_scanner->getCurrentToken(); - if (Token::IsElementaryTypeName(token)) { + if (Token::isElementaryTypeName(token)) + { type = ASTNodeFactory(*this).createNode(token); m_scanner->next(); - } else if (token == Token::VAR) { + } + else if (token == Token::VAR) + { if (!_allowVar) throwExpectationError("Expected explicit type name."); m_scanner->next(); - } else if (token == Token::MAPPING) { + } + else if (token == Token::MAPPING) + { type = parseMapping(); - } else if (token == Token::IDENTIFIER) { + } + else if (token == Token::IDENTIFIER) + { ASTNodeFactory nodeFactory(*this); nodeFactory.markEndPosition(); type = nodeFactory.createNode(expectIdentifierToken()); - } else { + } + else + { throwExpectationError("Expected type name"); } - return type; } ptr Parser::parseMapping() { ASTNodeFactory nodeFactory(*this); - expectToken(Token::MAPPING); expectToken(Token::LPAREN); - - if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken())) + if (!Token::isElementaryTypeName(m_scanner->getCurrentToken())) throwExpectationError("Expected elementary type name for mapping key type"); ptr keyType; keyType = ASTNodeFactory(*this).createNode(m_scanner->getCurrentToken()); m_scanner->next(); - expectToken(Token::ARROW); bool const allowVar = false; ptr valueType = parseTypeName(allowVar); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); - return nodeFactory.createNode(keyType, valueType); } ptr Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); - vecptr parameters; expectToken(Token::LPAREN); - if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { + if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) + { bool const allowVar = false; parameters.push_back(parseVariableDeclaration(allowVar)); - while (m_scanner->getCurrentToken() != Token::RPAREN) { + while (m_scanner->getCurrentToken() != Token::RPAREN) + { expectToken(Token::COMMA); parameters.push_back(parseVariableDeclaration(allowVar)); } @@ -249,7 +270,8 @@ ptr Parser::parseBlock() ASTNodeFactory nodeFactory(*this); expectToken(Token::LBRACE); vecptr statements; - while (m_scanner->getCurrentToken() != Token::RBRACE) { + while (m_scanner->getCurrentToken() != Token::RBRACE) + { statements.push_back(parseStatement()); } nodeFactory.markEndPosition(); @@ -260,16 +282,15 @@ ptr Parser::parseBlock() ptr Parser::parseStatement() { ptr statement; - - switch (m_scanner->getCurrentToken()) { + switch (m_scanner->getCurrentToken()) + { case Token::IF: return parseIfStatement(); case Token::WHILE: return parseWhileStatement(); case Token::LBRACE: return parseBlock(); - - // starting from here, all statements must be terminated by a semicolon + // starting from here, all statements must be terminated by a semicolon case Token::CONTINUE: statement = ASTNodeFactory(*this).createNode(); break; @@ -277,28 +298,32 @@ ptr Parser::parseStatement() statement = ASTNodeFactory(*this).createNode(); break; case Token::RETURN: + { + ASTNodeFactory nodeFactory(*this); + ptr expression; + if (m_scanner->next() != Token::SEMICOLON) { - ASTNodeFactory nodeFactory(*this); - ptr expression; - if (m_scanner->next() != Token::SEMICOLON) { - expression = parseExpression(); - nodeFactory.setEndPositionFromNode(expression); - } - statement = nodeFactory.createNode(expression); + expression = parseExpression(); + nodeFactory.setEndPositionFromNode(expression); } - break; + statement = nodeFactory.createNode(expression); + } + break; default: // distinguish between variable definition (and potentially assignment) and expressions // (which include assignments to other expressions and pre-declared variables) // We have a variable definition if we ge a keyword that specifies a type name, or // in the case of a user-defined type, we have two identifiers following each other. if (m_scanner->getCurrentToken() == Token::MAPPING || - m_scanner->getCurrentToken() == Token::VAR || - Token::IsElementaryTypeName(m_scanner->getCurrentToken()) || - (m_scanner->getCurrentToken() == Token::IDENTIFIER && - m_scanner->peek() == Token::IDENTIFIER)) { + m_scanner->getCurrentToken() == Token::VAR || + Token::isElementaryTypeName(m_scanner->getCurrentToken()) || + (m_scanner->getCurrentToken() == Token::IDENTIFIER && + m_scanner->peek() == Token::IDENTIFIER)) + { statement = parseVariableDefinition(); - } else { + } + else + { // "ordinary" expression statement = parseExpression(); } @@ -316,11 +341,14 @@ ptr Parser::parseIfStatement() expectToken(Token::RPAREN); ptr trueBody = parseStatement(); ptr falseBody; - if (m_scanner->getCurrentToken() == Token::ELSE) { + if (m_scanner->getCurrentToken() == Token::ELSE) + { m_scanner->next(); falseBody = parseStatement(); nodeFactory.setEndPositionFromNode(falseBody); - } else { + } + else + { nodeFactory.setEndPositionFromNode(trueBody); } return nodeFactory.createNode(condition, trueBody, falseBody); @@ -344,11 +372,14 @@ ptr Parser::parseVariableDefinition() bool const allowVar = true; ptr variable = parseVariableDeclaration(allowVar); ptr value; - if (m_scanner->getCurrentToken() == Token::ASSIGN) { + if (m_scanner->getCurrentToken() == Token::ASSIGN) + { m_scanner->next(); value = parseExpression(); nodeFactory.setEndPositionFromNode(value); - } else { + } + else + { nodeFactory.setEndPositionFromNode(variable); } return nodeFactory.createNode(variable, value); @@ -358,9 +389,8 @@ ptr Parser::parseExpression() { ASTNodeFactory nodeFactory(*this); ptr expression = parseBinaryExpression(); - if (!Token::IsAssignmentOp(m_scanner->getCurrentToken())) + if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) return expression; - Token::Value assignmentOperator = expectAssignmentOperator(); ptr rightHandSide = parseExpression(); nodeFactory.setEndPositionFromNode(rightHandSide); @@ -371,9 +401,11 @@ ptr Parser::parseBinaryExpression(int _minPrecedence) { ASTNodeFactory nodeFactory(*this); ptr expression = parseUnaryExpression(); - int precedence = Token::Precedence(m_scanner->getCurrentToken()); - for (; precedence >= _minPrecedence; --precedence) { - while (Token::Precedence(m_scanner->getCurrentToken()) == precedence) { + int precedence = Token::precedence(m_scanner->getCurrentToken()); + for (; precedence >= _minPrecedence; --precedence) + { + while (Token::precedence(m_scanner->getCurrentToken()) == precedence) + { Token::Value op = m_scanner->getCurrentToken(); m_scanner->next(); ptr right = parseBinaryExpression(precedence + 1); @@ -388,17 +420,20 @@ ptr Parser::parseUnaryExpression() { ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); - if (Token::IsUnaryOp(token) || Token::IsCountOp(token)) { + if (Token::isUnaryOp(token) || Token::isCountOp(token)) + { // prefix expression m_scanner->next(); ptr subExpression = parseUnaryExpression(); nodeFactory.setEndPositionFromNode(subExpression); return nodeFactory.createNode(token, subExpression, true); - } else { + } + else + { // potential postfix expression ptr subExpression = parseLeftHandSideExpression(); token = m_scanner->getCurrentToken(); - if (!Token::IsCountOp(token)) + if (!Token::isCountOp(token)) return subExpression; nodeFactory.markEndPosition(); m_scanner->next(); @@ -410,34 +445,35 @@ ptr Parser::parseLeftHandSideExpression() { ASTNodeFactory nodeFactory(*this); ptr expression = parsePrimaryExpression(); - - while (true) { - switch (m_scanner->getCurrentToken()) { + while (true) + { + switch (m_scanner->getCurrentToken()) + { case Token::LBRACK: - { - m_scanner->next(); - ptr index = parseExpression(); - nodeFactory.markEndPosition(); - expectToken(Token::RBRACK); - expression = nodeFactory.createNode(expression, index); - } - break; + { + m_scanner->next(); + ptr index = parseExpression(); + nodeFactory.markEndPosition(); + expectToken(Token::RBRACK); + expression = nodeFactory.createNode(expression, index); + } + break; case Token::PERIOD: - { - m_scanner->next(); - nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(expression, expectIdentifierToken()); - } - break; + { + m_scanner->next(); + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode(expression, expectIdentifierToken()); + } + break; case Token::LPAREN: - { - m_scanner->next(); - vecptr arguments = parseFunctionCallArguments(); - nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); - expression = nodeFactory.createNode(expression, arguments); - } - break; + { + m_scanner->next(); + vecptr arguments = parseFunctionCallArguments(); + nodeFactory.markEndPosition(); + expectToken(Token::RPAREN); + expression = nodeFactory.createNode(expression, arguments); + } + break; default: return expression; } @@ -449,8 +485,8 @@ ptr Parser::parsePrimaryExpression() ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); ptr expression; - - switch (token) { + switch (token) + { case Token::TRUE_LITERAL: case Token::FALSE_LITERAL: expression = nodeFactory.createNode(token, ptr()); @@ -466,18 +502,21 @@ ptr Parser::parsePrimaryExpression() expression = nodeFactory.createNode(getLiteralAndAdvance()); break; case Token::LPAREN: - { - m_scanner->next(); - ptr expression = parseExpression(); - expectToken(Token::RPAREN); - return expression; - } + { + m_scanner->next(); + ptr expression = parseExpression(); + expectToken(Token::RPAREN); + return expression; + } default: - if (Token::IsElementaryTypeName(token)) { + if (Token::isElementaryTypeName(token)) + { // used for casts expression = nodeFactory.createNode(token); m_scanner->next(); - } else { + } + else + { throwExpectationError("Expected primary expression."); return ptr(); // this is not reached } @@ -488,9 +527,11 @@ ptr Parser::parsePrimaryExpression() vecptr Parser::parseFunctionCallArguments() { vecptr arguments; - if (m_scanner->getCurrentToken() != Token::RPAREN) { + if (m_scanner->getCurrentToken() != Token::RPAREN) + { arguments.push_back(parseExpression()); - while (m_scanner->getCurrentToken() != Token::RPAREN) { + while (m_scanner->getCurrentToken() != Token::RPAREN) + { expectToken(Token::COMMA); arguments.push_back(parseExpression()); } @@ -501,14 +542,14 @@ vecptr Parser::parseFunctionCallArguments() void Parser::expectToken(Token::Value _value) { if (m_scanner->getCurrentToken() != _value) - throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value))); + throwExpectationError(std::string("Expected token ") + std::string(Token::getName(_value))); m_scanner->next(); } Token::Value Parser::expectAssignmentOperator() { Token::Value op = m_scanner->getCurrentToken(); - if (!Token::IsAssignmentOp(op)) + if (!Token::isAssignmentOp(op)) throwExpectationError(std::string("Expected assignment operator")); m_scanner->next(); return op; @@ -518,7 +559,6 @@ ptr Parser::expectIdentifierToken() { if (m_scanner->getCurrentToken() != Token::IDENTIFIER) throwExpectationError("Expected identifier"); - return getLiteralAndAdvance(); } @@ -540,9 +580,9 @@ void Parser::throwExpectationError(const std::string& _description) << ", column " << (column + 1) << "\n" << m_scanner->getLineAtPosition(getPosition()) << "\n" << std::string(column, ' ') << "^"; - BOOST_THROW_EXCEPTION(ParserError() << errinfo_comment(buf.str())); } -} } +} +} diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 84d20cf8e..174ed7120 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -24,8 +24,10 @@ #include "libsolidity/AST.h" -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ class Scanner; @@ -77,4 +79,5 @@ private: std::shared_ptr m_scanner; }; -} } +} +} diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 334da8e7f..c2875c9cd 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -45,35 +45,47 @@ #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ -namespace { - bool IsDecimalDigit(char c) { - return '0' <= c && c <= '9'; - } - bool IsHexDigit(char c) { - return IsDecimalDigit(c) - || ('a' <= c && c <= 'f') - || ('A' <= c && c <= 'F'); - } - bool IsLineTerminator(char c) { return c == '\n'; } - bool IsWhiteSpace(char c) { - return c == ' ' || c == '\n' || c == '\t'; - } - bool IsIdentifierStart(char c) { - return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); - } - bool IsIdentifierPart(char c) { - return IsIdentifierStart(c) || IsDecimalDigit(c); - } +namespace +{ +bool IsDecimalDigit(char c) +{ + return '0' <= c && c <= '9'; +} +bool IsHexDigit(char c) +{ + return IsDecimalDigit(c) + || ('a' <= c && c <= 'f') + || ('A' <= c && c <= 'F'); +} +bool IsLineTerminator(char c) +{ + return c == '\n'; +} +bool IsWhiteSpace(char c) +{ + return c == ' ' || c == '\n' || c == '\t'; +} +bool IsIdentifierStart(char c) +{ + return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); +} +bool IsIdentifierPart(char c) +{ + return IsIdentifierStart(c) || IsDecimalDigit(c); +} - int HexValue(char c) { - if (c >= '0' && c <= '9') return c - '0'; - else if (c >= 'a' && c <= 'f') return c - 'a' + 10; - else if (c >= 'A' && c <= 'F') return c - 'A' + 10; - else return -1; - } +int HexValue(char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + else if (c >= 'a' && c <= 'f') return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') return c - 'A' + 10; + else return -1; +} } Scanner::Scanner(const CharStream& _source) @@ -84,7 +96,6 @@ Scanner::Scanner(const CharStream& _source) void Scanner::reset(const CharStream& _source) { m_source = _source; - m_char = m_source.get(); skipWhitespace(); scanToken(); @@ -95,18 +106,18 @@ void Scanner::reset(const CharStream& _source) bool Scanner::scanHexNumber(char& scanned_number, int expected_length) { BOOST_ASSERT(expected_length <= 4); // prevent overflow - char x = 0; - for (int i = 0; i < expected_length; i++) { + for (int i = 0; i < expected_length; i++) + { int d = HexValue(m_char); - if (d < 0) { + if (d < 0) + { rollback(i); return false; } x = x * 16 + d; advance(); } - scanned_number = x; return true; } @@ -128,16 +139,14 @@ Token::Value Scanner::next() bool Scanner::skipWhitespace() { const int start_position = getSourcePos(); - - while (true) { - if (IsLineTerminator(m_char)) { - m_hasLineTerminatorBeforeNext = true; - } else if (!IsWhiteSpace(m_char)) { - break; - } - advance(); + while (true) + { + if (IsLineTerminator(m_char)) + m_hasLineTerminatorBeforeNext = true; + else if (!IsWhiteSpace(m_char)) + break; + advance(); } - // Return whether or not we skipped any characters. return getSourcePos() != start_position; } @@ -145,24 +154,24 @@ bool Scanner::skipWhitespace() Token::Value Scanner::skipSingleLineComment() { - // The line terminator at the end of the line is not considered - // to be part of the single-line comment; it is recognized - // separately by the lexical grammar and becomes part of the - // stream of input elements for the syntactic grammar - while (advance() && !IsLineTerminator(m_char)) { }; - - return Token::WHITESPACE; + // The line terminator at the end of the line is not considered + // to be part of the single-line comment; it is recognized + // separately by the lexical grammar and becomes part of the + // stream of input elements for the syntactic grammar + while (advance() && !IsLineTerminator(m_char)) { }; + return Token::WHITESPACE; } Token::Value Scanner::skipMultiLineComment() { BOOST_ASSERT(m_char == '*'); advance(); - - while (!isSourcePastEndOfInput()) { + while (!isSourcePastEndOfInput()) + { char ch = m_char; advance(); - if (IsLineTerminator(ch)) { + if (IsLineTerminator(ch)) + { // Following ECMA-262, section 7.4, a comment containing // a newline will make the comment count as a line-terminator. m_hasMultilineCommentBeforeNext = true; @@ -170,274 +179,250 @@ Token::Value Scanner::skipMultiLineComment() // If we have reached the end of the multi-line comment, we // consume the '/' and insert a whitespace. This way all // multi-line comments are treated as whitespace. - if (ch == '*' && m_char == '/') { + if (ch == '*' && m_char == '/') + { m_char = ' '; return Token::WHITESPACE; } } - // Unterminated multi-line comment. return Token::ILLEGAL; } void Scanner::scanToken() { - m_next_token.literal.clear(); - Token::Value token; - do { - // Remember the position of the next token - m_next_token.location.start = getSourcePos(); - - switch (m_char) { - case '\n': - m_hasLineTerminatorBeforeNext = true; // fall-through - case ' ': - case '\t': - token = selectToken(Token::WHITESPACE); - break; - - case '"': case '\'': - token = scanString(); - break; - - case '<': - // < <= << <<= - advance(); - if (m_char == '=') { - token = selectToken(Token::LTE); - } else if (m_char == '<') { - token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); - } else { - token = Token::LT; - } - break; - - case '>': - // > >= >> >>= >>> >>>= - advance(); - if (m_char == '=') { - token = selectToken(Token::GTE); - } else if (m_char == '>') { - // >> >>= >>> >>>= - advance(); - if (m_char == '=') { - token = selectToken(Token::ASSIGN_SAR); - } else if (m_char == '>') { - token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); - } else { - token = Token::SAR; - } - } else { - token = Token::GT; - } - break; - - case '=': - // = == => - advance(); - if (m_char == '=') { - token = selectToken(Token::EQ); - } else if (m_char == '>') { - token = selectToken(Token::ARROW); - } else { - token = Token::ASSIGN; - } - break; - - case '!': - // ! != !== - advance(); - if (m_char == '=') { - token = selectToken(Token::NE); - } else { - token = Token::NOT; - } - break; - - case '+': - // + ++ += - advance(); - if (m_char == '+') { - token = selectToken(Token::INC); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_ADD); - } else { - token = Token::ADD; - } - break; - - case '-': - // - -- -= - advance(); - if (m_char == '-') { - advance(); - token = Token::DEC; - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_SUB); - } else { - token = Token::SUB; - } - break; - - case '*': - // * *= - token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); - break; - - case '%': - // % %= - token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); - break; - - case '/': - // / // /* /= - advance(); - if (m_char == '/') { - token = skipSingleLineComment(); - } else if (m_char == '*') { - token = skipMultiLineComment(); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_DIV); - } else { - token = Token::DIV; - } - break; - - case '&': - // & && &= - advance(); - if (m_char == '&') { - token = selectToken(Token::AND); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_BIT_AND); - } else { - token = Token::BIT_AND; - } - break; - - case '|': - // | || |= - advance(); - if (m_char == '|') { - token = selectToken(Token::OR); - } else if (m_char == '=') { - token = selectToken(Token::ASSIGN_BIT_OR); - } else { - token = Token::BIT_OR; + m_next_token.literal.clear(); + Token::Value token; + do + { + // Remember the position of the next token + m_next_token.location.start = getSourcePos(); + switch (m_char) + { + case '\n': + m_hasLineTerminatorBeforeNext = true; // fall-through + case ' ': + case '\t': + token = selectToken(Token::WHITESPACE); + break; + case '"': + case '\'': + token = scanString(); + break; + case '<': + // < <= << <<= + advance(); + if (m_char == '=') + token = selectToken(Token::LTE); + else if (m_char == '<') + token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); + else + token = Token::LT; + break; + case '>': + // > >= >> >>= >>> >>>= + advance(); + if (m_char == '=') + token = selectToken(Token::GTE); + else if (m_char == '>') + { + // >> >>= >>> >>>= + advance(); + if (m_char == '=') + token = selectToken(Token::ASSIGN_SAR); + else if (m_char == '>') + token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); + else + token = Token::SAR; + } + else + token = Token::GT; + break; + case '=': + // = == => + advance(); + if (m_char == '=') + token = selectToken(Token::EQ); + else if (m_char == '>') + token = selectToken(Token::ARROW); + else + token = Token::ASSIGN; + break; + case '!': + // ! != + advance(); + if (m_char == '=') + token = selectToken(Token::NE); + else + token = Token::NOT; + break; + case '+': + // + ++ += + advance(); + if (m_char == '+') + token = selectToken(Token::INC); + else if (m_char == '=') + token = selectToken(Token::ASSIGN_ADD); + else + token = Token::ADD; + break; + case '-': + // - -- -= + advance(); + if (m_char == '-') + { + advance(); + token = Token::DEC; + } + else if (m_char == '=') + token = selectToken(Token::ASSIGN_SUB); + else + token = Token::SUB; + break; + case '*': + // * *= + token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); + break; + case '%': + // % %= + token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); + break; + case '/': + // / // /* /= + advance(); + if (m_char == '/') + token = skipSingleLineComment(); + else if (m_char == '*') + token = skipMultiLineComment(); + else if (m_char == '=') + token = selectToken(Token::ASSIGN_DIV); + else + token = Token::DIV; + break; + case '&': + // & && &= + advance(); + if (m_char == '&') + token = selectToken(Token::AND); + else if (m_char == '=') + token = selectToken(Token::ASSIGN_BIT_AND); + else + token = Token::BIT_AND; + break; + case '|': + // | || |= + advance(); + if (m_char == '|') + token = selectToken(Token::OR); + else if (m_char == '=') + token = selectToken(Token::ASSIGN_BIT_OR); + else + token = Token::BIT_OR; + break; + case '^': + // ^ ^= + token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + case '.': + // . Number + advance(); + if (IsDecimalDigit(m_char)) + token = scanNumber(true); + else + token = Token::PERIOD; + break; + case ':': + token = selectToken(Token::COLON); + break; + case ';': + token = selectToken(Token::SEMICOLON); + break; + case ',': + token = selectToken(Token::COMMA); + break; + case '(': + token = selectToken(Token::LPAREN); + break; + case ')': + token = selectToken(Token::RPAREN); + break; + case '[': + token = selectToken(Token::LBRACK); + break; + case ']': + token = selectToken(Token::RBRACK); + break; + case '{': + token = selectToken(Token::LBRACE); + break; + case '}': + token = selectToken(Token::RBRACE); + break; + case '?': + token = selectToken(Token::CONDITIONAL); + break; + case '~': + token = selectToken(Token::BIT_NOT); + break; + default: + if (IsIdentifierStart(m_char)) + token = scanIdentifierOrKeyword(); + else if (IsDecimalDigit(m_char)) + token = scanNumber(false); + else if (skipWhitespace()) + token = Token::WHITESPACE; + else if (isSourcePastEndOfInput()) + token = Token::EOS; + else + token = selectToken(Token::ILLEGAL); + break; } - break; - - case '^': - // ^ ^= - token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - break; - - case '.': - // . Number - advance(); - if (IsDecimalDigit(m_char)) { - token = scanNumber(true); - } else { - token = Token::PERIOD; - } - break; - - case ':': - token = selectToken(Token::COLON); - break; - - case ';': - token = selectToken(Token::SEMICOLON); - break; - - case ',': - token = selectToken(Token::COMMA); - break; - - case '(': - token = selectToken(Token::LPAREN); - break; - - case ')': - token = selectToken(Token::RPAREN); - break; - - case '[': - token = selectToken(Token::LBRACK); - break; - - case ']': - token = selectToken(Token::RBRACK); - break; - - case '{': - token = selectToken(Token::LBRACE); - break; - - case '}': - token = selectToken(Token::RBRACE); - break; - - case '?': - token = selectToken(Token::CONDITIONAL); - break; - - case '~': - token = selectToken(Token::BIT_NOT); - break; - - default: - if (IsIdentifierStart(m_char)) { - token = scanIdentifierOrKeyword(); - } else if (IsDecimalDigit(m_char)) { - token = scanNumber(false); - } else if (skipWhitespace()) { - token = Token::WHITESPACE; - } else if (isSourcePastEndOfInput()) { - token = Token::EOS; - } else { - token = selectToken(Token::ILLEGAL); - } - break; + // Continue scanning for tokens as long as we're just skipping + // whitespace. } - - // Continue scanning for tokens as long as we're just skipping - // whitespace. - } while (token == Token::WHITESPACE); - - m_next_token.location.end = getSourcePos(); - m_next_token.token = token; + while (token == Token::WHITESPACE); + m_next_token.location.end = getSourcePos(); + m_next_token.token = token; } bool Scanner::scanEscape() { char c = m_char; advance(); - // Skip escaped newlines. if (IsLineTerminator(c)) return true; - - switch (c) { + switch (c) + { case '\'': // fall through case '"' : // fall through - case '\\': break; - case 'b' : c = '\b'; break; - case 'f' : c = '\f'; break; - case 'n' : c = '\n'; break; - case 'r' : c = '\r'; break; - case 't' : c = '\t'; break; - case 'u' : { + case '\\': + break; + case 'b' : + c = '\b'; + break; + case 'f' : + c = '\f'; + break; + case 'n' : + c = '\n'; + break; + case 'r' : + c = '\r'; + break; + case 't' : + c = '\t'; + break; + case 'u' : if (!scanHexNumber(c, 4)) return false; break; - } - case 'v' : c = '\v'; break; - case 'x' : { + case 'v' : + c = '\v'; + break; + case 'x' : if (!scanHexNumber(c, 2)) return false; break; } - } - // According to ECMA-262, section 7.8.4, characters not covered by the // above cases should be illegal, but they are commonly handled as // non-escaped characters by JS VMs. @@ -449,20 +434,21 @@ Token::Value Scanner::scanString() { const char quote = m_char; advance(); // consume quote - LiteralScope literal(this); - while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) { + while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) + { char c = m_char; advance(); - if (c == '\\') { - if (isSourcePastEndOfInput() || !scanEscape()) return Token::ILLEGAL; - } else { - addLiteralChar(c); + if (c == '\\') + { + if (isSourcePastEndOfInput() || !scanEscape()) + return Token::ILLEGAL; } + else + addLiteralChar(c); } if (m_char != quote) return Token::ILLEGAL; literal.Complete(); - advance(); // consume quote return Token::STRING_LITERAL; } @@ -477,71 +463,66 @@ void Scanner::scanDecimalDigits() Token::Value Scanner::scanNumber(bool _periodSeen) { - BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction - - enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL; - - LiteralScope literal(this); - if (_periodSeen) { - // we have already seen a decimal point of the float - addLiteralChar('.'); - scanDecimalDigits(); // we know we have at least one digit - } else { - // if the first character is '0' we must check for octals and hex - if (m_char == '0') { - addLiteralCharAndAdvance(); - - // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or - // an octal number. - if (m_char == 'x' || m_char == 'X') { - // hex number - kind = HEX; - addLiteralCharAndAdvance(); - if (!IsHexDigit(m_char)) { - // we must have at least one hex digit after 'x'/'X' - return Token::ILLEGAL; + BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction + enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL; + LiteralScope literal(this); + if (_periodSeen) + { + // we have already seen a decimal point of the float + addLiteralChar('.'); + scanDecimalDigits(); // we know we have at least one digit + } + else + { + // if the first character is '0' we must check for octals and hex + if (m_char == '0') + { + addLiteralCharAndAdvance(); + // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or + // an octal number. + if (m_char == 'x' || m_char == 'X') + { + // hex number + kind = HEX; + addLiteralCharAndAdvance(); + if (!IsHexDigit(m_char)) + return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X' + while (IsHexDigit(m_char)) + addLiteralCharAndAdvance(); + } } - while (IsHexDigit(m_char)) { - addLiteralCharAndAdvance(); + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) + { + scanDecimalDigits(); // optional + if (m_char == '.') + { + addLiteralCharAndAdvance(); + scanDecimalDigits(); // optional + } } - } } - - // Parse decimal digits and allow trailing fractional part. - if (kind == DECIMAL) { - scanDecimalDigits(); // optional - if (m_char == '.') { + // scan exponent, if any + if (m_char == 'e' || m_char == 'E') + { + BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind != DECIMAL) return Token::ILLEGAL; + // scan exponent addLiteralCharAndAdvance(); - scanDecimalDigits(); // optional - } + if (m_char == '+' || m_char == '-') + addLiteralCharAndAdvance(); + if (!IsDecimalDigit(m_char)) + return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E' + scanDecimalDigits(); } - } - - // scan exponent, if any - if (m_char == 'e' || m_char == 'E') { - BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number - if (kind != DECIMAL) return Token::ILLEGAL; - // scan exponent - addLiteralCharAndAdvance(); - if (m_char == '+' || m_char == '-') - addLiteralCharAndAdvance(); - if (!IsDecimalDigit(m_char)) { - // we must have at least one decimal digit after 'e'/'E' - return Token::ILLEGAL; - } - scanDecimalDigits(); - } - - // The source character immediately following a numeric literal must - // not be an identifier start or a decimal digit; see ECMA-262 - // section 7.8.3, page 17 (note that we read only one decimal digit - // if the value is 0). - if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char)) - return Token::ILLEGAL; - - literal.Complete(); - - return Token::NUMBER; + // The source character immediately following a numeric literal must + // not be an identifier start or a decimal digit; see ECMA-262 + // section 7.8.3, page 17 (note that we read only one decimal digit + // if the value is 0). + if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char)) + return Token::ILLEGAL; + literal.Complete(); + return Token::NUMBER; } @@ -549,130 +530,126 @@ Token::Value Scanner::scanNumber(bool _periodSeen) // Keyword Matcher #define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ - KEYWORD_GROUP('a') \ - KEYWORD("address", Token::ADDRESS) \ - KEYWORD_GROUP('b') \ - KEYWORD("break", Token::BREAK) \ - KEYWORD("bool", Token::BOOL) \ - KEYWORD_GROUP('c') \ - KEYWORD("case", Token::CASE) \ - KEYWORD("catch", Token::CATCH) \ - KEYWORD("const", Token::CONST) \ - KEYWORD("continue", Token::CONTINUE) \ - KEYWORD("contract", Token::CONTRACT) \ - KEYWORD_GROUP('d') \ - KEYWORD("debugger", Token::DEBUGGER) \ - KEYWORD("default", Token::DEFAULT) \ - KEYWORD("delete", Token::DELETE) \ - KEYWORD("do", Token::DO) \ - KEYWORD_GROUP('e') \ - KEYWORD("else", Token::ELSE) \ - KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ - KEYWORD_GROUP('f') \ - KEYWORD("false", Token::FALSE_LITERAL) \ - KEYWORD("finally", Token::FINALLY) \ - KEYWORD("for", Token::FOR) \ - KEYWORD("function", Token::FUNCTION) \ - KEYWORD_GROUP('h') \ - KEYWORD("hash", Token::HASH) \ - KEYWORD("hash32", Token::HASH32) \ - KEYWORD("hash64", Token::HASH64) \ - KEYWORD("hash128", Token::HASH128) \ - KEYWORD("hash256", Token::HASH256) \ - KEYWORD_GROUP('i') \ - KEYWORD("if", Token::IF) \ - KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("in", Token::IN) \ - KEYWORD("instanceof", Token::INSTANCEOF) \ - KEYWORD("int", Token::INT) \ - KEYWORD("int32", Token::INT32) \ - KEYWORD("int64", Token::INT64) \ - KEYWORD("int128", Token::INT128) \ - KEYWORD("int256", Token::INT256) \ - KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('l') \ - KEYWORD_GROUP('m') \ - KEYWORD("mapping", Token::MAPPING) \ - KEYWORD_GROUP('n') \ - KEYWORD("new", Token::NEW) \ - KEYWORD("null", Token::NULL_LITERAL) \ - KEYWORD_GROUP('p') \ - KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("private", Token::PRIVATE) \ - KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("public", Token::PUBLIC) \ - KEYWORD_GROUP('r') \ - KEYWORD("real", Token::REAL) \ - KEYWORD("return", Token::RETURN) \ - KEYWORD("returns", Token::RETURNS) \ - KEYWORD_GROUP('s') \ - KEYWORD("string", Token::STRING_TYPE) \ - KEYWORD("struct", Token::STRUCT) \ - KEYWORD("switch", Token::SWITCH) \ - KEYWORD_GROUP('t') \ - KEYWORD("text", Token::TEXT) \ - KEYWORD("this", Token::THIS) \ - KEYWORD("throw", Token::THROW) \ - KEYWORD("true", Token::TRUE_LITERAL) \ - KEYWORD("try", Token::TRY) \ - KEYWORD("typeof", Token::TYPEOF) \ - KEYWORD_GROUP('u') \ - KEYWORD("uint", Token::UINT) \ - KEYWORD("uint32", Token::UINT32) \ - KEYWORD("uint64", Token::UINT64) \ - KEYWORD("uint128", Token::UINT128) \ - KEYWORD("uint256", Token::UINT256) \ - KEYWORD("ureal", Token::UREAL) \ - KEYWORD_GROUP('v') \ - KEYWORD("var", Token::VAR) \ - KEYWORD("void", Token::VOID) \ - KEYWORD_GROUP('w') \ - KEYWORD("while", Token::WHILE) \ - KEYWORD("with", Token::WITH) + KEYWORD_GROUP('a') \ + KEYWORD("address", Token::ADDRESS) \ + KEYWORD_GROUP('b') \ + KEYWORD("break", Token::BREAK) \ + KEYWORD("bool", Token::BOOL) \ + KEYWORD_GROUP('c') \ + KEYWORD("case", Token::CASE) \ + KEYWORD("catch", Token::CATCH) \ + KEYWORD("const", Token::CONST) \ + KEYWORD("continue", Token::CONTINUE) \ + KEYWORD("contract", Token::CONTRACT) \ + KEYWORD_GROUP('d') \ + KEYWORD("debugger", Token::DEBUGGER) \ + KEYWORD("default", Token::DEFAULT) \ + KEYWORD("delete", Token::DELETE) \ + KEYWORD("do", Token::DO) \ + KEYWORD_GROUP('e') \ + KEYWORD("else", Token::ELSE) \ + KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ + KEYWORD_GROUP('f') \ + KEYWORD("false", Token::FALSE_LITERAL) \ + KEYWORD("finally", Token::FINALLY) \ + KEYWORD("for", Token::FOR) \ + KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('h') \ + KEYWORD("hash", Token::HASH) \ + KEYWORD("hash32", Token::HASH32) \ + KEYWORD("hash64", Token::HASH64) \ + KEYWORD("hash128", Token::HASH128) \ + KEYWORD("hash256", Token::HASH256) \ + KEYWORD_GROUP('i') \ + KEYWORD("if", Token::IF) \ + KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("in", Token::IN) \ + KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("int", Token::INT) \ + KEYWORD("int32", Token::INT32) \ + KEYWORD("int64", Token::INT64) \ + KEYWORD("int128", Token::INT128) \ + KEYWORD("int256", Token::INT256) \ + KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('l') \ + KEYWORD_GROUP('m') \ + KEYWORD("mapping", Token::MAPPING) \ + KEYWORD_GROUP('n') \ + KEYWORD("new", Token::NEW) \ + KEYWORD("null", Token::NULL_LITERAL) \ + KEYWORD_GROUP('p') \ + KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::PRIVATE) \ + KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("public", Token::PUBLIC) \ + KEYWORD_GROUP('r') \ + KEYWORD("real", Token::REAL) \ + KEYWORD("return", Token::RETURN) \ + KEYWORD("returns", Token::RETURNS) \ + KEYWORD_GROUP('s') \ + KEYWORD("string", Token::STRING_TYPE) \ + KEYWORD("struct", Token::STRUCT) \ + KEYWORD("switch", Token::SWITCH) \ + KEYWORD_GROUP('t') \ + KEYWORD("text", Token::TEXT) \ + KEYWORD("this", Token::THIS) \ + KEYWORD("throw", Token::THROW) \ + KEYWORD("true", Token::TRUE_LITERAL) \ + KEYWORD("try", Token::TRY) \ + KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('u') \ + KEYWORD("uint", Token::UINT) \ + KEYWORD("uint32", Token::UINT32) \ + KEYWORD("uint64", Token::UINT64) \ + KEYWORD("uint128", Token::UINT128) \ + KEYWORD("uint256", Token::UINT256) \ + KEYWORD("ureal", Token::UREAL) \ + KEYWORD_GROUP('v') \ + KEYWORD("var", Token::VAR) \ + KEYWORD("void", Token::VOID) \ + KEYWORD_GROUP('w') \ + KEYWORD("while", Token::WHILE) \ + KEYWORD("with", Token::WITH) static Token::Value KeywordOrIdentifierToken(const std::string& input) { - BOOST_ASSERT(!input.empty()); - const int kMinLength = 2; - const int kMaxLength = 10; - if (input.size() < kMinLength || input.size() > kMaxLength) { - return Token::IDENTIFIER; - } - switch (input[0]) { + BOOST_ASSERT(!input.empty()); + const int kMinLength = 2; + const int kMaxLength = 10; + if (input.size() < kMinLength || input.size() > kMaxLength) + return Token::IDENTIFIER; + switch (input[0]) + { default: #define KEYWORD_GROUP_CASE(ch) \ - break; \ - case ch: + break; \ +case ch: #define KEYWORD(keyword, token) \ { \ - /* 'keyword' is a char array, so sizeof(keyword) is */ \ - /* strlen(keyword) plus 1 for the NUL char. */ \ - const int keyword_length = sizeof(keyword) - 1; \ - BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ - BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ - if (input == keyword) { \ - return token; \ - } \ + /* 'keyword' is a char array, so sizeof(keyword) is */ \ + /* strlen(keyword) plus 1 for the NUL char. */ \ + const int keyword_length = sizeof(keyword) - 1; \ + BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ + BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ + if (input == keyword) { \ + return token; \ + } \ } - KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) - } - return Token::IDENTIFIER; + KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) + } + return Token::IDENTIFIER; } Token::Value Scanner::scanIdentifierOrKeyword() { BOOST_ASSERT(IsIdentifierStart(m_char)); LiteralScope literal(this); - addLiteralCharAndAdvance(); - // Scan the rest of the identifier characters. while (IsIdentifierPart(m_char)) addLiteralCharAndAdvance(); - literal.Complete(); - return KeywordOrIdentifierToken(m_next_token.literal); } @@ -697,17 +674,17 @@ std::tuple CharStream::translatePositionToLineColumn(int _position) co using size_type = std::string::size_type; size_type searchPosition = std::min(m_source.size(), _position); int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); - size_type lineStart; - if (searchPosition == 0) { + if (searchPosition == 0) lineStart = 0; - } else { + else + { lineStart = m_source.rfind('\n', searchPosition - 1); lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; } - return std::tuple(lineNumber, searchPosition - lineStart); } -} } +} +} diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 4bc841111..ea7c6b71a 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -50,33 +50,36 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ class AstRawString; class AstValueFactory; class ParserRecorder; -class CharStream { +class CharStream +{ public: CharStream() : m_pos(0) {} - explicit CharStream(const std::string& _source) - : m_source(_source), m_pos(0) - {} + explicit CharStream(const std::string& _source) : m_source(_source), m_pos(0) {} int getPos() const { return m_pos; } bool isPastEndOfInput() const { return m_pos >= m_source.size(); } char get() const { return m_source[m_pos]; } - char advanceAndGet() { + char advanceAndGet() + { if (isPastEndOfInput()) return 0; ++m_pos; if (isPastEndOfInput()) return 0; return get(); } - char rollback(size_t _amount) { + char rollback(size_t _amount) + { BOOST_ASSERT(m_pos >= _amount); m_pos -= _amount; return get(); @@ -96,22 +99,17 @@ private: // ---------------------------------------------------------------------------- // JavaScript Scanner. -class Scanner { +class Scanner +{ public: // Scoped helper for literal recording. Automatically drops the literal // if aborting the scanning before it's complete. - class LiteralScope { + class LiteralScope + { public: - explicit LiteralScope(Scanner* self) - : scanner_(self), complete_(false) { - scanner_->startNewLiteral(); - } - ~LiteralScope() { - if (!complete_) scanner_->dropLiteral(); - } - void Complete() { - complete_ = true; - } + explicit LiteralScope(Scanner* self) : scanner_(self), complete_(false) { scanner_->startNewLiteral(); } + ~LiteralScope() { if (!complete_) scanner_->dropLiteral(); } + void Complete() { complete_ = true; } private: Scanner* scanner_; @@ -143,7 +141,10 @@ public: /// Functions that help pretty-printing parse errors. /// Do only use in error cases, they are quite expensive. /// @{ - std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } + std::string getLineAtPosition(int _position) const + { + return m_source.getLineAtPosition(_position); + } std::tuple translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); @@ -152,56 +153,46 @@ public: // Returns true if there was a line terminator before the peek'ed token, // possibly inside a multi-line comment. - bool hasAnyLineTerminatorBeforeNext() const { + bool hasAnyLineTerminatorBeforeNext() const + { return m_hasLineTerminatorBeforeNext || - m_hasMultilineCommentBeforeNext; + m_hasMultilineCommentBeforeNext; } private: // Used for the current and look-ahead token. - struct TokenDesc { + struct TokenDesc + { Token::Value token; Location location; std::string literal; }; // Literal buffer support - inline void startNewLiteral() { - m_next_token.literal.clear(); - } + inline void startNewLiteral() { m_next_token.literal.clear(); } - inline void addLiteralChar(char c) { - m_next_token.literal.push_back(c); - } + inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); } - inline void dropLiteral() { - m_next_token.literal.clear(); - } + inline void dropLiteral() { m_next_token.literal.clear(); } - inline void addLiteralCharAndAdvance() { - addLiteralChar(m_char); - advance(); - } + inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } // Low-level scanning support. bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } - void rollback(int amount) { - m_char = m_source.rollback(amount); - } + void rollback(int amount) { m_char = m_source.rollback(amount); } - inline Token::Value selectToken(Token::Value tok) { - advance(); - return tok; - } + inline Token::Value selectToken(Token::Value tok) { advance(); return tok; } - inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) { + inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) + { advance(); - if (m_char == next) { + if (m_char == next) + { advance(); return then; - } else { - return else_; } + else + return else_; } bool scanHexNumber(char& scanned_number, int expected_length); @@ -225,12 +216,8 @@ private: bool scanEscape(); // Return the current source position. - int getSourcePos() { - return m_source.getPos(); - } - bool isSourcePastEndOfInput() { - return m_source.isPastEndOfInput(); - } + int getSourcePos() { return m_source.getPos(); } + bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } TokenDesc m_current_token; // desc for current token (as returned by Next()) TokenDesc m_next_token; // desc for next token (one token look-ahead) @@ -249,4 +236,5 @@ private: bool m_hasMultilineCommentBeforeNext; }; -} } +} +} diff --git a/libsolidity/Scope.cpp b/libsolidity/Scope.cpp index 27298f878..7c298b723 100644 --- a/libsolidity/Scope.cpp +++ b/libsolidity/Scope.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -23,8 +23,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ bool Scope::registerDeclaration(Declaration& _declaration) @@ -35,7 +37,7 @@ bool Scope::registerDeclaration(Declaration& _declaration) return true; } -Declaration*Scope::resolveName(ASTString const& _name, bool _recursive) const +Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const { auto result = m_declarations.find(_name); if (result != m_declarations.end()) @@ -45,4 +47,5 @@ Declaration*Scope::resolveName(ASTString const& _name, bool _recursive) const return nullptr; } -} } +} +} diff --git a/libsolidity/Scope.h b/libsolidity/Scope.h index e3b024ece..b3686ec0e 100644 --- a/libsolidity/Scope.h +++ b/libsolidity/Scope.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -28,8 +28,10 @@ #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ class Scope { @@ -46,4 +48,5 @@ private: std::map m_declarations; }; -} } +} +} diff --git a/libsolidity/Token.cpp b/libsolidity/Token.cpp index 0264f7e85..564be734a 100644 --- a/libsolidity/Token.cpp +++ b/libsolidity/Token.cpp @@ -42,25 +42,30 @@ #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ #define T(name, string, precedence) #name, -const char* const Token::m_name[NUM_TOKENS] = { +const char* const Token::m_name[NUM_TOKENS] = +{ TOKEN_LIST(T, T) }; #undef T #define T(name, string, precedence) string, -const char* const Token::m_string[NUM_TOKENS] = { +const char* const Token::m_string[NUM_TOKENS] = +{ TOKEN_LIST(T, T) }; #undef T #define T(name, string, precedence) precedence, -const int8_t Token::m_precedence[NUM_TOKENS] = { +const int8_t Token::m_precedence[NUM_TOKENS] = +{ TOKEN_LIST(T, T) }; #undef T @@ -68,10 +73,12 @@ const int8_t Token::m_precedence[NUM_TOKENS] = { #define KT(a, b, c) 'T', #define KK(a, b, c) 'K', -const char Token::m_tokenType[] = { +const char Token::m_tokenType[] = +{ TOKEN_LIST(KT, KK) }; #undef KT #undef KK -} } +} +} diff --git a/libsolidity/Token.h b/libsolidity/Token.h index d1db4134b..9e3896671 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -47,8 +47,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ // TOKEN_LIST takes a list of 3 macros M, all of which satisfy the // same signature M(name, string, precedence), where name is the @@ -68,7 +70,7 @@ namespace solidity { #define TOKEN_LIST(T, K) \ /* End of source indicator. */ \ T(EOS, "EOS", 0) \ - \ + \ /* Punctuators (ECMA-262, section 7.7, page 15). */ \ T(LPAREN, "(", 0) \ T(RPAREN, ")", 0) \ @@ -83,7 +85,7 @@ namespace solidity { T(INC, "++", 0) \ T(DEC, "--", 0) \ T(ARROW, "=>", 0) \ - \ + \ /* Assignment operators. */ \ /* IsAssignmentOp() and Assignment::is_compound() relies on */ \ /* this block of enum values being contiguous and sorted in the */ \ @@ -105,7 +107,7 @@ namespace solidity { T(ASSIGN_MUL, "*=", 2) \ T(ASSIGN_DIV, "/=", 2) \ T(ASSIGN_MOD, "%=", 2) \ - \ + \ /* Binary operators sorted by precedence. */ \ /* IsBinaryOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ @@ -123,7 +125,7 @@ namespace solidity { T(MUL, "*", 13) \ T(DIV, "/", 13) \ T(MOD, "%", 13) \ - \ + \ /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ @@ -137,7 +139,7 @@ namespace solidity { T(GTE, ">=", 10) \ K(INSTANCEOF, "instanceof", 10) \ K(IN, "in", 10) \ - \ + \ /* Unary operators. */ \ /* IsUnaryOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ @@ -146,7 +148,7 @@ namespace solidity { K(DELETE, "delete", 0) \ K(TYPEOF, "typeof", 0) \ K(VOID, "void", 0) \ - \ + \ /* Keywords (ECMA-262, section 7.5.2, page 13). */ \ K(BREAK, "break", 0) \ K(CASE, "case", 0) \ @@ -180,7 +182,7 @@ namespace solidity { /* VOID */ \ K(WHILE, "while", 0) \ K(WITH, "with", 0) \ - \ + \ /* type keywords, keep them in this order, keep int as first keyword * the implementation in Types.cpp has to be synced to this here * TODO more to be added */ \ @@ -206,17 +208,17 @@ namespace solidity { K(REAL, "real", 0) \ K(UREAL, "ureal", 0) \ T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ - \ + \ /* Literals (ECMA-262, section 7.8, page 16). */ \ K(NULL_LITERAL, "null", 0) \ K(TRUE_LITERAL, "true", 0) \ K(FALSE_LITERAL, "false", 0) \ T(NUMBER, NULL, 0) \ T(STRING_LITERAL, NULL, 0) \ - \ + \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, NULL, 0) \ - \ + \ /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ T(FUTURE_RESERVED_WORD, NULL, 0) \ T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ @@ -227,21 +229,23 @@ namespace solidity { K(IMPORT, "import", 0) \ K(LET, "let", 0) \ K(STATIC, "static", 0) \ -/* K(YIELD, "yield", 0) */ \ + /* K(YIELD, "yield", 0) */ \ K(SUPER, "super", 0) \ - \ + \ /* Illegal token - not able to scan. */ \ T(ILLEGAL, "ILLEGAL", 0) \ - \ + \ /* Scanner-internal use only. */ \ T(WHITESPACE, NULL, 0) -class Token { +class Token +{ public: // All token values. #define T(name, string, precedence) name, - enum Value { + enum Value + { TOKEN_LIST(T, T) NUM_TOKENS }; @@ -249,123 +253,110 @@ public: // Returns a string corresponding to the C++ token name // (e.g. "LT" for the token LT). - static const char* Name(Value tok) { + static const char* getName(Value tok) + { BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned return m_name[tok]; } // Predicates - static bool IsKeyword(Value tok) { - return m_tokenType[tok] == 'K'; - } - - static bool IsIdentifier(Value tok) { - return tok == IDENTIFIER; - } - - static bool IsElementaryTypeName(Value tok) { - return INT <= tok && tok < TYPES_END; - } - - static bool IsAssignmentOp(Value tok) { - return INIT_VAR <= tok && tok <= ASSIGN_MOD; - } - - static bool IsBinaryOp(Value op) { - return COMMA <= op && op <= MOD; - } - - static bool IsTruncatingBinaryOp(Value op) { - return BIT_OR <= op && op <= SHR; - } - - static bool IsCompareOp(Value op) { - return EQ <= op && op <= IN; - } - - static bool IsOrderedRelationalCompareOp(Value op) { + static bool isKeyword(Value tok) { return m_tokenType[tok] == 'K'; } + static bool isIdentifier(Value tok) { return tok == IDENTIFIER; } + static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; } + static bool isAssignmentOp(Value tok) { return INIT_VAR <= tok && tok <= ASSIGN_MOD; } + static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } + static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; } + static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } + static bool isOrderedRelationalCompareOp(Value op) + { return op == LT || op == LTE || op == GT || op == GTE; } - - static bool IsEqualityOp(Value op) { - return op == EQ || op == EQ_STRICT; - } - - static bool IsInequalityOp(Value op) { - return op == NE || op == NE_STRICT; + static bool isEqualityOp(Value op) { return op == EQ || op == EQ_STRICT; } + static bool isInequalityOp(Value op) { return op == NE || op == NE_STRICT; } + static bool isArithmeticCompareOp(Value op) + { + return isOrderedRelationalCompareOp(op) || + isEqualityOp(op) || isInequalityOp(op); } - static bool IsArithmeticCompareOp(Value op) { - return IsOrderedRelationalCompareOp(op) || - IsEqualityOp(op) || IsInequalityOp(op); - } - - static Value NegateCompareOp(Value op) { - BOOST_ASSERT(IsArithmeticCompareOp(op)); - switch (op) { - case EQ: return NE; - case NE: return EQ; - case EQ_STRICT: return NE_STRICT; - case NE_STRICT: return EQ_STRICT; - case LT: return GTE; - case GT: return LTE; - case LTE: return GT; - case GTE: return LT; + static Value negateCompareOp(Value op) + { + BOOST_ASSERT(isArithmeticCompareOp(op)); + switch (op) + { + case EQ: + return NE; + case NE: + return EQ; + case EQ_STRICT: + return NE_STRICT; + case NE_STRICT: + return EQ_STRICT; + case LT: + return GTE; + case GT: + return LTE; + case LTE: + return GT; + case GTE: + return LT; default: BOOST_ASSERT(false); // should not get here return op; } } - static Value ReverseCompareOp(Value op) { - BOOST_ASSERT(IsArithmeticCompareOp(op)); - switch (op) { - case EQ: return EQ; - case NE: return NE; - case EQ_STRICT: return EQ_STRICT; - case NE_STRICT: return NE_STRICT; - case LT: return GT; - case GT: return LT; - case LTE: return GTE; - case GTE: return LTE; + static Value reverseCompareOp(Value op) + { + BOOST_ASSERT(isArithmeticCompareOp(op)); + switch (op) + { + case EQ: + return EQ; + case NE: + return NE; + case EQ_STRICT: + return EQ_STRICT; + case NE_STRICT: + return NE_STRICT; + case LT: + return GT; + case GT: + return LT; + case LTE: + return GTE; + case GTE: + return LTE; default: BOOST_ASSERT(false); // should not get here return op; } } - static Value AssignmentToBinaryOp(Value op) { - BOOST_ASSERT(IsAssignmentOp(op) && op != ASSIGN); + static Value AssignmentToBinaryOp(Value op) + { + BOOST_ASSERT(isAssignmentOp(op) && op != ASSIGN); return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); } - static bool IsBitOp(Value op) { - return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; - } - - static bool IsUnaryOp(Value op) { - return (NOT <= op && op <= VOID) || op == ADD || op == SUB; - } - - static bool IsCountOp(Value op) { - return op == INC || op == DEC; - } - - static bool IsShiftOp(Value op) { - return (SHL <= op) && (op <= SHR); - } + static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; } + static bool isUnaryOp(Value op) { return (NOT <= op && op <= VOID) || op == ADD || op == SUB; } + static bool isCountOp(Value op) { return op == INC || op == DEC; } + static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } // Returns a string corresponding to the JS token string // (.e., "<" for the token LT) or NULL if the token doesn't // have a (unique) string (e.g. an IDENTIFIER). - static const char* String(Value tok) { + static const char* toString(Value tok) + { BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. return m_string[tok]; } // Returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. - static int Precedence(Value tok) { + static int precedence(Value tok) + { BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. return m_precedence[tok]; } @@ -377,4 +368,5 @@ private: static const char m_tokenType[NUM_TOKENS]; }; -} } +} +} diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 8e8a9d979..c5ee40cb8 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -23,12 +23,15 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ ptr Type::fromElementaryTypeName(Token::Value _typeToken) { - if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { + if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) + { int offset = _typeToken - Token::INT; int bits = offset % 5; if (bits == 0) @@ -39,15 +42,14 @@ ptr Type::fromElementaryTypeName(Token::Value _typeToken) return std::make_shared(bits, modifier == 0 ? IntegerType::Modifier::SIGNED : modifier == 1 ? IntegerType::Modifier::UNSIGNED : - IntegerType::Modifier::HASH); - } else if (_typeToken == Token::ADDRESS) { + IntegerType::Modifier::HASH); + } + else if (_typeToken == Token::ADDRESS) return std::make_shared(0, IntegerType::Modifier::ADDRESS); - } else if (_typeToken == Token::BOOL) { + else if (_typeToken == Token::BOOL) return std::make_shared(); - } else { - BOOST_ASSERT(false); - // @todo add other tyes - } + else + BOOST_ASSERT(false); // @todo add other tyes } ptr Type::fromUserDefinedTypeName(const UserDefinedTypeName& _typeName) @@ -63,7 +65,8 @@ ptr Type::fromMapping(const Mapping&) ptr Type::forLiteral(const Literal& _literal) { - switch (_literal.getToken()) { + switch (_literal.getToken()) + { case Token::TRUE_LITERAL: case Token::FALSE_LITERAL: return std::make_shared(); @@ -114,13 +117,12 @@ bool IntegerType::isExplicitlyConvertibleTo(const Type& _convertTo) const bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const { - if (isAddress()) { - return Token::IsCompareOp(_operator); - } else if (isHash()) { - return Token::IsCompareOp(_operator) || Token::IsBitOp(_operator); - } else { + if (isAddress()) + return Token::isCompareOp(_operator); + else if (isHash()) + return Token::isCompareOp(_operator) || Token::isBitOp(_operator); + else return true; - } } bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const @@ -132,7 +134,8 @@ bool BoolType::isExplicitlyConvertibleTo(const Type& _convertTo) const { // conversion to integer is fine, but not to address // this is an example of explicit conversions being not transitive (though implicit should be) - if (_convertTo.getCategory() == Category::INTEGER) { + if (_convertTo.getCategory() == Category::INTEGER) + { IntegerType const& convertTo = dynamic_cast(_convertTo); if (!convertTo.isAddress()) return true; @@ -157,4 +160,5 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const } -} } +} +} diff --git a/libsolidity/Types.h b/libsolidity/Types.h index f73357a2f..aed8a7e7c 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -1,18 +1,18 @@ /* - This file is part of cpp-ethereum. + This file is part of cpp-ethereum. - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see . + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . */ /** * @author Christian @@ -29,8 +29,10 @@ #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ // AST forward declarations class ContractDefinition; @@ -49,7 +51,8 @@ using ptr = std::shared_ptr; class Type : private boost::noncopyable { public: - enum class Category { + enum class Category + { INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE }; @@ -62,7 +65,10 @@ public: virtual Category getCategory() const = 0; virtual bool isImplicitlyConvertibleTo(const Type&) const { return false; } - virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const { return isImplicitlyConvertibleTo(_convertTo); } + virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const + { + return isImplicitlyConvertibleTo(_convertTo); + } virtual bool acceptsBinaryOperator(Token::Value) const { return false; } virtual bool acceptsUnaryOperator(Token::Value) const { return false; } }; @@ -70,7 +76,8 @@ public: class IntegerType : public Type { public: - enum class Modifier { + enum class Modifier + { UNSIGNED, SIGNED, HASH, ADDRESS }; virtual Category getCategory() const { return Category::INTEGER; } @@ -98,12 +105,18 @@ class BoolType : public Type public: virtual Category getCategory() const { return Category::BOOL; } virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override - { return _convertTo.getCategory() == Category::BOOL; } + { + return _convertTo.getCategory() == Category::BOOL; + } virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; virtual bool acceptsBinaryOperator(Token::Value _operator) const override - { return _operator == Token::AND || _operator == Token::OR; } + { + return _operator == Token::AND || _operator == Token::OR; + } virtual bool acceptsUnaryOperator(Token::Value _operator) const override - { return _operator == Token::NOT || _operator == Token::DELETE; } + { + return _operator == Token::NOT || _operator == Token::DELETE; + } }; class ContractType : public Type @@ -123,7 +136,9 @@ public: StructType(StructDefinition const& _struct) : m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; virtual bool acceptsUnaryOperator(Token::Value _operator) const override - { return _operator == Token::DELETE; } + { + return _operator == Token::DELETE; + } private: StructDefinition const& m_struct; }; @@ -168,4 +183,5 @@ private: }; -} } +} +} diff --git a/solc/main.cpp b/solc/main.cpp index a92f466ec..a2b791462 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -10,8 +10,10 @@ #include #include -namespace dev { -namespace solidity { +namespace dev +{ +namespace solidity +{ ptr parseAST(std::string const& _source) { @@ -20,15 +22,16 @@ ptr parseAST(std::string const& _source) return parser.parse(scanner); } -} } // end namespaces +} +} // end namespaces void help() { std::cout - << "Usage solc [OPTIONS] " << std::endl - << "Options:" << std::endl - << " -h,--help Show this help message and exit." << std::endl - << " -V,--version Show the version and exit." << std::endl; + << "Usage solc [OPTIONS] " << std::endl + << "Options:" << std::endl + << " -h,--help Show this help message and exit." << std::endl + << " -V,--version Show the version and exit." << std::endl; exit(0); } @@ -44,7 +47,6 @@ void version() int main(int argc, char** argv) { std::string infile; - for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; @@ -55,7 +57,6 @@ int main(int argc, char** argv) else infile = argv[i]; } - std::string src; if (infile.empty()) { @@ -65,10 +66,11 @@ int main(int argc, char** argv) getline(std::cin, s); src.append(s); } - } else { + } + else + { src = dev::asString(dev::contents(infile)); } - std::cout << "Parsing..." << std::endl; // @todo catch exception dev::solidity::ptr ast = dev::solidity::parseAST(src); diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index d2f99e83e..ed2cb7b53 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -29,19 +29,23 @@ #include #include -namespace dev { -namespace solidity { -namespace test { +namespace dev +{ +namespace solidity +{ +namespace test +{ -namespace { - void parseTextAndResolveNames(const std::string& _source) - { - Parser parser; - ptr contract = parser.parse( - std::make_shared(CharStream(_source))); - NameAndTypeResolver resolver; - resolver.resolveNamesAndTypes(*contract); - } +namespace +{ +void parseTextAndResolveNames(const std::string& _source) +{ + Parser parser; + ptr contract = parser.parse( + std::make_shared(CharStream(_source))); + NameAndTypeResolver resolver; + resolver.resolveNamesAndTypes(*contract); +} } BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution) @@ -168,5 +172,7 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) BOOST_AUTO_TEST_SUITE_END() -} } } // end namespaces +} +} +} // end namespaces diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index b1f27bcb6..ef434cc35 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -28,16 +28,20 @@ #include #include -namespace dev { -namespace solidity { -namespace test { +namespace dev +{ +namespace solidity +{ +namespace test +{ -namespace { - ptr parseText(const std::string& _source) - { - Parser parser; - return parser.parse(std::make_shared(CharStream(_source))); - } +namespace +{ +ptr parseText(const std::string& _source) +{ + Parser parser; + return parser.parse(std::make_shared(CharStream(_source))); +} } BOOST_AUTO_TEST_SUITE(SolidityParser) @@ -211,5 +215,7 @@ BOOST_AUTO_TEST_CASE(else_if_statement) BOOST_AUTO_TEST_SUITE_END() -} } } // end namespaces +} +} +} // end namespaces diff --git a/test/solidityScanner.cpp b/test/solidityScanner.cpp index 759d2f101..d2a960cfb 100644 --- a/test/solidityScanner.cpp +++ b/test/solidityScanner.cpp @@ -23,116 +23,121 @@ #include #include -namespace dev { -namespace solidity { -namespace test { +namespace dev +{ +namespace solidity +{ +namespace test +{ BOOST_AUTO_TEST_SUITE(SolidityScanner) BOOST_AUTO_TEST_CASE(test_empty) { - Scanner scanner(CharStream("")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); + Scanner scanner(CharStream("")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); } BOOST_AUTO_TEST_CASE(smoke_test) { - Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::FUNCTION); - BOOST_CHECK_EQUAL(scanner.next(), Token::BREAK); - BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "765"); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1"); - BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string2"); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "identifier1"); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::FUNCTION); + BOOST_CHECK_EQUAL(scanner.next(), Token::BREAK); + BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "765"); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1"); + BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string2"); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "identifier1"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(string_escapes) { - Scanner scanner(CharStream(" { \"a\\x61\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "aa"); + Scanner scanner(CharStream(" { \"a\\x61\"")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "aa"); } BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { - Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); - BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), std::string("aa\0abc", 6)); + Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE); + BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), std::string("aa\0abc", 6)); } BOOST_AUTO_TEST_CASE(string_escape_illegal) { - Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); - // TODO recovery from illegal tokens should be improved - BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); + // TODO recovery from illegal tokens should be improved + BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(hex_numbers) { - Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), 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.getCurrentLiteral(), "0x765432536763762734623472346"); - BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), 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.getCurrentLiteral(), "0x765432536763762734623472346"); + BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(locations) { - Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 0); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 19); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 20); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 23); - BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 24); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 25); - BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 26); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 27); - BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 27); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 32); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 45); - BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 50); - BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 0); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 19); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 20); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 23); + BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 24); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 25); + BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 26); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 27); + BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 27); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 32); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 45); + BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 50); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } BOOST_AUTO_TEST_CASE(ambiguities) { - // test scanning of some operators which need look-ahead - Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); - BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LTE); - BOOST_CHECK_EQUAL(scanner.next(), Token::LT); - BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); - BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN_ADD); - BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); - BOOST_CHECK_EQUAL(scanner.next(), Token::INC); - BOOST_CHECK_EQUAL(scanner.next(), Token::ARROW); - BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); + // test scanning of some operators which need look-ahead + Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); + BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LTE); + BOOST_CHECK_EQUAL(scanner.next(), Token::LT); + BOOST_CHECK_EQUAL(scanner.next(), Token::ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN_ADD); + BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); + BOOST_CHECK_EQUAL(scanner.next(), Token::INC); + BOOST_CHECK_EQUAL(scanner.next(), Token::ARROW); + BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); } BOOST_AUTO_TEST_SUITE_END() -} } } // end namespaces +} +} +} // end namespaces From e06c05e9d41de63981551716c2aefe8f7ee79626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 15:21:40 +0200 Subject: [PATCH 21/34] Fix missing include of cstdint and GCC warning. --- libevm/FeeStructure.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 8591afa14..60a158388 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -22,6 +22,8 @@ #pragma once +#include + #include namespace dev From c821c66d20ba39dbd97fec4aa9ab2036227e425a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 22:06:01 +0200 Subject: [PATCH 22/34] Revert FeeStructure changes except compile-time constants --- libethereum/Executive.cpp | 2 +- libethereum/Interface.h | 2 +- libevm/FeeStructure.cpp | 160 -------------------------------------- libevm/FeeStructure.h | 28 +++---- libevm/VM.h | 142 ++++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 183 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index f31683be0..193010cfa 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -66,7 +66,7 @@ bool Executive::setup(bytesConstRef _rlp) } // Check gas cost is enough. - u256 gasCost = u256(m_t.data.size()) * FeeStructure::c_txDataGas + FeeStructure::c_txGas; + u256 gasCost = m_t.data.size() * c_txDataGas + c_txGas; if (m_t.gas < gasCost) { diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 6a32e81c4..7ae650590 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -123,7 +123,7 @@ public: virtual Addresses addresses(int _block) const = 0; /// Get the fee associated for a transaction with the given data. - static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return FeeStructure::c_txDataGas * u256(_dataCount) + FeeStructure::c_txGas + _gas; } + static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const = 0; diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index aeeb19e27..b9974a3e7 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -21,163 +21,3 @@ */ #include "FeeStructure.h" - -#include - -#include "VM.h" - -namespace dev -{ -namespace eth -{ - -uint32_t FeeStructure::getInstructionFee(Instruction _inst) -{ - switch (_inst) - { - default: - BOOST_THROW_EXCEPTION(BadInstruction()); - - case Instruction::STOP: - case Instruction::SUICIDE: - return 0; - - case Instruction::SSTORE: - return c_sstoreGas; - - case Instruction::SLOAD: - return c_sloadGas; - - case Instruction::SHA3: - return c_sha3Gas; - - case Instruction::BALANCE: - return c_sha3Gas; - - case Instruction::CALL: - case Instruction::CALLCODE: - return c_callGas; - - case Instruction::CREATE: - return c_createGas; - - case Instruction::ADD: - case Instruction::MUL: - case Instruction::SUB: - case Instruction::DIV: - case Instruction::SDIV: - case Instruction::MOD: - case Instruction::SMOD: - case Instruction::EXP: - case Instruction::NEG: - case Instruction::LT: - case Instruction::GT: - case Instruction::SLT: - case Instruction::SGT: - case Instruction::EQ: - case Instruction::NOT: - case Instruction::AND: - case Instruction::OR: - case Instruction::XOR: - case Instruction::BYTE: - case Instruction::ADDMOD: - case Instruction::MULMOD: - case Instruction::ADDRESS: - case Instruction::ORIGIN: - case Instruction::CALLER: - case Instruction::CALLVALUE: - case Instruction::CALLDATALOAD: - case Instruction::CALLDATASIZE: - case Instruction::CODESIZE: - case Instruction::EXTCODESIZE: - case Instruction::GASPRICE: - case Instruction::PREVHASH: - case Instruction::COINBASE: - case Instruction::TIMESTAMP: - case Instruction::NUMBER: - case Instruction::DIFFICULTY: - case Instruction::GASLIMIT: - case Instruction::PUSH1: - case Instruction::PUSH2: - case Instruction::PUSH3: - case Instruction::PUSH4: - case Instruction::PUSH5: - case Instruction::PUSH6: - case Instruction::PUSH7: - case Instruction::PUSH8: - case Instruction::PUSH9: - case Instruction::PUSH10: - case Instruction::PUSH11: - case Instruction::PUSH12: - case Instruction::PUSH13: - case Instruction::PUSH14: - case Instruction::PUSH15: - case Instruction::PUSH16: - case Instruction::PUSH17: - case Instruction::PUSH18: - case Instruction::PUSH19: - case Instruction::PUSH20: - case Instruction::PUSH21: - case Instruction::PUSH22: - case Instruction::PUSH23: - case Instruction::PUSH24: - case Instruction::PUSH25: - case Instruction::PUSH26: - case Instruction::PUSH27: - case Instruction::PUSH28: - case Instruction::PUSH29: - case Instruction::PUSH30: - case Instruction::PUSH31: - case Instruction::PUSH32: - case Instruction::POP: - case Instruction::DUP1: - case Instruction::DUP2: - case Instruction::DUP3: - case Instruction::DUP4: - case Instruction::DUP5: - case Instruction::DUP6: - case Instruction::DUP7: - case Instruction::DUP8: - case Instruction::DUP9: - case Instruction::DUP10: - case Instruction::DUP11: - case Instruction::DUP12: - case Instruction::DUP13: - case Instruction::DUP14: - case Instruction::DUP15: - case Instruction::DUP16: - case Instruction::SWAP1: - case Instruction::SWAP2: - case Instruction::SWAP3: - case Instruction::SWAP4: - case Instruction::SWAP5: - case Instruction::SWAP6: - case Instruction::SWAP7: - case Instruction::SWAP8: - case Instruction::SWAP9: - case Instruction::SWAP10: - case Instruction::SWAP11: - case Instruction::SWAP12: - case Instruction::SWAP13: - case Instruction::SWAP14: - case Instruction::SWAP15: - case Instruction::SWAP16: - case Instruction::JUMP: - case Instruction::JUMPI: - case Instruction::PC: - case Instruction::MSIZE: - case Instruction::GAS: - case Instruction::JUMPDEST: - case Instruction::RETURN: - case Instruction::MSTORE: - case Instruction::MSTORE8: - case Instruction::MLOAD: - case Instruction::CALLDATACOPY: - case Instruction::CODECOPY: - case Instruction::EXTCODECOPY: - return c_stepGas; - } -} - -} -} \ No newline at end of file diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 60a158388..8c1519e86 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -22,10 +22,6 @@ #pragma once -#include - -#include - namespace dev { namespace eth @@ -35,21 +31,19 @@ enum class Instruction: uint8_t; struct FeeStructure { - static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. - static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. - static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. - static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. - static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). - static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. - static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. - static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. - static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. - - /// Returns step fee of the instruction. - /// In case of bad instruction code, throws BadInstruction exception. static uint32_t getInstructionFee(Instruction _inst); }; +static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. +static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. +static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. +static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. +static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). +static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. +static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. +static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. +static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. +static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. + } } diff --git a/libevm/VM.h b/libevm/VM.h index e97e5c4eb..401e30baf 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -101,17 +101,30 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con Instruction inst = (Instruction)_ext.getCode(m_curPC); // FEES... - bigint runGas = FeeStructure::getInstructionFee(inst); // throws BadInstruction + bigint runGas = c_stepGas; bigint newTempSize = m_temp.size(); switch (inst) { + case Instruction::STOP: + runGas = 0; + break; + + case Instruction::SUICIDE: + runGas = 0; + break; case Instruction::SSTORE: require(2); if (!_ext.store(m_stack.back()) && m_stack[m_stack.size() - 2]) - runGas = FeeStructure::c_sstoreGas * 2; + runGas = c_sstoreGas * 2; else if (_ext.store(m_stack.back()) && !m_stack[m_stack.size() - 2]) runGas = 0; + else + runGas = c_sstoreGas; + break; + + case Instruction::SLOAD: + runGas = c_sloadGas; break; // These all operate on memory and therefore potentially expand it: @@ -133,6 +146,7 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con break; case Instruction::SHA3: require(2); + runGas = c_sha3Gas; newTempSize = memNeed(m_stack.back(), m_stack[m_stack.size() - 2]); break; case Instruction::CALLDATACOPY: @@ -147,11 +161,20 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con require(4); newTempSize = memNeed(m_stack[m_stack.size() - 2], m_stack[m_stack.size() - 4]); break; + + case Instruction::BALANCE: + runGas = c_balanceGas; + break; case Instruction::CALL: + require(7); + runGas = c_callGas + m_stack[m_stack.size() - 1]; + newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); + break; + case Instruction::CALLCODE: require(7); - runGas += m_stack[m_stack.size() - 1]; + runGas = c_callGas + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; @@ -161,14 +184,125 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con auto inOff = m_stack[m_stack.size() - 2]; auto inSize = m_stack[m_stack.size() - 3]; newTempSize = inOff + inSize; + runGas = c_createGas; break; } + case Instruction::ADD: + case Instruction::MUL: + case Instruction::SUB: + case Instruction::DIV: + case Instruction::SDIV: + case Instruction::MOD: + case Instruction::SMOD: + case Instruction::EXP: + case Instruction::NEG: + case Instruction::LT: + case Instruction::GT: + case Instruction::SLT: + case Instruction::SGT: + case Instruction::EQ: + case Instruction::NOT: + case Instruction::AND: + case Instruction::OR: + case Instruction::XOR: + case Instruction::BYTE: + case Instruction::ADDMOD: + case Instruction::MULMOD: + case Instruction::ADDRESS: + case Instruction::ORIGIN: + case Instruction::CALLER: + case Instruction::CALLVALUE: + case Instruction::CALLDATALOAD: + case Instruction::CALLDATASIZE: + case Instruction::CODESIZE: + case Instruction::EXTCODESIZE: + case Instruction::GASPRICE: + case Instruction::PREVHASH: + case Instruction::COINBASE: + case Instruction::TIMESTAMP: + case Instruction::NUMBER: + case Instruction::DIFFICULTY: + case Instruction::GASLIMIT: + case Instruction::PUSH1: + case Instruction::PUSH2: + case Instruction::PUSH3: + case Instruction::PUSH4: + case Instruction::PUSH5: + case Instruction::PUSH6: + case Instruction::PUSH7: + case Instruction::PUSH8: + case Instruction::PUSH9: + case Instruction::PUSH10: + case Instruction::PUSH11: + case Instruction::PUSH12: + case Instruction::PUSH13: + case Instruction::PUSH14: + case Instruction::PUSH15: + case Instruction::PUSH16: + case Instruction::PUSH17: + case Instruction::PUSH18: + case Instruction::PUSH19: + case Instruction::PUSH20: + case Instruction::PUSH21: + case Instruction::PUSH22: + case Instruction::PUSH23: + case Instruction::PUSH24: + case Instruction::PUSH25: + case Instruction::PUSH26: + case Instruction::PUSH27: + case Instruction::PUSH28: + case Instruction::PUSH29: + case Instruction::PUSH30: + case Instruction::PUSH31: + case Instruction::PUSH32: + case Instruction::POP: + case Instruction::DUP1: + case Instruction::DUP2: + case Instruction::DUP3: + case Instruction::DUP4: + case Instruction::DUP5: + case Instruction::DUP6: + case Instruction::DUP7: + case Instruction::DUP8: + case Instruction::DUP9: + case Instruction::DUP10: + case Instruction::DUP11: + case Instruction::DUP12: + case Instruction::DUP13: + case Instruction::DUP14: + case Instruction::DUP15: + case Instruction::DUP16: + case Instruction::SWAP1: + case Instruction::SWAP2: + case Instruction::SWAP3: + case Instruction::SWAP4: + case Instruction::SWAP5: + case Instruction::SWAP6: + case Instruction::SWAP7: + case Instruction::SWAP8: + case Instruction::SWAP9: + case Instruction::SWAP10: + case Instruction::SWAP11: + case Instruction::SWAP12: + case Instruction::SWAP13: + case Instruction::SWAP14: + case Instruction::SWAP15: + case Instruction::SWAP16: + case Instruction::JUMP: + case Instruction::JUMPI: + case Instruction::PC: + case Instruction::MSIZE: + case Instruction::GAS: + case Instruction::JUMPDEST: + break; + default: + BOOST_THROW_EXCEPTION(BadInstruction()); } newTempSize = (newTempSize + 31) / 32 * 32; if (newTempSize > m_temp.size()) - runGas += FeeStructure::c_memoryGas * (newTempSize - m_temp.size()) / 32; + runGas += c_memoryGas * (newTempSize - m_temp.size()) / 32; if (_onOp) _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext); From 465e682dc61e6e0cf55a25a1bd94356a004ac673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 22:15:47 +0200 Subject: [PATCH 23/34] Prevent integer overflow in some gas calculations --- libethereum/Executive.cpp | 2 +- libethereum/Interface.h | 2 +- libevm/VM.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 193010cfa..fe5b21761 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -66,7 +66,7 @@ bool Executive::setup(bytesConstRef _rlp) } // Check gas cost is enough. - u256 gasCost = m_t.data.size() * c_txDataGas + c_txGas; + u256 gasCost = u256(m_t.data.size()) * c_txDataGas + c_txGas; if (m_t.gas < gasCost) { diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 7ae650590..e746ae760 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -123,7 +123,7 @@ public: virtual Addresses addresses(int _block) const = 0; /// Get the fee associated for a transaction with the given data. - static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } + static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return u256(c_txDataGas) * _dataCount + c_txGas + _gas; } /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const = 0; diff --git a/libevm/VM.h b/libevm/VM.h index 401e30baf..4cfac2783 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -168,13 +168,13 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::CALL: require(7); - runGas = c_callGas + m_stack[m_stack.size() - 1]; + runGas = bigint(c_callGas) + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; case Instruction::CALLCODE: require(7); - runGas = c_callGas + m_stack[m_stack.size() - 1]; + runGas = bigint(c_callGas) + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; From 6b4a46dfd9b4133706b94c537c7568ce798902f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 22:18:39 +0200 Subject: [PATCH 24/34] Remove unused stuff --- libevm/FeeStructure.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 8c1519e86..2aca43799 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -27,13 +27,6 @@ namespace dev namespace eth { -enum class Instruction: uint8_t; - -struct FeeStructure -{ - static uint32_t getInstructionFee(Instruction _inst); -}; - static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. From 374031902a26ad811a37de2da6ff1b8c705c1bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 16 Oct 2014 23:50:58 +0200 Subject: [PATCH 25/34] Missing include again. I need to start building with GCC. --- libevm/FeeStructure.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 2aca43799..6fa30dea4 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -22,6 +22,8 @@ #pragma once +#include + namespace dev { namespace eth From 116070f304c712d059032943518e9f56089bfb65 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 16 Oct 2014 23:49:45 +0200 Subject: [PATCH 26/34] Coding style and cleanup --- libsolidity/AST.cpp | 29 +----- libsolidity/AST.h | 138 ++++++++++++---------------- libsolidity/ASTPrinter.cpp | 4 +- libsolidity/ASTPrinter.h | 2 +- libsolidity/BaseTypes.h | 4 +- libsolidity/Exceptions.h | 6 +- libsolidity/NameAndTypeResolver.cpp | 8 +- libsolidity/NameAndTypeResolver.h | 9 +- libsolidity/Parser.cpp | 98 +++++++------------- libsolidity/Scanner.cpp | 74 ++++++++------- libsolidity/Scanner.h | 113 +++++++---------------- libsolidity/Scope.cpp | 1 - libsolidity/Scope.h | 2 +- libsolidity/Types.h | 26 +++--- 14 files changed, 199 insertions(+), 315 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 355ff1bd1..c1a3faa55 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -45,18 +45,14 @@ void ContractDefinition::accept(ASTVisitor& _visitor) void StructDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { listAccept(m_members, _visitor); - } _visitor.endVisit(*this); } void ParameterList::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { listAccept(m_parameters, _visitor); - } _visitor.endVisit(*this); } @@ -75,10 +71,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) void VariableDeclaration::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { if (m_typeName) m_typeName->accept(_visitor); - } _visitor.endVisit(*this); } @@ -119,9 +113,7 @@ void Statement::accept(ASTVisitor& _visitor) void Block::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { listAccept(m_statements, _visitor); - } _visitor.endVisit(*this); } @@ -168,10 +160,8 @@ void Break::accept(ASTVisitor& _visitor) void Return::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { if (m_expression) m_expression->accept(_visitor); - } _visitor.endVisit(*this); } @@ -199,9 +189,7 @@ void Assignment::accept(ASTVisitor& _visitor) void UnaryOperation::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { m_subExpression->accept(_visitor); - } _visitor.endVisit(*this); } @@ -228,9 +216,7 @@ void FunctionCall::accept(ASTVisitor& _visitor) void MemberAccess::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) - { m_expression->accept(_visitor); - } _visitor.endVisit(*this); } @@ -272,7 +258,7 @@ void Statement::expectType(Expression& _expression, const Type& _expectedType) ptr Block::checkTypeRequirements() { - for (ptr const & statement : m_statements) + for (ptr const& statement: m_statements) statement->checkTypeRequirements(); return ptr(); } @@ -281,7 +267,8 @@ ptr IfStatement::checkTypeRequirements() { expectType(*m_condition, BoolType()); m_trueBody->checkTypeRequirements(); - if (m_falseBody) m_falseBody->checkTypeRequirements(); + if (m_falseBody) + m_falseBody->checkTypeRequirements(); return ptr(); } @@ -324,14 +311,10 @@ ptr VariableDefinition::checkTypeRequirements() if (m_value) { if (m_variable->getType()) - { expectType(*m_value, *m_variable->getType()); - } else - { // no type declared and no previous assignment, infer the type m_variable->setType(m_value->checkTypeRequirements()); - } } return ptr(); } @@ -371,9 +354,7 @@ ptr BinaryOperation::checkTypeRequirements() else BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation.")); if (Token::isCompareOp(m_operator)) - { m_type = std::make_shared(); - } else { BOOST_ASSERT(Token::isBinaryOp(m_operator)); @@ -387,7 +368,7 @@ ptr BinaryOperation::checkTypeRequirements() ptr FunctionCall::checkTypeRequirements() { m_expression->checkTypeRequirements(); - for (ptr const & argument : m_arguments) + for (ptr const& argument: m_arguments) argument->checkTypeRequirements(); ptr expressionType = m_expression->getType(); Type::Category const category = expressionType->getCategory(); @@ -418,11 +399,9 @@ ptr FunctionCall::checkTypeRequirements() BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for " "function call.")); for (size_t i = 0; i < m_arguments.size(); ++i) - { if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Invalid type for argument in " "function call.")); - } // @todo actually the return type should be an anonymous struct, // but we change it to the type of the first return value until we have structs if (fun.getReturnParameterList()->getParameters().empty()) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index a289455cb..8da3f529d 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -40,7 +40,7 @@ namespace solidity class ASTVisitor; -class ASTNode : private boost::noncopyable +class ASTNode: private boost::noncopyable { public: explicit ASTNode(Location const& _location) @@ -53,7 +53,7 @@ public: template static void listAccept(vecptr& _list, ASTVisitor& _visitor) { - for (ptr& element : _list) + for (ptr& element: _list) element->accept(_visitor); } @@ -62,7 +62,7 @@ private: Location m_location; }; -class Declaration : public ASTNode +class Declaration: public ASTNode { public: Declaration(Location const& _location, ptr const& _name) @@ -73,7 +73,7 @@ private: ptr m_name; }; -class ContractDefinition : public Declaration +class ContractDefinition: public Declaration { public: ContractDefinition(Location const& _location, @@ -98,14 +98,13 @@ private: vecptr m_definedFunctions; }; -class StructDefinition : public Declaration +class StructDefinition: public Declaration { public: StructDefinition(Location const& _location, ptr const& _name, vecptr const& _members) - : Declaration(_location, _name), m_members(_members) - {} + : Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; private: @@ -115,12 +114,11 @@ private: /// Used as function parameter list and return list /// None of the parameters is allowed to contain mappings (not even recursively /// inside structs) -class ParameterList : public ASTNode +class ParameterList: public ASTNode { public: ParameterList(Location const& _location, vecptr const& _parameters) - : ASTNode(_location), m_parameters(_parameters) - {} + : ASTNode(_location), m_parameters(_parameters) {} virtual void accept(ASTVisitor& _visitor) override; vecptr const& getParameters() { return m_parameters; } @@ -128,7 +126,7 @@ private: vecptr m_parameters; }; -class FunctionDefinition : public Declaration +class FunctionDefinition: public Declaration { public: FunctionDefinition(Location const& _location, ptr const& _name, bool _isPublic, @@ -138,8 +136,7 @@ public: ptr const& _body) : Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), - m_body(_body) - {} + m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; bool isPublic() const { return m_isPublic; } @@ -156,14 +153,12 @@ private: ptr m_body; }; -class VariableDeclaration : public Declaration +class VariableDeclaration: public Declaration { public: - VariableDeclaration(Location const& _location, - ptr const& _type, + VariableDeclaration(Location const& _location, ptr const& _type, ptr const& _name) - : Declaration(_location, _name), m_typeName(_type) - {} + : Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; bool isTypeGivenExplicitly() const { return m_typeName.get() != nullptr; } @@ -182,22 +177,21 @@ private: /// types /// @{ -class TypeName : public ASTNode +class TypeName: public ASTNode { public: - explicit TypeName(Location const& _location) : ASTNode(_location) {} + explicit TypeName(Location const& _location): ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr toType() = 0; }; /// any pre-defined type that is not a mapping -class ElementaryTypeName : public TypeName +class ElementaryTypeName: public TypeName { public: explicit ElementaryTypeName(Location const& _location, Token::Value _type) - : TypeName(_location), m_type(_type) - {} + : TypeName(_location), m_type(_type) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr toType() override { return Type::fromElementaryTypeName(m_type); } @@ -206,12 +200,11 @@ private: Token::Value m_type; }; -class UserDefinedTypeName : public TypeName +class UserDefinedTypeName: public TypeName { public: UserDefinedTypeName(Location const& _location, ptr const& _name) - : TypeName(_location), m_name(_name) - {} + : TypeName(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr toType() override { return Type::fromUserDefinedTypeName(*this); } @@ -224,13 +217,12 @@ private: StructDefinition* m_referencedStruct; }; -class Mapping : public TypeName +class Mapping: public TypeName { public: Mapping(Location const& _location, ptr const& _keyType, ptr const& _valueType) - : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) - {} + : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr toType() override { return Type::fromMapping(*this); } private: @@ -243,10 +235,10 @@ private: /// Statements /// @{ -class Statement : public ASTNode +class Statement: public ASTNode { public: - explicit Statement(Location const& _location) : ASTNode(_location) {} + explicit Statement(Location const& _location): ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; //! Check all type requirements, throws exception if some requirement is not met. @@ -259,12 +251,11 @@ protected: void expectType(Expression& _expression, Type const& _expectedType); }; -class Block : public Statement +class Block: public Statement { public: Block(Location const& _location, vecptr const& _statements) - : Statement(_location), m_statements(_statements) - {} + : Statement(_location), m_statements(_statements) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; @@ -272,14 +263,13 @@ private: vecptr m_statements; }; -class IfStatement : public Statement +class IfStatement: public Statement { public: IfStatement(Location const& _location, ptr const& _condition, ptr const& _trueBody, ptr const& _falseBody) : Statement(_location), m_condition(_condition), - m_trueBody(_trueBody), m_falseBody(_falseBody) - {} + m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; private: @@ -288,20 +278,19 @@ private: ptr m_falseBody; //< "else" part, optional }; -class BreakableStatement : public Statement +class BreakableStatement: public Statement { public: - BreakableStatement(Location const& _location) : Statement(_location) {} + BreakableStatement(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; }; -class WhileStatement : public BreakableStatement +class WhileStatement: public BreakableStatement { public: WhileStatement(Location const& _location, ptr const& _condition, ptr const& _body) - : BreakableStatement(_location), m_condition(_condition), m_body(_body) - {} + : BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; private: @@ -309,28 +298,27 @@ private: ptr m_body; }; -class Continue : public Statement +class Continue: public Statement { public: - Continue(Location const& _location) : Statement(_location) {} + Continue(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; }; -class Break : public Statement +class Break: public Statement { public: - Break(Location const& _location) : Statement(_location) {} + Break(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; }; -class Return : public Statement +class Return: public Statement { public: Return(Location const& _location, ptr _expression) - : Statement(_location), m_expression(_expression) - {} + : Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; @@ -341,13 +329,12 @@ private: ParameterList* m_returnParameters; //< extracted from the function declaration }; -class VariableDefinition : public Statement +class VariableDefinition: public Statement { public: VariableDefinition(Location const& _location, ptr _variable, ptr _value) - : Statement(_location), m_variable(_variable), m_value(_value) - {} + : Statement(_location), m_variable(_variable), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; @@ -356,10 +343,10 @@ private: ptr m_value; ///< can be missing }; -class Expression : public Statement +class Expression: public Statement { public: - Expression(Location const& _location) : Statement(_location) {} + Expression(Location const& _location): Statement(_location) {} ptr const& getType() { return m_type; } protected: ptr m_type; @@ -370,14 +357,13 @@ protected: /// Expressions /// @{ -class Assignment : public Expression +class Assignment: public Expression { public: Assignment(Location const& _location, ptr const& _leftHandSide, Token::Value _assignmentOperator, ptr const& _rightHandSide) : Expression(_location), m_leftHandSide(_leftHandSide), - m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) - {} + m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; @@ -388,14 +374,13 @@ private: ptr m_rightHandSide; }; -class UnaryOperation : public Expression +class UnaryOperation: public Expression { public: UnaryOperation(Location const& _location, Token::Value _operator, ptr const& _subExpression, bool _isPrefix) : Expression(_location), m_operator(_operator), - m_subExpression(_subExpression), m_isPrefix(_isPrefix) - {} + m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; @@ -407,13 +392,12 @@ private: bool m_isPrefix; }; -class BinaryOperation : public Expression +class BinaryOperation: public Expression { public: BinaryOperation(Location const& _location, ptr const& _left, Token::Value _operator, ptr const& _right) - : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) - {} + : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; @@ -427,13 +411,12 @@ private: }; /// Can be ordinary function call, type cast or struct construction. -class FunctionCall : public Expression +class FunctionCall: public Expression { public: FunctionCall(Location const& _location, ptr const& _expression, vecptr const& _arguments) - : Expression(_location), m_expression(_expression), m_arguments(_arguments) - {} + : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; private: @@ -441,13 +424,12 @@ private: vecptr m_arguments; }; -class MemberAccess : public Expression +class MemberAccess: public Expression { public: MemberAccess(Location const& _location, ptr _expression, ptr const& _memberName) - : Expression(_location), m_expression(_expression), m_memberName(_memberName) - {} + : Expression(_location), m_expression(_expression), m_memberName(_memberName) {} virtual void accept(ASTVisitor& _visitor) override; const ASTString& getMemberName() const { return *m_memberName; } virtual ptr checkTypeRequirements() override; @@ -456,13 +438,12 @@ private: ptr m_memberName; }; -class IndexAccess : public Expression +class IndexAccess: public Expression { public: IndexAccess(Location const& _location, ptr const& _base, ptr const& _index) - : Expression(_location), m_base(_base), m_index(_index) - {} + : Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; private: @@ -470,13 +451,13 @@ private: ptr m_index; }; -class PrimaryExpression : public Expression +class PrimaryExpression: public Expression { public: - PrimaryExpression(Location const& _location) : Expression(_location) {} + PrimaryExpression(Location const& _location): Expression(_location) {} }; -class Identifier : public PrimaryExpression +class Identifier: public PrimaryExpression { public: Identifier(Location const& _location, ptr const& _name) @@ -494,7 +475,7 @@ private: Declaration* m_referencedDeclaration; }; -class ElementaryTypeNameExpression : public PrimaryExpression +class ElementaryTypeNameExpression: public PrimaryExpression { public: ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken) @@ -507,12 +488,11 @@ private: Token::Value m_typeToken; }; -class Literal : public PrimaryExpression +class Literal: public PrimaryExpression { public: Literal(Location const& _location, Token::Value _token, ptr const& _value) - : PrimaryExpression(_location), m_token(_token), m_value(_value) - {} + : PrimaryExpression(_location), m_token(_token), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 7c48dacb3..cd8263f2c 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -244,7 +244,7 @@ bool ASTPrinter::visit(Literal& _node) { const char* tokenString = Token::toString(_node.getToken()); if (tokenString == nullptr) - tokenString = "----"; + tokenString = "[no token]"; writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); printSourcePart(_node); return goDeeper(); @@ -255,8 +255,6 @@ void ASTPrinter::endVisit(ASTNode&) m_indentation--; } -// @todo instead of this, we could make the default implementation of endVisit call the -// superclass' endVisit void ASTPrinter::endVisit(ContractDefinition&) { m_indentation--; diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 52f349919..00c681f34 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -30,7 +30,7 @@ namespace dev namespace solidity { -class ASTPrinter : public ASTVisitor +class ASTPrinter: public ASTVisitor { public: /// Create a printer for the given abstract syntax tree. If the source is specified, diff --git a/libsolidity/BaseTypes.h b/libsolidity/BaseTypes.h index c8926b6a7..fdf3f7b53 100644 --- a/libsolidity/BaseTypes.h +++ b/libsolidity/BaseTypes.h @@ -32,8 +32,8 @@ namespace solidity /// The interval includes start and excludes end. struct Location { - Location(int _start, int _end) : start(_start), end(_end) { } - Location() : start(-1), end(-1) { } + Location(int _start, int _end): start(_start), end(_end) { } + Location(): start(-1), end(-1) { } bool IsValid() const { return start >= 0 && end >= start; } diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h index c14e0d797..c600ebf12 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -29,9 +29,9 @@ namespace dev namespace solidity { -struct ParserError : virtual Exception {}; -struct TypeError : virtual Exception {}; -struct DeclarationError : virtual Exception {}; +struct ParserError: virtual Exception {}; +struct TypeError: virtual Exception {}; +struct DeclarationError: virtual Exception {}; } } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index ada987b08..628d94769 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -42,18 +42,18 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) DeclarationRegistrationHelper registrar(m_scopes, _contract); m_currentScope = &m_scopes[&_contract]; //@todo structs - for (ptr const & variable : _contract.getStateVariables()) + for (ptr const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, nullptr); - for (ptr const & function : _contract.getDefinedFunctions()) + for (ptr const& function: _contract.getDefinedFunctions()) { m_currentScope = &m_scopes[function.get()]; ReferencesResolver referencesResolver(*function, *this, function->getReturnParameterList().get()); } - // First, all function parameter types need to be resolved before we can check + // First, the parameter types of all functions need to be resolved before we can check // the types, since it is possible to call functions that are only defined later // in the source. - for (ptr const & function : _contract.getDefinedFunctions()) + for (ptr const& function: _contract.getDefinedFunctions()) { m_currentScope = &m_scopes[function.get()]; function->getBody().checkTypeRequirements(); diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 346c9e644..076b1389f 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -34,8 +34,9 @@ namespace dev namespace solidity { - -class NameAndTypeResolver : private boost::noncopyable +//! Resolves name references, resolves all types and checks that all operations are valid for the +//! inferred types. An exception is throw on the first error. +class NameAndTypeResolver: private boost::noncopyable { public: NameAndTypeResolver(); @@ -54,7 +55,7 @@ private: //! Traverses the given AST upon construction and fills _scopes with all declarations inside the //! AST. -class DeclarationRegistrationHelper : private ASTVisitor +class DeclarationRegistrationHelper: private ASTVisitor { public: DeclarationRegistrationHelper(std::map& _scopes, ASTNode& _astRoot); @@ -79,7 +80,7 @@ private: //! Resolves references to declarations (of variables and types) and also establishes the link //! between a return statement and the return parameter list. -class ReferencesResolver : private ASTVisitor +class ReferencesResolver: private ASTVisitor { public: ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ParameterList* _returnParameters); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 5a329ae71..945cec0f7 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -43,27 +43,13 @@ ptr Parser::parse(std::shared_ptr const& _scanner) class Parser::ASTNodeFactory { public: - ASTNodeFactory(const Parser& _parser) - : m_parser(_parser), m_location(_parser.getPosition(), -1) - {} - - void markEndPosition() - { - m_location.end = m_parser.getEndPosition(); - } - - void setLocationEmpty() - { - m_location.end = m_location.start; - } + ASTNodeFactory(const Parser& _parser) : m_parser(_parser), m_location(_parser.getPosition(), -1) {} + void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + void setLocationEmpty() { m_location.end = m_location.start; } /// Set the end position to the one of the given node. - void setEndPositionFromNode(const ptr& _node) - { - m_location.end = _node->getLocation().end; - } + void setEndPositionFromNode(const ptr& _node) { m_location.end = _node->getLocation().end; } - /// @todo: check that this actually uses perfect forwarding template ptr createNode(Args&& ... _args) { @@ -102,9 +88,7 @@ ptr Parser::parseContractDefinition() { Token::Value currentToken = m_scanner->getCurrentToken(); if (currentToken == Token::RBRACE) - { break; - } else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) { visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); @@ -112,13 +96,9 @@ ptr Parser::parseContractDefinition() expectToken(Token::COLON); } else if (currentToken == Token::FUNCTION) - { functions.push_back(parseFunctionDefinition(visibilityIsPublic)); - } else if (currentToken == Token::STRUCT) - { structs.push_back(parseStructDefinition()); - } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::isElementaryTypeName(currentToken)) { @@ -127,9 +107,7 @@ ptr Parser::parseContractDefinition() expectToken(Token::SEMICOLON); } else - { throwExpectationError("Function, variable or struct declaration expected."); - } } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); @@ -221,9 +199,7 @@ ptr Parser::parseTypeName(bool _allowVar) type = nodeFactory.createNode(expectIdentifierToken()); } else - { throwExpectationError("Expected type name"); - } return type; } @@ -271,9 +247,7 @@ ptr Parser::parseBlock() expectToken(Token::LBRACE); vecptr statements; while (m_scanner->getCurrentToken() != Token::RBRACE) - { statements.push_back(parseStatement()); - } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); return nodeFactory.createNode(statements); @@ -318,15 +292,10 @@ ptr Parser::parseStatement() m_scanner->getCurrentToken() == Token::VAR || Token::isElementaryTypeName(m_scanner->getCurrentToken()) || (m_scanner->getCurrentToken() == Token::IDENTIFIER && - m_scanner->peek() == Token::IDENTIFIER)) - { + m_scanner->peekNextToken() == Token::IDENTIFIER)) statement = parseVariableDefinition(); - } - else - { - // "ordinary" expression + else // "ordinary" expression statement = parseExpression(); - } } expectToken(Token::SEMICOLON); return statement; @@ -348,9 +317,7 @@ ptr Parser::parseIfStatement() nodeFactory.setEndPositionFromNode(falseBody); } else - { nodeFactory.setEndPositionFromNode(trueBody); - } return nodeFactory.createNode(condition, trueBody, falseBody); } @@ -379,9 +346,7 @@ ptr Parser::parseVariableDefinition() nodeFactory.setEndPositionFromNode(value); } else - { nodeFactory.setEndPositionFromNode(variable); - } return nodeFactory.createNode(variable, value); } @@ -450,29 +415,29 @@ ptr Parser::parseLeftHandSideExpression() switch (m_scanner->getCurrentToken()) { case Token::LBRACK: - { - m_scanner->next(); - ptr index = parseExpression(); - nodeFactory.markEndPosition(); - expectToken(Token::RBRACK); - expression = nodeFactory.createNode(expression, index); - } + { + m_scanner->next(); + ptr index = parseExpression(); + nodeFactory.markEndPosition(); + expectToken(Token::RBRACK); + expression = nodeFactory.createNode(expression, index); + } break; case Token::PERIOD: - { - m_scanner->next(); - nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(expression, expectIdentifierToken()); - } + { + m_scanner->next(); + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode(expression, expectIdentifierToken()); + } break; case Token::LPAREN: - { - m_scanner->next(); - vecptr arguments = parseFunctionCallArguments(); - nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); - expression = nodeFactory.createNode(expression, arguments); - } + { + m_scanner->next(); + vecptr arguments = parseFunctionCallArguments(); + nodeFactory.markEndPosition(); + expectToken(Token::RPAREN); + expression = nodeFactory.createNode(expression, arguments); + } break; default: return expression; @@ -502,12 +467,12 @@ ptr Parser::parsePrimaryExpression() expression = nodeFactory.createNode(getLiteralAndAdvance()); break; case Token::LPAREN: - { - m_scanner->next(); - ptr expression = parseExpression(); - expectToken(Token::RPAREN); - return expression; - } + { + m_scanner->next(); + ptr expression = parseExpression(); + expectToken(Token::RPAREN); + return expression; + } default: if (Token::isElementaryTypeName(token)) { @@ -520,6 +485,7 @@ ptr Parser::parsePrimaryExpression() throwExpectationError("Expected primary expression."); return ptr(); // this is not reached } + break; } return expression; } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index c2875c9cd..61f56f292 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -103,11 +103,11 @@ void Scanner::reset(const CharStream& _source) } -bool Scanner::scanHexNumber(char& scanned_number, int expected_length) +bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength) { - BOOST_ASSERT(expected_length <= 4); // prevent overflow + BOOST_ASSERT(_expectedLength <= 4); // prevent overflow char x = 0; - for (int i = 0; i < expected_length; i++) + for (int i = 0; i < _expectedLength; i++) { int d = HexValue(m_char); if (d < 0) @@ -118,7 +118,7 @@ bool Scanner::scanHexNumber(char& scanned_number, int expected_length) x = x * 16 + d; advance(); } - scanned_number = x; + o_scannedNumber = x; return true; } @@ -129,24 +129,25 @@ BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); Token::Value Scanner::next() { m_current_token = m_next_token; - m_hasLineTerminatorBeforeNext = false; - m_hasMultilineCommentBeforeNext = false; scanToken(); return m_current_token.token; } +Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) +{ + advance(); + if (m_char == _next) + selectToken(_then); + else + return _else; +} + bool Scanner::skipWhitespace() { const int start_position = getSourcePos(); - while (true) - { - if (IsLineTerminator(m_char)) - m_hasLineTerminatorBeforeNext = true; - else if (!IsWhiteSpace(m_char)) - break; + while (IsWhiteSpace(m_char)) advance(); - } // Return whether or not we skipped any characters. return getSourcePos() != start_position; } @@ -170,12 +171,7 @@ Token::Value Scanner::skipMultiLineComment() { char ch = m_char; advance(); - if (IsLineTerminator(ch)) - { - // Following ECMA-262, section 7.4, a comment containing - // a newline will make the comment count as a line-terminator. - m_hasMultilineCommentBeforeNext = true; - } + // If we have reached the end of the multi-line comment, we // consume the '/' and insert a whitespace. This way all // multi-line comments are treated as whitespace. @@ -199,8 +195,7 @@ void Scanner::scanToken() m_next_token.location.start = getSourcePos(); switch (m_char) { - case '\n': - m_hasLineTerminatorBeforeNext = true; // fall-through + case '\n': // fall-through case ' ': case '\t': token = selectToken(Token::WHITESPACE); @@ -395,37 +390,35 @@ bool Scanner::scanEscape() switch (c) { case '\'': // fall through - case '"' : // fall through + case '"': // fall through case '\\': break; - case 'b' : + case 'b': c = '\b'; break; - case 'f' : + case 'f': c = '\f'; break; - case 'n' : + case 'n': c = '\n'; break; - case 'r' : + case 'r': c = '\r'; break; - case 't' : + case 't': c = '\t'; break; - case 'u' : + case 'u': if (!scanHexNumber(c, 4)) return false; break; - case 'v' : + case 'v': c = '\v'; break; - case 'x' : + case 'x': if (!scanHexNumber(c, 2)) return false; break; } - // According to ECMA-262, section 7.8.4, characters not covered by the - // above cases should be illegal, but they are commonly handled as - // non-escaped characters by JS VMs. + addLiteralChar(c); return true; } @@ -653,6 +646,21 @@ Token::Value Scanner::scanIdentifierOrKeyword() return KeywordOrIdentifierToken(m_next_token.literal); } +char CharStream::advanceAndGet() +{ + if (isPastEndOfInput()) return 0; + ++m_pos; + if (isPastEndOfInput()) return 0; + return get(); +} + +char CharStream::rollback(size_t _amount) +{ + BOOST_ASSERT(m_pos >= _amount); + m_pos -= _amount; + return get(); +} + std::string CharStream::getLineAtPosition(int _position) const { // if _position points to \n, it returns the line before the \n diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index ea7c6b71a..f4fec5236 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -63,27 +63,13 @@ class ParserRecorder; class CharStream { public: - CharStream() - : m_pos(0) - {} - - explicit CharStream(const std::string& _source) : m_source(_source), m_pos(0) {} + CharStream() : m_pos(0) {} + explicit CharStream(const std::string& _source): m_source(_source), m_pos(0) {} int getPos() const { return m_pos; } bool isPastEndOfInput() const { return m_pos >= m_source.size(); } char get() const { return m_source[m_pos]; } - char advanceAndGet() - { - if (isPastEndOfInput()) return 0; - ++m_pos; - if (isPastEndOfInput()) return 0; - return get(); - } - char rollback(size_t _amount) - { - BOOST_ASSERT(m_pos >= _amount); - m_pos -= _amount; - return get(); - } + char advanceAndGet(); + char rollback(size_t _amount); /// Functions that help pretty-printing parse errors /// Do only use in error cases, they are quite expensive. @@ -96,8 +82,6 @@ private: size_t m_pos; }; -// ---------------------------------------------------------------------------- -// JavaScript Scanner. class Scanner { @@ -107,7 +91,7 @@ public: class LiteralScope { public: - explicit LiteralScope(Scanner* self) : scanner_(self), complete_(false) { scanner_->startNewLiteral(); } + explicit LiteralScope(Scanner* self): scanner_(self), complete_(false) { scanner_->startNewLiteral(); } ~LiteralScope() { if (!complete_) scanner_->dropLiteral(); } void Complete() { complete_ = true; } @@ -118,47 +102,36 @@ public: explicit Scanner(const CharStream& _source); - // Resets the scanner as if newly constructed with _input as input. + /// Resets the scanner as if newly constructed with _input as input. void reset(const CharStream& _source); - // Returns the next token and advances input. + /// Returns the next token and advances input. Token::Value next(); - // Returns the current token again. + + /// Information about the current token + /// @{ + + /// Returns the current token Token::Value getCurrentToken() { return m_current_token.token; } - // Returns the location information for the current token - // (the token last returned by Next()). Location getCurrentLocation() const { return m_current_token.location; } const std::string& getCurrentLiteral() const { return m_current_token.literal; } + /// @} - // Similar functions for the upcoming token. - - // One token look-ahead (past the token returned by Next()). - Token::Value peek() const { return m_next_token.token; } - + /// Information about the next token + /// @{ + /// Returns the next token without advancing input. + Token::Value peekNextToken() const { return m_next_token.token; } Location peekLocation() const { return m_next_token.location; } const std::string& peekLiteral() const { return m_next_token.literal; } + /// @} /// Functions that help pretty-printing parse errors. /// Do only use in error cases, they are quite expensive. /// @{ - std::string getLineAtPosition(int _position) const - { - return m_source.getLineAtPosition(_position); - } - std::tuple translatePositionToLineColumn(int _position) const - { - return m_source.translatePositionToLineColumn(_position); - } + std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } + std::tuple translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } /// @} - // Returns true if there was a line terminator before the peek'ed token, - // possibly inside a multi-line comment. - bool hasAnyLineTerminatorBeforeNext() const - { - return m_hasLineTerminatorBeforeNext || - m_hasMultilineCommentBeforeNext; - } - private: // Used for the current and look-ahead token. struct TokenDesc @@ -168,34 +141,22 @@ private: std::string literal; }; - // Literal buffer support + /// Literal buffer support + /// @{ inline void startNewLiteral() { m_next_token.literal.clear(); } - inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); } - inline void dropLiteral() { m_next_token.literal.clear(); } - inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } + /// @} - // Low-level scanning support. bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } - void rollback(int amount) { m_char = m_source.rollback(amount); } + void rollback(int _amount) { m_char = m_source.rollback(_amount); } - inline Token::Value selectToken(Token::Value tok) { advance(); return tok; } + inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; } + /// If the next character is _next, advance and return _then, otherwise return _else. + inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else); - inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) - { - advance(); - if (m_char == next) - { - advance(); - return then; - } - else - return else_; - } - - bool scanHexNumber(char& scanned_number, int expected_length); + bool scanHexNumber(char& o_scannedNumber, int _expectedLength); // Scans a single JavaScript token. void scanToken(); @@ -210,12 +171,12 @@ private: Token::Value scanString(); - // Scans an escape-sequence which is part of a string and adds the - // decoded character to the current literal. Returns true if a pattern - // is scanned. + /// Scans an escape-sequence which is part of a string and adds the + /// decoded character to the current literal. Returns true if a pattern + /// is scanned. bool scanEscape(); - // Return the current source position. + /// Return the current source position. int getSourcePos() { return m_source.getPos(); } bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } @@ -224,16 +185,8 @@ private: CharStream m_source; - // one character look-ahead, equals 0 at end of input + /// one character look-ahead, equals 0 at end of input char m_char; - - // Whether there is a line terminator whitespace character after - // the current token, and before the next. Does not count newlines - // inside multiline comments. - bool m_hasLineTerminatorBeforeNext; - // Whether there is a multi-line comment that contains a - // line-terminator after the current token, and before the next. - bool m_hasMultilineCommentBeforeNext; }; } diff --git a/libsolidity/Scope.cpp b/libsolidity/Scope.cpp index 7c298b723..28a54dd27 100644 --- a/libsolidity/Scope.cpp +++ b/libsolidity/Scope.cpp @@ -28,7 +28,6 @@ namespace dev namespace solidity { - bool Scope::registerDeclaration(Declaration& _declaration) { if (m_declarations.find(_declaration.getName()) != m_declarations.end()) diff --git a/libsolidity/Scope.h b/libsolidity/Scope.h index b3686ec0e..ced783261 100644 --- a/libsolidity/Scope.h +++ b/libsolidity/Scope.h @@ -36,7 +36,7 @@ namespace solidity class Scope { public: - explicit Scope(Scope* _outerScope = nullptr) : m_outerScope(_outerScope) {} + explicit Scope(Scope* _outerScope = nullptr): m_outerScope(_outerScope) {} /// Registers the declaration in the scope unless its name is already declared. Returns true iff /// it was not yet declared. bool registerDeclaration(Declaration& _declaration); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index aed8a7e7c..d9455a501 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -48,7 +48,7 @@ using ptr = std::shared_ptr; // @todo realMxN, string, mapping -class Type : private boost::noncopyable +class Type: private boost::noncopyable { public: enum class Category @@ -73,7 +73,7 @@ public: virtual bool acceptsUnaryOperator(Token::Value) const { return false; } }; -class IntegerType : public Type +class IntegerType: public Type { public: enum class Modifier @@ -100,7 +100,7 @@ private: Modifier m_modifier; }; -class BoolType : public Type +class BoolType: public Type { public: virtual Category getCategory() const { return Category::BOOL; } @@ -119,21 +119,21 @@ public: } }; -class ContractType : public Type +class ContractType: public Type { public: virtual Category getCategory() const { return Category::CONTRACT; } - ContractType(ContractDefinition const& _contract) : m_contract(_contract) {} + ContractType(ContractDefinition const& _contract): m_contract(_contract) {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; private: ContractDefinition const& m_contract; }; -class StructType : public Type +class StructType: public Type { public: virtual Category getCategory() const { return Category::STRUCT; } - StructType(StructDefinition const& _struct) : m_struct(_struct) {} + StructType(StructDefinition const& _struct): m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; virtual bool acceptsUnaryOperator(Token::Value _operator) const override { @@ -143,18 +143,18 @@ private: StructDefinition const& m_struct; }; -class FunctionType : public Type +class FunctionType: public Type { public: virtual Category getCategory() const { return Category::FUNCTION; } - FunctionType(FunctionDefinition const& _function) : m_function(_function) {} + FunctionType(FunctionDefinition const& _function): m_function(_function) {} FunctionDefinition const& getFunction() const { return m_function; } private: FunctionDefinition const& m_function; }; -class MappingType : public Type +class MappingType: public Type { public: virtual Category getCategory() const { return Category::MAPPING; } @@ -164,18 +164,18 @@ private: }; //@todo should be changed into "empty anonymous struct" -class VoidType : public Type +class VoidType: public Type { public: virtual Category getCategory() const { return Category::VOID; } VoidType() {} }; -class TypeType : public Type +class TypeType: public Type { public: virtual Category getCategory() const { return Category::TYPE; } - TypeType(ptr const& _actualType) : m_actualType(_actualType) {} + TypeType(ptr const& _actualType): m_actualType(_actualType) {} ptr const& getActualType() { return m_actualType; } private: From 43bb41ef19c4e4b08eeda0d2e9a0e81519810851 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 17 Oct 2014 12:52:39 +0200 Subject: [PATCH 27/34] Keyword cleanup --- libsolidity/Scanner.cpp | 17 ++--------- libsolidity/Token.h | 62 ++++++++--------------------------------- 2 files changed, 14 insertions(+), 65 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 61f56f292..236498fd5 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -137,7 +137,7 @@ Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _ { advance(); if (m_char == _next) - selectToken(_then); + return selectToken(_then); else return _else; } @@ -530,21 +530,18 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("bool", Token::BOOL) \ KEYWORD_GROUP('c') \ KEYWORD("case", Token::CASE) \ - KEYWORD("catch", Token::CATCH) \ KEYWORD("const", Token::CONST) \ KEYWORD("continue", Token::CONTINUE) \ KEYWORD("contract", Token::CONTRACT) \ KEYWORD_GROUP('d') \ - KEYWORD("debugger", Token::DEBUGGER) \ KEYWORD("default", Token::DEFAULT) \ KEYWORD("delete", Token::DELETE) \ KEYWORD("do", Token::DO) \ KEYWORD_GROUP('e') \ KEYWORD("else", Token::ELSE) \ - KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("extends", Token::EXTENDS) \ KEYWORD_GROUP('f') \ KEYWORD("false", Token::FALSE_LITERAL) \ - KEYWORD("finally", Token::FINALLY) \ KEYWORD("for", Token::FOR) \ KEYWORD("function", Token::FUNCTION) \ KEYWORD_GROUP('h') \ @@ -555,15 +552,12 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("hash256", Token::HASH256) \ KEYWORD_GROUP('i') \ KEYWORD("if", Token::IF) \ - KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD("in", Token::IN) \ - KEYWORD("instanceof", Token::INSTANCEOF) \ KEYWORD("int", Token::INT) \ KEYWORD("int32", Token::INT32) \ KEYWORD("int64", Token::INT64) \ KEYWORD("int128", Token::INT128) \ KEYWORD("int256", Token::INT256) \ - KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD_GROUP('l') \ KEYWORD_GROUP('m') \ KEYWORD("mapping", Token::MAPPING) \ @@ -571,9 +565,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("new", Token::NEW) \ KEYWORD("null", Token::NULL_LITERAL) \ KEYWORD_GROUP('p') \ - KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD("private", Token::PRIVATE) \ - KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ KEYWORD("public", Token::PUBLIC) \ KEYWORD_GROUP('r') \ KEYWORD("real", Token::REAL) \ @@ -586,10 +578,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD_GROUP('t') \ KEYWORD("text", Token::TEXT) \ KEYWORD("this", Token::THIS) \ - KEYWORD("throw", Token::THROW) \ KEYWORD("true", Token::TRUE_LITERAL) \ - KEYWORD("try", Token::TRY) \ - KEYWORD("typeof", Token::TYPEOF) \ KEYWORD_GROUP('u') \ KEYWORD("uint", Token::UINT) \ KEYWORD("uint32", Token::UINT32) \ @@ -599,10 +588,8 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("ureal", Token::UREAL) \ KEYWORD_GROUP('v') \ KEYWORD("var", Token::VAR) \ - KEYWORD("void", Token::VOID) \ KEYWORD_GROUP('w') \ KEYWORD("while", Token::WHILE) \ - KEYWORD("with", Token::WITH) static Token::Value KeywordOrIdentifierToken(const std::string& input) diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 9e3896671..726c36eb6 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -87,13 +87,8 @@ namespace solidity T(ARROW, "=>", 0) \ \ /* Assignment operators. */ \ - /* IsAssignmentOp() and Assignment::is_compound() relies on */ \ - /* this block of enum values being contiguous and sorted in the */ \ - /* same order! */ \ - T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \ - T(INIT_LET, "=init_let", 2) /* AST-use only. */ \ - T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ - T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \ + /* IsAssignmentOp() relies on this block of enum values being */ \ + /* contiguous and sorted in the same order!*/ \ T(ASSIGN, "=", 2) \ /* The following have to be in exactly the same order as the simple binary operators*/ \ T(ASSIGN_BIT_OR, "|=", 2) \ @@ -131,13 +126,10 @@ namespace solidity /* being contiguous and sorted in the same order! */ \ T(EQ, "==", 9) \ T(NE, "!=", 9) \ - T(EQ_STRICT, "===", 9) \ - T(NE_STRICT, "!==", 9) \ T(LT, "<", 10) \ T(GT, ">", 10) \ T(LTE, "<=", 10) \ T(GTE, ">=", 10) \ - K(INSTANCEOF, "instanceof", 10) \ K(IN, "in", 10) \ \ /* Unary operators. */ \ @@ -146,26 +138,21 @@ namespace solidity T(NOT, "!", 0) \ T(BIT_NOT, "~", 0) \ K(DELETE, "delete", 0) \ - K(TYPEOF, "typeof", 0) \ - K(VOID, "void", 0) \ \ - /* Keywords (ECMA-262, section 7.5.2, page 13). */ \ + /* Keywords */ \ K(BREAK, "break", 0) \ K(CASE, "case", 0) \ - K(CATCH, "catch", 0) \ + K(CONST, "const", 0) \ K(CONTINUE, "continue", 0) \ K(CONTRACT, "contract", 0) \ - K(DEBUGGER, "debugger", 0) \ K(DEFAULT, "default", 0) \ - /* DELETE */ \ K(DO, "do", 0) \ K(ELSE, "else", 0) \ - K(FINALLY, "finally", 0) \ + K(EXTENDS, "extends", 0) \ K(FOR, "for", 0) \ K(FUNCTION, "function", 0) \ K(IF, "if", 0) \ - /* IN */ \ - /* INSTANCEOF */ \ + K(IMPORT, "import", 0) \ K(MAPPING, "mapping", 0) \ K(NEW, "new", 0) \ K(PUBLIC, "public", 0) \ @@ -175,13 +162,9 @@ namespace solidity K(STRUCT, "struct", 0) \ K(SWITCH, "switch", 0) \ K(THIS, "this", 0) \ - K(THROW, "throw", 0) \ - K(TRY, "try", 0) \ - /* TYPEOF */ \ K(VAR, "var", 0) \ - /* VOID */ \ K(WHILE, "while", 0) \ - K(WITH, "with", 0) \ + \ \ /* type keywords, keep them in this order, keep int as first keyword * the implementation in Types.cpp has to be synced to this here @@ -209,7 +192,7 @@ namespace solidity K(UREAL, "ureal", 0) \ T(TYPES_END, NULL, 0) /* used as type enum end marker */ \ \ - /* Literals (ECMA-262, section 7.8, page 16). */ \ + /* Literals */ \ K(NULL_LITERAL, "null", 0) \ K(TRUE_LITERAL, "true", 0) \ K(FALSE_LITERAL, "false", 0) \ @@ -219,19 +202,6 @@ namespace solidity /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, NULL, 0) \ \ - /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ - T(FUTURE_RESERVED_WORD, NULL, 0) \ - T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \ - K(CLASS, "class", 0) \ - K(CONST, "const", 0) \ - K(EXPORT, "export", 0) \ - K(EXTENDS, "extends", 0) \ - K(IMPORT, "import", 0) \ - K(LET, "let", 0) \ - K(STATIC, "static", 0) \ - /* K(YIELD, "yield", 0) */ \ - K(SUPER, "super", 0) \ - \ /* Illegal token - not able to scan. */ \ T(ILLEGAL, "ILLEGAL", 0) \ \ @@ -263,7 +233,7 @@ public: static bool isKeyword(Value tok) { return m_tokenType[tok] == 'K'; } static bool isIdentifier(Value tok) { return tok == IDENTIFIER; } static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; } - static bool isAssignmentOp(Value tok) { return INIT_VAR <= tok && tok <= ASSIGN_MOD; } + static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; } static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } @@ -271,8 +241,8 @@ public: { return op == LT || op == LTE || op == GT || op == GTE; } - static bool isEqualityOp(Value op) { return op == EQ || op == EQ_STRICT; } - static bool isInequalityOp(Value op) { return op == NE || op == NE_STRICT; } + static bool isEqualityOp(Value op) { return op == EQ; } + static bool isInequalityOp(Value op) { return op == NE; } static bool isArithmeticCompareOp(Value op) { return isOrderedRelationalCompareOp(op) || @@ -288,10 +258,6 @@ public: return NE; case NE: return EQ; - case EQ_STRICT: - return NE_STRICT; - case NE_STRICT: - return EQ_STRICT; case LT: return GTE; case GT: @@ -315,10 +281,6 @@ public: return EQ; case NE: return NE; - case EQ_STRICT: - return EQ_STRICT; - case NE_STRICT: - return NE_STRICT; case LT: return GT; case GT: @@ -340,7 +302,7 @@ public: } static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; } - static bool isUnaryOp(Value op) { return (NOT <= op && op <= VOID) || op == ADD || op == SUB; } + static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; } static bool isCountOp(Value op) { return op == INC || op == DEC; } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } From c253a90fc46a9ec39db8e4bd57efad767dc2bd0e Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 20 Oct 2014 13:02:06 +0200 Subject: [PATCH 28/34] Coding style cleanup: const and vecptr. --- libsolidity/AST.cpp | 2 +- libsolidity/AST.h | 40 +++++++++++++++++++------------------- libsolidity/ASTForward.h | 2 -- libsolidity/ASTPrinter.cpp | 6 +++--- libsolidity/Parser.cpp | 30 ++++++++++++++-------------- libsolidity/Parser.h | 4 ++-- libsolidity/Scanner.cpp | 16 +++++++-------- libsolidity/Scanner.h | 6 +++--- libsolidity/Token.cpp | 8 ++++---- libsolidity/Token.h | 12 ++++++------ libsolidity/Types.cpp | 14 ++++++------- libsolidity/Types.h | 10 +++++----- 12 files changed, 74 insertions(+), 76 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index c1a3faa55..8555d8c6d 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -394,7 +394,7 @@ ptr FunctionCall::checkTypeRequirements() FunctionType* function = dynamic_cast(expressionType.get()); BOOST_ASSERT(function != nullptr); FunctionDefinition const& fun = function->getFunction(); - vecptr const& parameters = fun.getParameters(); + std::vector> const& parameters = fun.getParameters(); if (parameters.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for " "function call.")); diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 8da3f529d..661e10a7e 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -51,7 +51,7 @@ public: virtual void accept(ASTVisitor& _visitor) = 0; template - static void listAccept(vecptr& _list, ASTVisitor& _visitor) + static void listAccept(std::vector>& _list, ASTVisitor& _visitor) { for (ptr& element: _list) element->accept(_visitor); @@ -78,9 +78,9 @@ class ContractDefinition: public Declaration public: ContractDefinition(Location const& _location, ptr const& _name, - vecptr const& _definedStructs, - vecptr const& _stateVariables, - vecptr const& _definedFunctions) + std::vector> const& _definedStructs, + std::vector> const& _stateVariables, + std::vector> const& _definedFunctions) : Declaration(_location, _name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), @@ -89,13 +89,13 @@ public: virtual void accept(ASTVisitor& _visitor) override; - vecptr const& getDefinedStructs() { return m_definedStructs; } - vecptr const& getStateVariables() { return m_stateVariables; } - vecptr const& getDefinedFunctions() { return m_definedFunctions; } + std::vector> const& getDefinedStructs() { return m_definedStructs; } + std::vector> const& getStateVariables() { return m_stateVariables; } + std::vector> const& getDefinedFunctions() { return m_definedFunctions; } private: - vecptr m_definedStructs; - vecptr m_stateVariables; - vecptr m_definedFunctions; + std::vector> m_definedStructs; + std::vector> m_stateVariables; + std::vector> m_definedFunctions; }; class StructDefinition: public Declaration @@ -103,12 +103,12 @@ class StructDefinition: public Declaration public: StructDefinition(Location const& _location, ptr const& _name, - vecptr const& _members) + std::vector> const& _members) : Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; private: - vecptr m_members; + std::vector> m_members; }; /// Used as function parameter list and return list @@ -117,13 +117,13 @@ private: class ParameterList: public ASTNode { public: - ParameterList(Location const& _location, vecptr const& _parameters) + ParameterList(Location const& _location, std::vector> const& _parameters) : ASTNode(_location), m_parameters(_parameters) {} virtual void accept(ASTVisitor& _visitor) override; - vecptr const& getParameters() { return m_parameters; } + std::vector> const& getParameters() { return m_parameters; } private: - vecptr m_parameters; + std::vector> m_parameters; }; class FunctionDefinition: public Declaration @@ -141,7 +141,7 @@ public: bool isPublic() const { return m_isPublic; } bool isDeclaredConst() const { return m_isDeclaredConst; } - vecptr const& getParameters() const { return m_parameters->getParameters(); } + std::vector> const& getParameters() const { return m_parameters->getParameters(); } ParameterList& getParameterList() { return *m_parameters; } ptr const& getReturnParameterList() const { return m_returnParameters; } Block& getBody() { return *m_body; } @@ -254,13 +254,13 @@ protected: class Block: public Statement { public: - Block(Location const& _location, vecptr const& _statements) + Block(Location const& _location, std::vector> const& _statements) : Statement(_location), m_statements(_statements) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; private: - vecptr m_statements; + std::vector> m_statements; }; class IfStatement: public Statement @@ -415,13 +415,13 @@ class FunctionCall: public Expression { public: FunctionCall(Location const& _location, ptr const& _expression, - vecptr const& _arguments) + std::vector> const& _arguments) : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; virtual ptr checkTypeRequirements() override; private: ptr m_expression; - vecptr m_arguments; + std::vector> m_arguments; }; class MemberAccess: public Expression diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 0397cadda..46fe8393a 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -70,8 +70,6 @@ class Literal; // explicitly. template using ptr = std::shared_ptr; -template -using vecptr = std::vector>; using ASTString = std::string; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index cd8263f2c..1ff0c52e7 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -28,7 +28,7 @@ namespace dev namespace solidity { -ASTPrinter::ASTPrinter(ptr _ast, const std::string& _source) +ASTPrinter::ASTPrinter(ptr _ast, std::string const& _source) : m_indentation(0), m_source(_source), m_ast(_ast) { } @@ -242,7 +242,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) bool ASTPrinter::visit(Literal& _node) { - const char* tokenString = Token::toString(_node.getToken()); + char const* tokenString = Token::toString(_node.getToken()); if (tokenString == nullptr) tokenString = "[no token]"; writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); @@ -415,7 +415,7 @@ std::string ASTPrinter::getIndentation() const return std::string(m_indentation * 2, ' '); } -void ASTPrinter::writeLine(const std::string& _line) +void ASTPrinter::writeLine(std::string const& _line) { *m_ostream << getIndentation() << _line << '\n'; } diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 945cec0f7..4716c55df 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -43,12 +43,12 @@ ptr Parser::parse(std::shared_ptr const& _scanner) class Parser::ASTNodeFactory { public: - ASTNodeFactory(const Parser& _parser) : m_parser(_parser), m_location(_parser.getPosition(), -1) {} + ASTNodeFactory(Parser const& _parser) : m_parser(_parser), m_location(_parser.getPosition(), -1) {} void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void setLocationEmpty() { m_location.end = m_location.start; } /// Set the end position to the one of the given node. - void setEndPositionFromNode(const ptr& _node) { m_location.end = _node->getLocation().end; } + void setEndPositionFromNode(ptr const& _node) { m_location.end = _node->getLocation().end; } template ptr createNode(Args&& ... _args) @@ -59,7 +59,7 @@ public: } private: - const Parser& m_parser; + Parser const& m_parser; Location m_location; }; @@ -80,9 +80,9 @@ ptr Parser::parseContractDefinition() expectToken(Token::CONTRACT); ptr name = expectIdentifierToken(); expectToken(Token::LBRACE); - vecptr structs; - vecptr stateVariables; - vecptr functions; + std::vector> structs; + std::vector> stateVariables; + std::vector> functions; bool visibilityIsPublic = true; while (true) { @@ -130,7 +130,7 @@ ptr Parser::parseFunctionDefinition(bool _isPublic) ptr returnParameters; if (m_scanner->getCurrentToken() == Token::RETURNS) { - const bool permitEmptyParameterList = false; + bool const permitEmptyParameterList = false; m_scanner->next(); returnParameters = parseParameterList(permitEmptyParameterList); } @@ -139,7 +139,7 @@ ptr Parser::parseFunctionDefinition(bool _isPublic) // create an empty parameter list at a zero-length location ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); - returnParameters = nodeFactory.createNode(vecptr()); + returnParameters = nodeFactory.createNode(std::vector>()); } ptr block = parseBlock(); nodeFactory.setEndPositionFromNode(block); @@ -152,7 +152,7 @@ ptr Parser::parseStructDefinition() ASTNodeFactory nodeFactory(*this); expectToken(Token::STRUCT); ptr name = expectIdentifierToken(); - vecptr members; + std::vector> members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { @@ -224,7 +224,7 @@ ptr Parser::parseMapping() ptr Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); - vecptr parameters; + std::vector> parameters; expectToken(Token::LPAREN); if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { @@ -245,7 +245,7 @@ ptr Parser::parseBlock() { ASTNodeFactory nodeFactory(*this); expectToken(Token::LBRACE); - vecptr statements; + std::vector> statements; while (m_scanner->getCurrentToken() != Token::RBRACE) statements.push_back(parseStatement()); nodeFactory.markEndPosition(); @@ -433,7 +433,7 @@ ptr Parser::parseLeftHandSideExpression() case Token::LPAREN: { m_scanner->next(); - vecptr arguments = parseFunctionCallArguments(); + std::vector> arguments = parseFunctionCallArguments(); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); expression = nodeFactory.createNode(expression, arguments); @@ -490,9 +490,9 @@ ptr Parser::parsePrimaryExpression() return expression; } -vecptr Parser::parseFunctionCallArguments() +std::vector> Parser::parseFunctionCallArguments() { - vecptr arguments; + std::vector> arguments; if (m_scanner->getCurrentToken() != Token::RPAREN) { arguments.push_back(parseExpression()); @@ -535,7 +535,7 @@ ptr Parser::getLiteralAndAdvance() return identifier; } -void Parser::throwExpectationError(const std::string& _description) +void Parser::throwExpectationError(std::string const& _description) { //@todo put some of this stuff into ParserError int line, column; diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 174ed7120..2a8613216 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -63,7 +63,7 @@ private: ptr parseUnaryExpression(); ptr parseLeftHandSideExpression(); ptr parsePrimaryExpression(); - vecptr parseFunctionCallArguments(); + std::vector> parseFunctionCallArguments(); /// @} /// Helper functions @@ -73,7 +73,7 @@ private: Token::Value expectAssignmentOperator(); ptr expectIdentifierToken(); ptr getLiteralAndAdvance(); - void throwExpectationError(const std::string& _description); + void throwExpectationError(std::string const& _description); /// @} std::shared_ptr m_scanner; diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 236498fd5..b65c0545b 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -88,12 +88,12 @@ int HexValue(char c) } } -Scanner::Scanner(const CharStream& _source) +Scanner::Scanner(CharStream const& _source) { reset(_source); } -void Scanner::reset(const CharStream& _source) +void Scanner::reset(CharStream const& _source) { m_source = _source; m_char = m_source.get(); @@ -145,7 +145,7 @@ Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _ bool Scanner::skipWhitespace() { - const int start_position = getSourcePos(); + int const start_position = getSourcePos(); while (IsWhiteSpace(m_char)) advance(); // Return whether or not we skipped any characters. @@ -425,7 +425,7 @@ bool Scanner::scanEscape() Token::Value Scanner::scanString() { - const char quote = m_char; + char const quote = m_char; advance(); // consume quote LiteralScope literal(this); while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) @@ -592,11 +592,11 @@ Token::Value Scanner::scanNumber(bool _periodSeen) KEYWORD("while", Token::WHILE) \ -static Token::Value KeywordOrIdentifierToken(const std::string& input) +static Token::Value KeywordOrIdentifierToken(std::string const& input) { BOOST_ASSERT(!input.empty()); - const int kMinLength = 2; - const int kMaxLength = 10; + int const kMinLength = 2; + int const kMaxLength = 10; if (input.size() < kMinLength || input.size() > kMaxLength) return Token::IDENTIFIER; switch (input[0]) @@ -609,7 +609,7 @@ case ch: { \ /* 'keyword' is a char array, so sizeof(keyword) is */ \ /* strlen(keyword) plus 1 for the NUL char. */ \ - const int keyword_length = sizeof(keyword) - 1; \ + int const keyword_length = sizeof(keyword) - 1; \ BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ if (input == keyword) { \ diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index f4fec5236..e71fb6347 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -64,7 +64,7 @@ class CharStream { public: CharStream() : m_pos(0) {} - explicit CharStream(const std::string& _source): m_source(_source), m_pos(0) {} + explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {} int getPos() const { return m_pos; } bool isPastEndOfInput() const { return m_pos >= m_source.size(); } char get() const { return m_source[m_pos]; } @@ -100,10 +100,10 @@ public: bool complete_; }; - explicit Scanner(const CharStream& _source); + explicit Scanner(CharStream const& _source); /// Resets the scanner as if newly constructed with _input as input. - void reset(const CharStream& _source); + void reset(CharStream const& _source); /// Returns the next token and advances input. Token::Value next(); diff --git a/libsolidity/Token.cpp b/libsolidity/Token.cpp index 564be734a..093bd9c1d 100644 --- a/libsolidity/Token.cpp +++ b/libsolidity/Token.cpp @@ -48,7 +48,7 @@ namespace solidity { #define T(name, string, precedence) #name, -const char* const Token::m_name[NUM_TOKENS] = +char const* const Token::m_name[NUM_TOKENS] = { TOKEN_LIST(T, T) }; @@ -56,7 +56,7 @@ const char* const Token::m_name[NUM_TOKENS] = #define T(name, string, precedence) string, -const char* const Token::m_string[NUM_TOKENS] = +char const* const Token::m_string[NUM_TOKENS] = { TOKEN_LIST(T, T) }; @@ -64,7 +64,7 @@ const char* const Token::m_string[NUM_TOKENS] = #define T(name, string, precedence) precedence, -const int8_t Token::m_precedence[NUM_TOKENS] = +int8_t const Token::m_precedence[NUM_TOKENS] = { TOKEN_LIST(T, T) }; @@ -73,7 +73,7 @@ const int8_t Token::m_precedence[NUM_TOKENS] = #define KT(a, b, c) 'T', #define KK(a, b, c) 'K', -const char Token::m_tokenType[] = +char const Token::m_tokenType[] = { TOKEN_LIST(KT, KK) }; diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 726c36eb6..2db6e05de 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -223,7 +223,7 @@ public: // Returns a string corresponding to the C++ token name // (e.g. "LT" for the token LT). - static const char* getName(Value tok) + static char const* getName(Value tok) { BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned return m_name[tok]; @@ -309,7 +309,7 @@ public: // Returns a string corresponding to the JS token string // (.e., "<" for the token LT) or NULL if the token doesn't // have a (unique) string (e.g. an IDENTIFIER). - static const char* toString(Value tok) + static char const* toString(Value tok) { BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. return m_string[tok]; @@ -324,10 +324,10 @@ public: } private: - static const char* const m_name[NUM_TOKENS]; - static const char* const m_string[NUM_TOKENS]; - static const int8_t m_precedence[NUM_TOKENS]; - static const char m_tokenType[NUM_TOKENS]; + static char const* const m_name[NUM_TOKENS]; + static char const* const m_string[NUM_TOKENS]; + static int8_t const m_precedence[NUM_TOKENS]; + static char const m_tokenType[NUM_TOKENS]; }; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index c5ee40cb8..8ca2c6342 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -52,18 +52,18 @@ ptr Type::fromElementaryTypeName(Token::Value _typeToken) BOOST_ASSERT(false); // @todo add other tyes } -ptr Type::fromUserDefinedTypeName(const UserDefinedTypeName& _typeName) +ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) { return std::make_shared(*_typeName.getReferencedStruct()); } -ptr Type::fromMapping(const Mapping&) +ptr Type::fromMapping(Mapping const&) { BOOST_ASSERT(false); //@todo not yet implemented return ptr(); } -ptr Type::forLiteral(const Literal& _literal) +ptr Type::forLiteral(Literal const& _literal) { switch (_literal.getToken()) { @@ -110,7 +110,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const return !convertTo.isSigned() || convertTo.m_bits > m_bits; } -bool IntegerType::isExplicitlyConvertibleTo(const Type& _convertTo) const +bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.getCategory() == Category::INTEGER; } @@ -130,7 +130,7 @@ bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const return _operator == Token::DELETE || (!isAddress() && _operator == Token::BIT_NOT); } -bool BoolType::isExplicitlyConvertibleTo(const Type& _convertTo) const +bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const { // conversion to integer is fine, but not to address // this is an example of explicit conversions being not transitive (though implicit should be) @@ -143,7 +143,7 @@ bool BoolType::isExplicitlyConvertibleTo(const Type& _convertTo) const return isImplicitlyConvertibleTo(_convertTo); } -bool ContractType::isImplicitlyConvertibleTo(const Type& _convertTo) const +bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.getCategory() != Category::CONTRACT) return false; @@ -151,7 +151,7 @@ bool ContractType::isImplicitlyConvertibleTo(const Type& _convertTo) const return &m_contract == &convertTo.m_contract; } -bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +bool StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (_convertTo.getCategory() != Category::STRUCT) return false; diff --git a/libsolidity/Types.h b/libsolidity/Types.h index d9455a501..9ab710942 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -64,8 +64,8 @@ public: static ptr forLiteral(Literal const& _literal); virtual Category getCategory() const = 0; - virtual bool isImplicitlyConvertibleTo(const Type&) const { return false; } - virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const + virtual bool isImplicitlyConvertibleTo(Type const&) const { return false; } + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const { return isImplicitlyConvertibleTo(_convertTo); } @@ -87,7 +87,7 @@ public: explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool acceptsBinaryOperator(Token::Value _operator) const override; virtual bool acceptsUnaryOperator(Token::Value _operator) const override; @@ -104,11 +104,11 @@ class BoolType: public Type { public: virtual Category getCategory() const { return Category::BOOL; } - virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override { return _convertTo.getCategory() == Category::BOOL; } - virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool acceptsBinaryOperator(Token::Value _operator) const override { return _operator == Token::AND || _operator == Token::OR; From fa22722e0e20267c4f489f809f84e9d7935448b5 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 20 Oct 2014 14:00:37 +0200 Subject: [PATCH 29/34] Pointer type cleanup: Use ASTPointer only for AST nodes and shared_ptr for type pointer. --- libsolidity/AST.cpp | 86 +++++----- libsolidity/AST.h | 217 +++++++++++++------------ libsolidity/ASTForward.h | 2 +- libsolidity/ASTPrinter.cpp | 2 +- libsolidity/ASTPrinter.h | 4 +- libsolidity/NameAndTypeResolver.cpp | 6 +- libsolidity/NameAndTypeResolver.h | 1 - libsolidity/Parser.cpp | 172 ++++++++++---------- libsolidity/Parser.h | 42 ++--- libsolidity/Scanner.cpp | 31 ++-- libsolidity/Scope.h | 1 - libsolidity/Types.cpp | 16 +- libsolidity/Types.h | 35 ++-- solc/main.cpp | 6 +- test/solidityNameAndTypeResolution.cpp | 2 +- test/solidityParser.cpp | 2 +- 16 files changed, 307 insertions(+), 318 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 8555d8c6d..15da9e0d8 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -250,46 +250,42 @@ void Literal::accept(ASTVisitor& _visitor) void Statement::expectType(Expression& _expression, const Type& _expectedType) { - if (!_expression.checkTypeRequirements()->isImplicitlyConvertibleTo(_expectedType)) + _expression.checkTypeRequirements(); + if (!_expression.getType()->isImplicitlyConvertibleTo(_expectedType)) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type not implicitly convertible " "to expected type.")); //@todo provide more information to the exception } -ptr Block::checkTypeRequirements() +void Block::checkTypeRequirements() { - for (ptr const& statement: m_statements) + for (std::shared_ptr const& statement: m_statements) statement->checkTypeRequirements(); - return ptr(); } -ptr IfStatement::checkTypeRequirements() +void IfStatement::checkTypeRequirements() { expectType(*m_condition, BoolType()); m_trueBody->checkTypeRequirements(); if (m_falseBody) m_falseBody->checkTypeRequirements(); - return ptr(); } -ptr WhileStatement::checkTypeRequirements() +void WhileStatement::checkTypeRequirements() { expectType(*m_condition, BoolType()); m_body->checkTypeRequirements(); - return ptr(); } -ptr Continue::checkTypeRequirements() +void Continue::checkTypeRequirements() { - return ptr(); } -ptr Break::checkTypeRequirements() +void Break::checkTypeRequirements() { - return ptr(); } -ptr Return::checkTypeRequirements() +void Return::checkTypeRequirements() { BOOST_ASSERT(m_returnParameters != nullptr); if (m_returnParameters->getParameters().size() != 1) @@ -299,10 +295,9 @@ ptr Return::checkTypeRequirements() // this could later be changed such that the paramaters type is an anonymous struct type, // but for now, we only allow one return parameter expectType(*m_expression, *m_returnParameters->getParameters().front()->getType()); - return ptr(); } -ptr VariableDefinition::checkTypeRequirements() +void VariableDefinition::checkTypeRequirements() { // Variables can be declared without type (with "var"), in which case the first assignment // setsthe type. @@ -313,17 +308,20 @@ ptr VariableDefinition::checkTypeRequirements() if (m_variable->getType()) expectType(*m_value, *m_variable->getType()); else + { // no type declared and no previous assignment, infer the type - m_variable->setType(m_value->checkTypeRequirements()); + m_value->checkTypeRequirements(); + m_variable->setType(m_value->getType()); + } } - return ptr(); } -ptr Assignment::checkTypeRequirements() +void Assignment::checkTypeRequirements() { //@todo lefthandside actually has to be assignable // add a feature to the type system to check that - expectType(*m_rightHandSide, *m_leftHandSide->checkTypeRequirements()); + m_leftHandSide->checkTypeRequirements(); + expectType(*m_rightHandSide, *m_leftHandSide->getType()); m_type = m_leftHandSide->getType(); if (m_assigmentOperator != Token::ASSIGN) { @@ -331,19 +329,18 @@ ptr Assignment::checkTypeRequirements() if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); } - return m_type; } -ptr UnaryOperation::checkTypeRequirements() +void UnaryOperation::checkTypeRequirements() { // INC, DEC, NOT, BIT_NOT, DELETE - m_type = m_subExpression->checkTypeRequirements(); + m_subExpression->checkTypeRequirements(); + m_type = m_subExpression->getType(); if (m_type->acceptsUnaryOperator(m_operator)) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Unary operator not compatible with type.")); - return m_type; } -ptr BinaryOperation::checkTypeRequirements() +void BinaryOperation::checkTypeRequirements() { m_right->checkTypeRequirements(); m_left->checkTypeRequirements(); @@ -362,19 +359,18 @@ ptr BinaryOperation::checkTypeRequirements() if (!m_commonType->acceptsBinaryOperator(m_operator)) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); } - return m_type; } -ptr FunctionCall::checkTypeRequirements() +void FunctionCall::checkTypeRequirements() { m_expression->checkTypeRequirements(); - for (ptr const& argument: m_arguments) + for (ASTPointer const& argument: m_arguments) argument->checkTypeRequirements(); - ptr expressionType = m_expression->getType(); - Type::Category const category = expressionType->getCategory(); + Type const& expressionType = *m_expression->getType(); + Type::Category const category = expressionType.getCategory(); if (category == Type::Category::TYPE) { - TypeType* type = dynamic_cast(expressionType.get()); + TypeType const* type = dynamic_cast(&expressionType); BOOST_ASSERT(type != nullptr); //@todo for structs, we have to check the number of arguments to be equal to the // number of non-mapping members @@ -391,10 +387,10 @@ ptr FunctionCall::checkTypeRequirements() //@todo would be nice to create a struct type from the arguments // and then ask if that is implicitly convertible to the struct represented by the // function parameters - FunctionType* function = dynamic_cast(expressionType.get()); + FunctionType const* function = dynamic_cast(&expressionType); BOOST_ASSERT(function != nullptr); FunctionDefinition const& fun = function->getFunction(); - std::vector> const& parameters = fun.getParameters(); + std::vector> const& parameters = fun.getParameters(); if (parameters.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for " "function call.")); @@ -413,24 +409,21 @@ ptr FunctionCall::checkTypeRequirements() { BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation.")); } - return m_type; } -ptr MemberAccess::checkTypeRequirements() +void MemberAccess::checkTypeRequirements() { BOOST_ASSERT(false); // not yet implemented // m_type = ; - return m_type; } -ptr IndexAccess::checkTypeRequirements() +void IndexAccess::checkTypeRequirements() { BOOST_ASSERT(false); // not yet implemented // m_type = ; - return m_type; } -ptr Identifier::checkTypeRequirements() +void Identifier::checkTypeRequirements() { BOOST_ASSERT(m_referencedDeclaration != nullptr); //@todo these dynamic casts here are not really nice... @@ -444,11 +437,11 @@ ptr Identifier::checkTypeRequirements() VariableDeclaration* variable = dynamic_cast(m_referencedDeclaration); if (variable != nullptr) { - if (variable->getType().get() == nullptr) + if (!variable->getType()) BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Variable referenced before type " "could be determined.")); m_type = variable->getType(); - return m_type; + return; } //@todo can we unify these with TypeName::toType()? StructDefinition* structDef = dynamic_cast(m_referencedDeclaration); @@ -456,7 +449,7 @@ ptr Identifier::checkTypeRequirements() { // note that we do not have a struct type here m_type = std::make_shared(std::make_shared(*structDef)); - return m_type; + return; } FunctionDefinition* functionDef = dynamic_cast(m_referencedDeclaration); if (functionDef != nullptr) @@ -465,28 +458,25 @@ ptr Identifier::checkTypeRequirements() // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type // conversion. m_type = std::make_shared(*functionDef); - return m_type; + return; } ContractDefinition* contractDef = dynamic_cast(m_referencedDeclaration); if (contractDef != nullptr) { m_type = std::make_shared(std::make_shared(*contractDef)); - return m_type; + return; } BOOST_ASSERT(false); // declaration reference of unknown/forbidden type - return m_type; } -ptr ElementaryTypeNameExpression::checkTypeRequirements() +void ElementaryTypeNameExpression::checkTypeRequirements() { m_type = std::make_shared(Type::fromElementaryTypeName(m_typeToken)); - return m_type; } -ptr Literal::checkTypeRequirements() +void Literal::checkTypeRequirements() { m_type = Type::forLiteral(*this); - return m_type; } } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 661e10a7e..319fc0581 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -51,9 +51,9 @@ public: virtual void accept(ASTVisitor& _visitor) = 0; template - static void listAccept(std::vector>& _list, ASTVisitor& _visitor) + static void listAccept(std::vector>& _list, ASTVisitor& _visitor) { - for (ptr& element: _list) + for (ASTPointer& element: _list) element->accept(_visitor); } @@ -65,22 +65,22 @@ private: class Declaration: public ASTNode { public: - Declaration(Location const& _location, ptr const& _name) + Declaration(Location const& _location, ASTPointer const& _name) : ASTNode(_location), m_name(_name) {} const ASTString& getName() const { return *m_name; } private: - ptr m_name; + ASTPointer m_name; }; class ContractDefinition: public Declaration { public: ContractDefinition(Location const& _location, - ptr const& _name, - std::vector> const& _definedStructs, - std::vector> const& _stateVariables, - std::vector> const& _definedFunctions) + ASTPointer const& _name, + std::vector> const& _definedStructs, + std::vector> const& _stateVariables, + std::vector> const& _definedFunctions) : Declaration(_location, _name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), @@ -89,26 +89,26 @@ public: virtual void accept(ASTVisitor& _visitor) override; - std::vector> const& getDefinedStructs() { return m_definedStructs; } - std::vector> const& getStateVariables() { return m_stateVariables; } - std::vector> const& getDefinedFunctions() { return m_definedFunctions; } + std::vector> const& getDefinedStructs() { return m_definedStructs; } + std::vector> const& getStateVariables() { return m_stateVariables; } + std::vector> const& getDefinedFunctions() { return m_definedFunctions; } private: - std::vector> m_definedStructs; - std::vector> m_stateVariables; - std::vector> m_definedFunctions; + std::vector> m_definedStructs; + std::vector> m_stateVariables; + std::vector> m_definedFunctions; }; class StructDefinition: public Declaration { public: StructDefinition(Location const& _location, - ptr const& _name, - std::vector> const& _members) + ASTPointer const& _name, + std::vector> const& _members) : Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; private: - std::vector> m_members; + std::vector> m_members; }; /// Used as function parameter list and return list @@ -117,23 +117,23 @@ private: class ParameterList: public ASTNode { public: - ParameterList(Location const& _location, std::vector> const& _parameters) + ParameterList(Location const& _location, std::vector> const& _parameters) : ASTNode(_location), m_parameters(_parameters) {} virtual void accept(ASTVisitor& _visitor) override; - std::vector> const& getParameters() { return m_parameters; } + std::vector> const& getParameters() { return m_parameters; } private: - std::vector> m_parameters; + std::vector> m_parameters; }; class FunctionDefinition: public Declaration { public: - FunctionDefinition(Location const& _location, ptr const& _name, bool _isPublic, - ptr const& _parameters, + FunctionDefinition(Location const& _location, ASTPointer const& _name, bool _isPublic, + ASTPointer const& _parameters, bool _isDeclaredConst, - ptr const& _returnParameters, - ptr const& _body) + ASTPointer const& _returnParameters, + ASTPointer const& _body) : Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), m_body(_body) {} @@ -141,23 +141,23 @@ public: bool isPublic() const { return m_isPublic; } bool isDeclaredConst() const { return m_isDeclaredConst; } - std::vector> const& getParameters() const { return m_parameters->getParameters(); } + std::vector> const& getParameters() const { return m_parameters->getParameters(); } ParameterList& getParameterList() { return *m_parameters; } - ptr const& getReturnParameterList() const { return m_returnParameters; } + ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block& getBody() { return *m_body; } private: bool m_isPublic; - ptr m_parameters; + ASTPointer m_parameters; bool m_isDeclaredConst; - ptr m_returnParameters; - ptr m_body; + ASTPointer m_returnParameters; + ASTPointer m_body; }; class VariableDeclaration: public Declaration { public: - VariableDeclaration(Location const& _location, ptr const& _type, - ptr const& _name) + VariableDeclaration(Location const& _location, ASTPointer const& _type, + ASTPointer const& _name) : Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; @@ -166,12 +166,12 @@ public: //! Returns the declared or inferred type. Can be an empty pointer if no type was explicitly //! declared and there is no assignment to the variable that fixes the type. - ptr const& getType() const { return m_type; } - void setType(ptr const& _type) { m_type = _type; } + std::shared_ptr const& getType() const { return m_type; } + void setType(std::shared_ptr const& _type) { m_type = _type; } private: - ptr m_typeName; ///< can be empty ("var") + ASTPointer m_typeName; ///< can be empty ("var") - ptr m_type; + std::shared_ptr m_type; }; /// types @@ -183,7 +183,7 @@ public: explicit TypeName(Location const& _location): ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr toType() = 0; + virtual std::shared_ptr toType() = 0; }; /// any pre-defined type that is not a mapping @@ -193,7 +193,7 @@ public: explicit ElementaryTypeName(Location const& _location, Token::Value _type) : TypeName(_location), m_type(_type) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr toType() override { return Type::fromElementaryTypeName(m_type); } + virtual std::shared_ptr toType() override { return Type::fromElementaryTypeName(m_type); } Token::Value getType() const { return m_type; } private: @@ -203,16 +203,16 @@ private: class UserDefinedTypeName: public TypeName { public: - UserDefinedTypeName(Location const& _location, ptr const& _name) + UserDefinedTypeName(Location const& _location, ASTPointer const& _name) : TypeName(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr toType() override { return Type::fromUserDefinedTypeName(*this); } + virtual std::shared_ptr toType() override { return Type::fromUserDefinedTypeName(*this); } const ASTString& getName() const { return *m_name; } void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } private: - ptr m_name; + ASTPointer m_name; StructDefinition* m_referencedStruct; }; @@ -220,14 +220,14 @@ private: class Mapping: public TypeName { public: - Mapping(Location const& _location, ptr const& _keyType, - ptr const& _valueType) + Mapping(Location const& _location, ASTPointer const& _keyType, + ASTPointer const& _valueType) : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr toType() override { return Type::fromMapping(*this); } + virtual std::shared_ptr toType() override { return Type::fromMapping(*this); } private: - ptr m_keyType; - ptr m_valueType; + ASTPointer m_keyType; + ASTPointer m_valueType; }; /// @} @@ -244,7 +244,7 @@ public: //! Check all type requirements, throws exception if some requirement is not met. //! For expressions, this also returns the inferred type of the expression. For other //! statements, returns the empty pointer. - virtual ptr checkTypeRequirements() = 0; + virtual void checkTypeRequirements() = 0; protected: //! Check that the inferred type for _expression is _expectedType or at least implicitly //! convertible to _expectedType. If not, throw exception. @@ -254,28 +254,28 @@ protected: class Block: public Statement { public: - Block(Location const& _location, std::vector> const& _statements) + Block(Location const& _location, std::vector> const& _statements) : Statement(_location), m_statements(_statements) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; private: - std::vector> m_statements; + std::vector> m_statements; }; class IfStatement: public Statement { public: - IfStatement(Location const& _location, ptr const& _condition, - ptr const& _trueBody, ptr const& _falseBody) + IfStatement(Location const& _location, ASTPointer const& _condition, + ASTPointer const& _trueBody, ASTPointer const& _falseBody) : Statement(_location), m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; private: - ptr m_condition; - ptr m_trueBody; - ptr m_falseBody; //< "else" part, optional + ASTPointer m_condition; + ASTPointer m_trueBody; + ASTPointer m_falseBody; //< "else" part, optional }; class BreakableStatement: public Statement @@ -288,14 +288,14 @@ public: class WhileStatement: public BreakableStatement { public: - WhileStatement(Location const& _location, ptr const& _condition, - ptr const& _body) + WhileStatement(Location const& _location, ASTPointer const& _condition, + ASTPointer const& _body) : BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; private: - ptr m_condition; - ptr m_body; + ASTPointer m_condition; + ASTPointer m_body; }; class Continue: public Statement @@ -303,7 +303,7 @@ class Continue: public Statement public: Continue(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; }; class Break: public Statement @@ -311,20 +311,20 @@ class Break: public Statement public: Break(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; }; class Return: public Statement { public: - Return(Location const& _location, ptr _expression) + Return(Location const& _location, ASTPointer _expression) : Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } private: - ptr m_expression; //< value to return, optional + ASTPointer m_expression; //< value to return, optional ParameterList* m_returnParameters; //< extracted from the function declaration }; @@ -332,24 +332,25 @@ private: class VariableDefinition: public Statement { public: - VariableDefinition(Location const& _location, ptr _variable, - ptr _value) + VariableDefinition(Location const& _location, ASTPointer _variable, + ASTPointer _value) : Statement(_location), m_variable(_variable), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; private: - ptr m_variable; - ptr m_value; ///< can be missing + ASTPointer m_variable; + ASTPointer m_value; ///< can be missing }; class Expression: public Statement { public: Expression(Location const& _location): Statement(_location) {} - ptr const& getType() { return m_type; } + std::shared_ptr const& getType() const { return m_type; } protected: - ptr m_type; + //! Inferred type of the expression, only filled after a call to checkTypeRequirements(). + std::shared_ptr m_type; }; /// @} @@ -360,95 +361,95 @@ protected: class Assignment: public Expression { public: - Assignment(Location const& _location, ptr const& _leftHandSide, - Token::Value _assignmentOperator, ptr const& _rightHandSide) + Assignment(Location const& _location, ASTPointer const& _leftHandSide, + Token::Value _assignmentOperator, ASTPointer const& _rightHandSide) : Expression(_location), m_leftHandSide(_leftHandSide), m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; Token::Value getAssignmentOperator() const { return m_assigmentOperator; } private: - ptr m_leftHandSide; + ASTPointer m_leftHandSide; Token::Value m_assigmentOperator; - ptr m_rightHandSide; + ASTPointer m_rightHandSide; }; class UnaryOperation: public Expression { public: UnaryOperation(Location const& _location, Token::Value _operator, - ptr const& _subExpression, bool _isPrefix) + ASTPointer const& _subExpression, bool _isPrefix) : Expression(_location), m_operator(_operator), m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } private: Token::Value m_operator; - ptr m_subExpression; + ASTPointer m_subExpression; bool m_isPrefix; }; class BinaryOperation: public Expression { public: - BinaryOperation(Location const& _location, ptr const& _left, - Token::Value _operator, ptr const& _right) + BinaryOperation(Location const& _location, ASTPointer const& _left, + Token::Value _operator, ASTPointer const& _right) : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } private: - ptr m_left; + ASTPointer m_left; Token::Value m_operator; - ptr m_right; + ASTPointer m_right; - ptr m_commonType; + std::shared_ptr m_commonType; }; /// Can be ordinary function call, type cast or struct construction. class FunctionCall: public Expression { public: - FunctionCall(Location const& _location, ptr const& _expression, - std::vector> const& _arguments) + FunctionCall(Location const& _location, ASTPointer const& _expression, + std::vector> const& _arguments) : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; private: - ptr m_expression; - std::vector> m_arguments; + ASTPointer m_expression; + std::vector> m_arguments; }; class MemberAccess: public Expression { public: - MemberAccess(Location const& _location, ptr _expression, - ptr const& _memberName) + MemberAccess(Location const& _location, ASTPointer _expression, + ASTPointer const& _memberName) : Expression(_location), m_expression(_expression), m_memberName(_memberName) {} virtual void accept(ASTVisitor& _visitor) override; const ASTString& getMemberName() const { return *m_memberName; } - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; private: - ptr m_expression; - ptr m_memberName; + ASTPointer m_expression; + ASTPointer m_memberName; }; class IndexAccess: public Expression { public: - IndexAccess(Location const& _location, ptr const& _base, - ptr const& _index) + IndexAccess(Location const& _location, ASTPointer const& _base, + ASTPointer const& _index) : Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; private: - ptr m_base; - ptr m_index; + ASTPointer m_base; + ASTPointer m_index; }; class PrimaryExpression: public Expression @@ -460,16 +461,16 @@ public: class Identifier: public PrimaryExpression { public: - Identifier(Location const& _location, ptr const& _name) + Identifier(Location const& _location, ASTPointer const& _name) : PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; ASTString const& getName() const { return *m_name; } void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } private: - ptr m_name; + ASTPointer m_name; //! Declaration the name refers to. Declaration* m_referencedDeclaration; @@ -481,7 +482,7 @@ public: ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken) : PrimaryExpression(_location), m_typeToken(_typeToken) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; Token::Value getTypeToken() const { return m_typeToken; } private: @@ -491,16 +492,16 @@ private: class Literal: public PrimaryExpression { public: - Literal(Location const& _location, Token::Value _token, ptr const& _value) + Literal(Location const& _location, Token::Value _token, ASTPointer const& _value) : PrimaryExpression(_location), m_token(_token), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; - virtual ptr checkTypeRequirements() override; + virtual void checkTypeRequirements() override; Token::Value getToken() const { return m_token; } ASTString const& getValue() const { return *m_value; } private: Token::Value m_token; - ptr m_value; + ASTPointer m_value; }; /// @} diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 46fe8393a..c9a780f5f 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -69,7 +69,7 @@ class Literal; // not do reference counting but point to a special memory area that is completely released // explicitly. template -using ptr = std::shared_ptr; +using ASTPointer = std::shared_ptr; using ASTString = std::string; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 1ff0c52e7..af8f20157 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -28,7 +28,7 @@ namespace dev namespace solidity { -ASTPrinter::ASTPrinter(ptr _ast, std::string const& _source) +ASTPrinter::ASTPrinter(ASTPointer const& _ast, std::string const& _source) : m_indentation(0), m_source(_source), m_ast(_ast) { } diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index 00c681f34..e8d125a54 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -35,7 +35,7 @@ class ASTPrinter: public ASTVisitor public: /// Create a printer for the given abstract syntax tree. If the source is specified, /// the corresponding parts of the source are printed with each node. - ASTPrinter(ptr _ast, const std::string& _source = std::string()); + ASTPrinter(ASTPointer const& _ast, const std::string& _source = std::string()); /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); @@ -108,7 +108,7 @@ private: int m_indentation; std::string m_source; - ptr m_ast; + ASTPointer m_ast; std::ostream* m_ostream; }; diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 628d94769..84b12a238 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -42,9 +42,9 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) DeclarationRegistrationHelper registrar(m_scopes, _contract); m_currentScope = &m_scopes[&_contract]; //@todo structs - for (ptr const& variable: _contract.getStateVariables()) + for (ASTPointer const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, nullptr); - for (ptr const& function: _contract.getDefinedFunctions()) + for (ASTPointer const& function: _contract.getDefinedFunctions()) { m_currentScope = &m_scopes[function.get()]; ReferencesResolver referencesResolver(*function, *this, @@ -53,7 +53,7 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) // First, the parameter types of all functions need to be resolved before we can check // the types, since it is possible to call functions that are only defined later // in the source. - for (ptr const& function: _contract.getDefinedFunctions()) + for (ASTPointer const& function: _contract.getDefinedFunctions()) { m_currentScope = &m_scopes[function.get()]; function->getBody().checkTypeRequirements(); diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 076b1389f..625454566 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -23,7 +23,6 @@ #pragma once #include - #include #include diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 4716c55df..7b1fdeb28 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -31,7 +31,7 @@ namespace dev namespace solidity { -ptr Parser::parse(std::shared_ptr const& _scanner) +ASTPointer Parser::parse(std::shared_ptr const& _scanner) { m_scanner = _scanner; return parseContractDefinition(); @@ -48,10 +48,10 @@ public: void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void setLocationEmpty() { m_location.end = m_location.start; } /// Set the end position to the one of the given node. - void setEndPositionFromNode(ptr const& _node) { m_location.end = _node->getLocation().end; } + void setEndPositionFromNode(ASTPointer const& _node) { m_location.end = _node->getLocation().end; } template - ptr createNode(Args&& ... _args) + ASTPointer createNode(Args&& ... _args) { if (m_location.end < 0) markEndPosition(); @@ -74,15 +74,15 @@ int Parser::getEndPosition() const } -ptr Parser::parseContractDefinition() +ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); expectToken(Token::CONTRACT); - ptr name = expectIdentifierToken(); + ASTPointer name = expectIdentifierToken(); expectToken(Token::LBRACE); - std::vector> structs; - std::vector> stateVariables; - std::vector> functions; + std::vector> structs; + std::vector> stateVariables; + std::vector> functions; bool visibilityIsPublic = true; while (true) { @@ -115,19 +115,19 @@ ptr Parser::parseContractDefinition() return nodeFactory.createNode(name, structs, stateVariables, functions); } -ptr Parser::parseFunctionDefinition(bool _isPublic) +ASTPointer Parser::parseFunctionDefinition(bool _isPublic) { ASTNodeFactory nodeFactory(*this); expectToken(Token::FUNCTION); - ptr name(expectIdentifierToken()); - ptr parameters(parseParameterList()); + ASTPointer name(expectIdentifierToken()); + ASTPointer parameters(parseParameterList()); bool isDeclaredConst = false; if (m_scanner->getCurrentToken() == Token::CONST) { isDeclaredConst = true; m_scanner->next(); } - ptr returnParameters; + ASTPointer returnParameters; if (m_scanner->getCurrentToken() == Token::RETURNS) { bool const permitEmptyParameterList = false; @@ -139,20 +139,20 @@ ptr Parser::parseFunctionDefinition(bool _isPublic) // create an empty parameter list at a zero-length location ASTNodeFactory nodeFactory(*this); nodeFactory.setLocationEmpty(); - returnParameters = nodeFactory.createNode(std::vector>()); + returnParameters = nodeFactory.createNode(std::vector>()); } - ptr block = parseBlock(); + ASTPointer block = parseBlock(); nodeFactory.setEndPositionFromNode(block); return nodeFactory.createNode(name, _isPublic, parameters, isDeclaredConst, returnParameters, block); } -ptr Parser::parseStructDefinition() +ASTPointer Parser::parseStructDefinition() { ASTNodeFactory nodeFactory(*this); expectToken(Token::STRUCT); - ptr name = expectIdentifierToken(); - std::vector> members; + ASTPointer name = expectIdentifierToken(); + std::vector> members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { @@ -165,17 +165,17 @@ ptr Parser::parseStructDefinition() return nodeFactory.createNode(name, members); } -ptr Parser::parseVariableDeclaration(bool _allowVar) +ASTPointer Parser::parseVariableDeclaration(bool _allowVar) { ASTNodeFactory nodeFactory(*this); - ptr type = parseTypeName(_allowVar); + ASTPointer type = parseTypeName(_allowVar); nodeFactory.markEndPosition(); return nodeFactory.createNode(type, expectIdentifierToken()); } -ptr Parser::parseTypeName(bool _allowVar) +ASTPointer Parser::parseTypeName(bool _allowVar) { - ptr type; + ASTPointer type; Token::Value token = m_scanner->getCurrentToken(); if (Token::isElementaryTypeName(token)) { @@ -203,28 +203,28 @@ ptr Parser::parseTypeName(bool _allowVar) return type; } -ptr Parser::parseMapping() +ASTPointer Parser::parseMapping() { ASTNodeFactory nodeFactory(*this); expectToken(Token::MAPPING); expectToken(Token::LPAREN); if (!Token::isElementaryTypeName(m_scanner->getCurrentToken())) throwExpectationError("Expected elementary type name for mapping key type"); - ptr keyType; + ASTPointer keyType; keyType = ASTNodeFactory(*this).createNode(m_scanner->getCurrentToken()); m_scanner->next(); expectToken(Token::ARROW); bool const allowVar = false; - ptr valueType = parseTypeName(allowVar); + ASTPointer valueType = parseTypeName(allowVar); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); return nodeFactory.createNode(keyType, valueType); } -ptr Parser::parseParameterList(bool _allowEmpty) +ASTPointer Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); - std::vector> parameters; + std::vector> parameters; expectToken(Token::LPAREN); if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { @@ -241,11 +241,11 @@ ptr Parser::parseParameterList(bool _allowEmpty) return nodeFactory.createNode(parameters); } -ptr Parser::parseBlock() +ASTPointer Parser::parseBlock() { ASTNodeFactory nodeFactory(*this); expectToken(Token::LBRACE); - std::vector> statements; + std::vector> statements; while (m_scanner->getCurrentToken() != Token::RBRACE) statements.push_back(parseStatement()); nodeFactory.markEndPosition(); @@ -253,9 +253,9 @@ ptr Parser::parseBlock() return nodeFactory.createNode(statements); } -ptr Parser::parseStatement() +ASTPointer Parser::parseStatement() { - ptr statement; + ASTPointer statement; switch (m_scanner->getCurrentToken()) { case Token::IF: @@ -274,7 +274,7 @@ ptr Parser::parseStatement() case Token::RETURN: { ASTNodeFactory nodeFactory(*this); - ptr expression; + ASTPointer expression; if (m_scanner->next() != Token::SEMICOLON) { expression = parseExpression(); @@ -301,15 +301,15 @@ ptr Parser::parseStatement() return statement; } -ptr Parser::parseIfStatement() +ASTPointer Parser::parseIfStatement() { ASTNodeFactory nodeFactory(*this); expectToken(Token::IF); expectToken(Token::LPAREN); - ptr condition = parseExpression(); + ASTPointer condition = parseExpression(); expectToken(Token::RPAREN); - ptr trueBody = parseStatement(); - ptr falseBody; + ASTPointer trueBody = parseStatement(); + ASTPointer falseBody; if (m_scanner->getCurrentToken() == Token::ELSE) { m_scanner->next(); @@ -321,24 +321,24 @@ ptr Parser::parseIfStatement() return nodeFactory.createNode(condition, trueBody, falseBody); } -ptr Parser::parseWhileStatement() +ASTPointer Parser::parseWhileStatement() { ASTNodeFactory nodeFactory(*this); expectToken(Token::WHILE); expectToken(Token::LPAREN); - ptr condition = parseExpression(); + ASTPointer condition = parseExpression(); expectToken(Token::RPAREN); - ptr body = parseStatement(); + ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); return nodeFactory.createNode(condition, body); } -ptr Parser::parseVariableDefinition() +ASTPointer Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); bool const allowVar = true; - ptr variable = parseVariableDeclaration(allowVar); - ptr value; + ASTPointer variable = parseVariableDeclaration(allowVar); + ASTPointer value; if (m_scanner->getCurrentToken() == Token::ASSIGN) { m_scanner->next(); @@ -350,22 +350,22 @@ ptr Parser::parseVariableDefinition() return nodeFactory.createNode(variable, value); } -ptr Parser::parseExpression() +ASTPointer Parser::parseExpression() { ASTNodeFactory nodeFactory(*this); - ptr expression = parseBinaryExpression(); + ASTPointer expression = parseBinaryExpression(); if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) return expression; Token::Value assignmentOperator = expectAssignmentOperator(); - ptr rightHandSide = parseExpression(); + ASTPointer rightHandSide = parseExpression(); nodeFactory.setEndPositionFromNode(rightHandSide); return nodeFactory.createNode(expression, assignmentOperator, rightHandSide); } -ptr Parser::parseBinaryExpression(int _minPrecedence) +ASTPointer Parser::parseBinaryExpression(int _minPrecedence) { ASTNodeFactory nodeFactory(*this); - ptr expression = parseUnaryExpression(); + ASTPointer expression = parseUnaryExpression(); int precedence = Token::precedence(m_scanner->getCurrentToken()); for (; precedence >= _minPrecedence; --precedence) { @@ -373,7 +373,7 @@ ptr Parser::parseBinaryExpression(int _minPrecedence) { Token::Value op = m_scanner->getCurrentToken(); m_scanner->next(); - ptr right = parseBinaryExpression(precedence + 1); + ASTPointer right = parseBinaryExpression(precedence + 1); nodeFactory.setEndPositionFromNode(right); expression = nodeFactory.createNode(expression, op, right); } @@ -381,7 +381,7 @@ ptr Parser::parseBinaryExpression(int _minPrecedence) return expression; } -ptr Parser::parseUnaryExpression() +ASTPointer Parser::parseUnaryExpression() { ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); @@ -389,14 +389,14 @@ ptr Parser::parseUnaryExpression() { // prefix expression m_scanner->next(); - ptr subExpression = parseUnaryExpression(); + ASTPointer subExpression = parseUnaryExpression(); nodeFactory.setEndPositionFromNode(subExpression); return nodeFactory.createNode(token, subExpression, true); } else { // potential postfix expression - ptr subExpression = parseLeftHandSideExpression(); + ASTPointer subExpression = parseLeftHandSideExpression(); token = m_scanner->getCurrentToken(); if (!Token::isCountOp(token)) return subExpression; @@ -406,38 +406,38 @@ ptr Parser::parseUnaryExpression() } } -ptr Parser::parseLeftHandSideExpression() +ASTPointer Parser::parseLeftHandSideExpression() { ASTNodeFactory nodeFactory(*this); - ptr expression = parsePrimaryExpression(); + ASTPointer expression = parsePrimaryExpression(); while (true) { switch (m_scanner->getCurrentToken()) { case Token::LBRACK: - { - m_scanner->next(); - ptr index = parseExpression(); - nodeFactory.markEndPosition(); - expectToken(Token::RBRACK); - expression = nodeFactory.createNode(expression, index); - } + { + m_scanner->next(); + ASTPointer index = parseExpression(); + nodeFactory.markEndPosition(); + expectToken(Token::RBRACK); + expression = nodeFactory.createNode(expression, index); + } break; case Token::PERIOD: - { - m_scanner->next(); - nodeFactory.markEndPosition(); - expression = nodeFactory.createNode(expression, expectIdentifierToken()); - } + { + m_scanner->next(); + nodeFactory.markEndPosition(); + expression = nodeFactory.createNode(expression, expectIdentifierToken()); + } break; case Token::LPAREN: - { - m_scanner->next(); - std::vector> arguments = parseFunctionCallArguments(); - nodeFactory.markEndPosition(); - expectToken(Token::RPAREN); - expression = nodeFactory.createNode(expression, arguments); - } + { + m_scanner->next(); + std::vector> arguments = parseFunctionCallArguments(); + nodeFactory.markEndPosition(); + expectToken(Token::RPAREN); + expression = nodeFactory.createNode(expression, arguments); + } break; default: return expression; @@ -445,16 +445,16 @@ ptr Parser::parseLeftHandSideExpression() } } -ptr Parser::parsePrimaryExpression() +ASTPointer Parser::parsePrimaryExpression() { ASTNodeFactory nodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); - ptr expression; + ASTPointer expression; switch (token) { case Token::TRUE_LITERAL: case Token::FALSE_LITERAL: - expression = nodeFactory.createNode(token, ptr()); + expression = nodeFactory.createNode(token, ASTPointer()); m_scanner->next(); break; case Token::NUMBER: @@ -467,12 +467,12 @@ ptr Parser::parsePrimaryExpression() expression = nodeFactory.createNode(getLiteralAndAdvance()); break; case Token::LPAREN: - { - m_scanner->next(); - ptr expression = parseExpression(); - expectToken(Token::RPAREN); - return expression; - } + { + m_scanner->next(); + ASTPointer expression = parseExpression(); + expectToken(Token::RPAREN); + return expression; + } default: if (Token::isElementaryTypeName(token)) { @@ -483,16 +483,16 @@ ptr Parser::parsePrimaryExpression() else { throwExpectationError("Expected primary expression."); - return ptr(); // this is not reached + return ASTPointer(); // this is not reached } break; } return expression; } -std::vector> Parser::parseFunctionCallArguments() +std::vector> Parser::parseFunctionCallArguments() { - std::vector> arguments; + std::vector> arguments; if (m_scanner->getCurrentToken() != Token::RPAREN) { arguments.push_back(parseExpression()); @@ -521,16 +521,16 @@ Token::Value Parser::expectAssignmentOperator() return op; } -ptr Parser::expectIdentifierToken() +ASTPointer Parser::expectIdentifierToken() { if (m_scanner->getCurrentToken() != Token::IDENTIFIER) throwExpectationError("Expected identifier"); return getLiteralAndAdvance(); } -ptr Parser::getLiteralAndAdvance() +ASTPointer Parser::getLiteralAndAdvance() { - ptr identifier = std::make_shared(m_scanner->getCurrentLiteral()); + ASTPointer identifier = std::make_shared(m_scanner->getCurrentLiteral()); m_scanner->next(); return identifier; } diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h index 2a8613216..7cc415136 100644 --- a/libsolidity/Parser.h +++ b/libsolidity/Parser.h @@ -34,7 +34,7 @@ class Scanner; class Parser { public: - ptr parse(std::shared_ptr const& _scanner); + ASTPointer parse(std::shared_ptr const& _scanner); private: class ASTNodeFactory; @@ -46,24 +46,24 @@ private: /// Parsing functions for the AST nodes /// @{ - ptr parseContractDefinition(); - ptr parseFunctionDefinition(bool _isPublic); - ptr parseStructDefinition(); - ptr parseVariableDeclaration(bool _allowVar); - ptr parseTypeName(bool _allowVar); - ptr parseMapping(); - ptr parseParameterList(bool _allowEmpty = true); - ptr parseBlock(); - ptr parseStatement(); - ptr parseIfStatement(); - ptr parseWhileStatement(); - ptr parseVariableDefinition(); - ptr parseExpression(); - ptr parseBinaryExpression(int _minPrecedence = 4); - ptr parseUnaryExpression(); - ptr parseLeftHandSideExpression(); - ptr parsePrimaryExpression(); - std::vector> parseFunctionCallArguments(); + ASTPointer parseContractDefinition(); + ASTPointer parseFunctionDefinition(bool _isPublic); + ASTPointer parseStructDefinition(); + ASTPointer parseVariableDeclaration(bool _allowVar); + ASTPointer parseTypeName(bool _allowVar); + ASTPointer parseMapping(); + ASTPointer parseParameterList(bool _allowEmpty = true); + ASTPointer parseBlock(); + ASTPointer parseStatement(); + ASTPointer parseIfStatement(); + ASTPointer parseWhileStatement(); + ASTPointer parseVariableDefinition(); + ASTPointer parseExpression(); + ASTPointer parseBinaryExpression(int _minPrecedence = 4); + ASTPointer parseUnaryExpression(); + ASTPointer parseLeftHandSideExpression(); + ASTPointer parsePrimaryExpression(); + std::vector> parseFunctionCallArguments(); /// @} /// Helper functions @@ -71,8 +71,8 @@ private: /// If current token value is not _value, throw exception otherwise advance token. void expectToken(Token::Value _value); Token::Value expectAssignmentOperator(); - ptr expectIdentifierToken(); - ptr getLiteralAndAdvance(); + ASTPointer expectIdentifierToken(); + ASTPointer getLiteralAndAdvance(); void throwExpectationError(std::string const& _description); /// @} diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index b65c0545b..d0183db9f 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -81,12 +81,15 @@ bool IsIdentifierPart(char c) int HexValue(char c) { - if (c >= '0' && c <= '9') return c - '0'; - else if (c >= 'a' && c <= 'f') return c - 'a' + 10; - else if (c >= 'A' && c <= 'F') return c - 'A' + 10; + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; else return -1; } -} +} // end anonymous namespace Scanner::Scanner(CharStream const& _source) { @@ -409,13 +412,15 @@ bool Scanner::scanEscape() c = '\t'; break; case 'u': - if (!scanHexNumber(c, 4)) return false; + if (!scanHexNumber(c, 4)) + return false; break; case 'v': c = '\v'; break; case 'x': - if (!scanHexNumber(c, 2)) return false; + if (!scanHexNumber(c, 2)) + return false; break; } @@ -612,9 +617,8 @@ case ch: int const keyword_length = sizeof(keyword) - 1; \ BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ - if (input == keyword) { \ - return token; \ - } \ + if (input == keyword) \ + return token; \ } KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) } @@ -635,9 +639,11 @@ Token::Value Scanner::scanIdentifierOrKeyword() char CharStream::advanceAndGet() { - if (isPastEndOfInput()) return 0; + if (isPastEndOfInput()) + return 0; ++m_pos; - if (isPastEndOfInput()) return 0; + if (isPastEndOfInput()) + return 0; return get(); } @@ -653,7 +659,8 @@ std::string CharStream::getLineAtPosition(int _position) const // if _position points to \n, it returns the line before the \n using size_type = std::string::size_type; size_type searchStart = std::min(m_source.size(), _position); - if (searchStart > 0) searchStart--; + if (searchStart > 0) + searchStart--; size_type lineStart = m_source.rfind('\n', searchStart); if (lineStart == std::string::npos) lineStart = 0; diff --git a/libsolidity/Scope.h b/libsolidity/Scope.h index ced783261..2e36e5281 100644 --- a/libsolidity/Scope.h +++ b/libsolidity/Scope.h @@ -23,7 +23,6 @@ #pragma once #include - #include #include diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 8ca2c6342..88abbc77d 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -28,7 +28,7 @@ namespace dev namespace solidity { -ptr Type::fromElementaryTypeName(Token::Value _typeToken) +std::shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) { if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { @@ -52,18 +52,18 @@ ptr Type::fromElementaryTypeName(Token::Value _typeToken) BOOST_ASSERT(false); // @todo add other tyes } -ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) +std::shared_ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) { return std::make_shared(*_typeName.getReferencedStruct()); } -ptr Type::fromMapping(Mapping const&) +std::shared_ptr Type::fromMapping(Mapping const&) { BOOST_ASSERT(false); //@todo not yet implemented - return ptr(); + return std::shared_ptr(); } -ptr Type::forLiteral(Literal const& _literal) +std::shared_ptr Type::forLiteral(Literal const& _literal) { switch (_literal.getToken()) { @@ -73,13 +73,13 @@ ptr Type::forLiteral(Literal const& _literal) case Token::NUMBER: return IntegerType::smallestTypeForLiteral(_literal.getValue()); case Token::STRING_LITERAL: - return ptr(); // @todo + return std::shared_ptr(); // @todo default: - return ptr(); + return std::shared_ptr(); } } -ptr IntegerType::smallestTypeForLiteral(const std::string&) +std::shared_ptr IntegerType::smallestTypeForLiteral(std::string const&) { //@todo return std::make_shared(256, Modifier::UNSIGNED); diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 9ab710942..e0c09bdcf 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -26,7 +26,7 @@ #include #include #include - +#include #include namespace dev @@ -34,18 +34,6 @@ namespace dev namespace solidity { -// AST forward declarations -class ContractDefinition; -class FunctionDefinition; -class StructDefinition; -class Literal; -class ElementaryTypeName; -class UserDefinedTypeName; -class Mapping; - -template -using ptr = std::shared_ptr; - // @todo realMxN, string, mapping class Type: private boost::noncopyable @@ -57,11 +45,11 @@ public: }; //! factory functions that convert an AST TypeName to a Type. - static ptr fromElementaryTypeName(Token::Value _typeToken); - static ptr fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); - static ptr fromMapping(Mapping const& _typeName); + static std::shared_ptr fromElementaryTypeName(Token::Value _typeToken); + static std::shared_ptr fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); + static std::shared_ptr fromMapping(Mapping const& _typeName); - static ptr forLiteral(Literal const& _literal); + static std::shared_ptr forLiteral(Literal const& _literal); virtual Category getCategory() const = 0; virtual bool isImplicitlyConvertibleTo(Type const&) const { return false; } @@ -82,7 +70,7 @@ public: }; virtual Category getCategory() const { return Category::INTEGER; } - static ptr smallestTypeForLiteral(std::string const& _literal); + static std::shared_ptr smallestTypeForLiteral(std::string const& _literal); explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); @@ -95,6 +83,7 @@ public: bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } bool isAddress() const { return m_modifier == Modifier::ADDRESS; } int isSigned() const { return m_modifier == Modifier::SIGNED; } + private: int m_bits; Modifier m_modifier; @@ -125,6 +114,7 @@ public: virtual Category getCategory() const { return Category::CONTRACT; } ContractType(ContractDefinition const& _contract): m_contract(_contract) {} virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; + private: ContractDefinition const& m_contract; }; @@ -139,6 +129,7 @@ public: { return _operator == Token::DELETE; } + private: StructDefinition const& m_struct; }; @@ -150,6 +141,7 @@ public: FunctionType(FunctionDefinition const& _function): m_function(_function) {} FunctionDefinition const& getFunction() const { return m_function; } + private: FunctionDefinition const& m_function; }; @@ -175,11 +167,12 @@ class TypeType: public Type { public: virtual Category getCategory() const { return Category::TYPE; } - TypeType(ptr const& _actualType): m_actualType(_actualType) {} + TypeType(std::shared_ptr const& _actualType): m_actualType(_actualType) {} + + std::shared_ptr const& getActualType() const { return m_actualType; } - ptr const& getActualType() { return m_actualType; } private: - ptr m_actualType; + std::shared_ptr m_actualType; }; diff --git a/solc/main.cpp b/solc/main.cpp index a2b791462..e155b0fe6 100644 --- a/solc/main.cpp +++ b/solc/main.cpp @@ -15,9 +15,9 @@ namespace dev namespace solidity { -ptr parseAST(std::string const& _source) +ASTPointer parseAST(std::string const& _source) { - ptr scanner = std::make_shared(CharStream(_source)); + ASTPointer scanner = std::make_shared(CharStream(_source)); Parser parser; return parser.parse(scanner); } @@ -73,7 +73,7 @@ int main(int argc, char** argv) } std::cout << "Parsing..." << std::endl; // @todo catch exception - dev::solidity::ptr ast = dev::solidity::parseAST(src); + dev::solidity::ASTPointer ast = dev::solidity::parseAST(src); std::cout << "Syntax tree for the contract:" << std::endl; dev::solidity::ASTPrinter printer(ast, src); printer.print(std::cout); diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index ed2cb7b53..833ae6d4b 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -41,7 +41,7 @@ namespace void parseTextAndResolveNames(const std::string& _source) { Parser parser; - ptr contract = parser.parse( + ASTPointer contract = parser.parse( std::make_shared(CharStream(_source))); NameAndTypeResolver resolver; resolver.resolveNamesAndTypes(*contract); diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index ef434cc35..025cd74d1 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -37,7 +37,7 @@ namespace test namespace { -ptr parseText(const std::string& _source) +ASTPointer parseText(const std::string& _source) { Parser parser; return parser.parse(std::make_shared(CharStream(_source))); From fb35847012cdd29704cb2df8ed65f621ae5a1bc1 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 20 Oct 2014 16:28:24 +0200 Subject: [PATCH 30/34] Access modifier cleanup. --- libsolidity/AST.h | 24 ++++++++++++++++++++++++ libsolidity/NameAndTypeResolver.h | 4 +++- libsolidity/Scanner.h | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 319fc0581..0f015ab3d 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -58,6 +58,7 @@ public: } Location const& getLocation() const { return m_location; } + private: Location m_location; }; @@ -69,6 +70,7 @@ public: : ASTNode(_location), m_name(_name) {} const ASTString& getName() const { return *m_name; } + private: ASTPointer m_name; }; @@ -92,6 +94,7 @@ public: std::vector> const& getDefinedStructs() { return m_definedStructs; } std::vector> const& getStateVariables() { return m_stateVariables; } std::vector> const& getDefinedFunctions() { return m_definedFunctions; } + private: std::vector> m_definedStructs; std::vector> m_stateVariables; @@ -122,6 +125,7 @@ public: virtual void accept(ASTVisitor& _visitor) override; std::vector> const& getParameters() { return m_parameters; } + private: std::vector> m_parameters; }; @@ -145,6 +149,7 @@ public: ParameterList& getParameterList() { return *m_parameters; } ASTPointer const& getReturnParameterList() const { return m_returnParameters; } Block& getBody() { return *m_body; } + private: bool m_isPublic; ASTPointer m_parameters; @@ -168,6 +173,7 @@ public: //! declared and there is no assignment to the variable that fixes the type. std::shared_ptr const& getType() const { return m_type; } void setType(std::shared_ptr const& _type) { m_type = _type; } + private: ASTPointer m_typeName; ///< can be empty ("var") @@ -196,6 +202,7 @@ public: virtual std::shared_ptr toType() override { return Type::fromElementaryTypeName(m_type); } Token::Value getType() const { return m_type; } + private: Token::Value m_type; }; @@ -211,6 +218,7 @@ public: const ASTString& getName() const { return *m_name; } void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } + private: ASTPointer m_name; @@ -225,6 +233,7 @@ public: : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; virtual std::shared_ptr toType() override { return Type::fromMapping(*this); } + private: ASTPointer m_keyType; ASTPointer m_valueType; @@ -245,6 +254,7 @@ public: //! For expressions, this also returns the inferred type of the expression. For other //! statements, returns the empty pointer. virtual void checkTypeRequirements() = 0; + protected: //! Check that the inferred type for _expression is _expectedType or at least implicitly //! convertible to _expectedType. If not, throw exception. @@ -259,6 +269,7 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; + private: std::vector> m_statements; }; @@ -272,6 +283,7 @@ public: m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; + private: ASTPointer m_condition; ASTPointer m_trueBody; @@ -293,6 +305,7 @@ public: : BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; + private: ASTPointer m_condition; ASTPointer m_body; @@ -323,6 +336,7 @@ public: virtual void checkTypeRequirements() override; void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } + private: ASTPointer m_expression; //< value to return, optional @@ -348,6 +362,7 @@ class Expression: public Statement public: Expression(Location const& _location): Statement(_location) {} std::shared_ptr const& getType() const { return m_type; } + protected: //! Inferred type of the expression, only filled after a call to checkTypeRequirements(). std::shared_ptr m_type; @@ -369,6 +384,7 @@ public: virtual void checkTypeRequirements() override; Token::Value getAssignmentOperator() const { return m_assigmentOperator; } + private: ASTPointer m_leftHandSide; Token::Value m_assigmentOperator; @@ -387,6 +403,7 @@ public: Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } + private: Token::Value m_operator; ASTPointer m_subExpression; @@ -403,6 +420,7 @@ public: virtual void checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } + private: ASTPointer m_left; Token::Value m_operator; @@ -420,6 +438,7 @@ public: : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; + private: ASTPointer m_expression; std::vector> m_arguments; @@ -434,6 +453,7 @@ public: virtual void accept(ASTVisitor& _visitor) override; const ASTString& getMemberName() const { return *m_memberName; } virtual void checkTypeRequirements() override; + private: ASTPointer m_expression; ASTPointer m_memberName; @@ -447,6 +467,7 @@ public: : Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; + private: ASTPointer m_base; ASTPointer m_index; @@ -469,6 +490,7 @@ public: ASTString const& getName() const { return *m_name; } void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } + private: ASTPointer m_name; @@ -485,6 +507,7 @@ public: virtual void checkTypeRequirements() override; Token::Value getTypeToken() const { return m_typeToken; } + private: Token::Value m_typeToken; }; @@ -499,6 +522,7 @@ public: Token::Value getToken() const { return m_token; } ASTString const& getValue() const { return *m_value; } + private: Token::Value m_token; ASTPointer m_value; diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 625454566..5305565b3 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -42,6 +42,7 @@ public: void resolveNamesAndTypes(ContractDefinition& _contract); Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + private: void reset(); @@ -83,13 +84,14 @@ class ReferencesResolver: private ASTVisitor { public: ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ParameterList* _returnParameters); + private: virtual void endVisit(VariableDeclaration& _variable) override; virtual bool visit(Identifier& _identifier) override; virtual bool visit(UserDefinedTypeName& _typeName) override; virtual bool visit(Mapping&) override; virtual bool visit(Return& _return) override; -private: + NameAndTypeResolver& m_resolver; ParameterList* m_returnParameters; }; diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index e71fb6347..05265d603 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -77,6 +77,7 @@ public: std::string getLineAtPosition(int _position) const; std::tuple translatePositionToLineColumn(int _position) const; /// @} + private: std::string m_source; size_t m_pos; From cf2cc8e8ba917e9b07d75e6015720f64df7daa8a Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 20 Oct 2014 16:37:04 +0200 Subject: [PATCH 31/34] Cleanup of copyright headers. --- libsolidity/Scanner.cpp | 92 +++++++++++++++++++++++------------------ libsolidity/Scanner.h | 92 +++++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 82 deletions(-) diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index d0183db9f..35da248a4 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -1,44 +1,54 @@ -// Copyright 2006-2012, the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Modifications as part of cpp-ethereum under the following license: -// -// cpp-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cpp-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cpp-ethereum. If not, see . +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + + This file is derived from the file "scanner.cc", which was part of the + V8 project. The original copyright header follows: + + Copyright 2006-2012, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/** + * @author Christian + * @date 2014 + * Solidity scanner. + */ #include #include diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 05265d603..232b300a5 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -1,44 +1,54 @@ -// Copyright 2006-2012, the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Modifications as part of cpp-ethereum under the following license: -// -// cpp-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cpp-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cpp-ethereum. If not, see . +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . + + This file is derived from the file "scanner.h", which was part of the + V8 project. The original copyright header follows: + + Copyright 2006-2012, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/** + * @author Christian + * @date 2014 + * Solidity scanner. + */ #pragma once From 9a059ec6626ba93fffe96d03b37564bb04ef4133 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 23 Oct 2014 00:24:07 +0200 Subject: [PATCH 32/34] Corrected spacing around colon. --- libsolidity/AST.h | 113 ++++++++++++++-------------- libsolidity/ASTPrinter.cpp | 4 +- libsolidity/NameAndTypeResolver.cpp | 13 ++-- libsolidity/NameAndTypeResolver.h | 2 +- libsolidity/Parser.cpp | 3 +- libsolidity/Scanner.h | 2 +- libsolidity/Types.cpp | 4 +- 7 files changed, 68 insertions(+), 73 deletions(-) diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 0f015ab3d..d5e1e0662 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -43,9 +43,7 @@ class ASTVisitor; class ASTNode: private boost::noncopyable { public: - explicit ASTNode(Location const& _location) - : m_location(_location) - {} + explicit ASTNode(Location const& _location): m_location(_location) {} virtual ~ASTNode() {} @@ -66,8 +64,8 @@ private: class Declaration: public ASTNode { public: - Declaration(Location const& _location, ASTPointer const& _name) - : ASTNode(_location), m_name(_name) {} + Declaration(Location const& _location, ASTPointer const& _name): + ASTNode(_location), m_name(_name) {} const ASTString& getName() const { return *m_name; } @@ -82,11 +80,11 @@ public: ASTPointer const& _name, std::vector> const& _definedStructs, std::vector> const& _stateVariables, - std::vector> const& _definedFunctions) - : Declaration(_location, _name), - m_definedStructs(_definedStructs), - m_stateVariables(_stateVariables), - m_definedFunctions(_definedFunctions) + std::vector> const& _definedFunctions): + Declaration(_location, _name), + m_definedStructs(_definedStructs), + m_stateVariables(_stateVariables), + m_definedFunctions(_definedFunctions) {} virtual void accept(ASTVisitor& _visitor) override; @@ -106,8 +104,8 @@ class StructDefinition: public Declaration public: StructDefinition(Location const& _location, ASTPointer const& _name, - std::vector> const& _members) - : Declaration(_location, _name), m_members(_members) {} + std::vector> const& _members): + Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; private: @@ -120,8 +118,9 @@ private: class ParameterList: public ASTNode { public: - ParameterList(Location const& _location, std::vector> const& _parameters) - : ASTNode(_location), m_parameters(_parameters) {} + ParameterList(Location const& _location, + std::vector> const& _parameters): + ASTNode(_location), m_parameters(_parameters) {} virtual void accept(ASTVisitor& _visitor) override; std::vector> const& getParameters() { return m_parameters; } @@ -137,10 +136,10 @@ public: ASTPointer const& _parameters, bool _isDeclaredConst, ASTPointer const& _returnParameters, - ASTPointer const& _body) - : Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), - m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), - m_body(_body) {} + ASTPointer const& _body): + Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), + m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), + m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; bool isPublic() const { return m_isPublic; } @@ -162,8 +161,8 @@ class VariableDeclaration: public Declaration { public: VariableDeclaration(Location const& _location, ASTPointer const& _type, - ASTPointer const& _name) - : Declaration(_location, _name), m_typeName(_type) {} + ASTPointer const& _name): + Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; bool isTypeGivenExplicitly() const { return m_typeName.get() != nullptr; } @@ -196,8 +195,8 @@ public: class ElementaryTypeName: public TypeName { public: - explicit ElementaryTypeName(Location const& _location, Token::Value _type) - : TypeName(_location), m_type(_type) {} + explicit ElementaryTypeName(Location const& _location, Token::Value _type): + TypeName(_location), m_type(_type) {} virtual void accept(ASTVisitor& _visitor) override; virtual std::shared_ptr toType() override { return Type::fromElementaryTypeName(m_type); } @@ -210,8 +209,8 @@ private: class UserDefinedTypeName: public TypeName { public: - UserDefinedTypeName(Location const& _location, ASTPointer const& _name) - : TypeName(_location), m_name(_name) {} + UserDefinedTypeName(Location const& _location, ASTPointer const& _name): + TypeName(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual std::shared_ptr toType() override { return Type::fromUserDefinedTypeName(*this); } @@ -229,8 +228,8 @@ class Mapping: public TypeName { public: Mapping(Location const& _location, ASTPointer const& _keyType, - ASTPointer const& _valueType) - : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} + ASTPointer const& _valueType): + TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; virtual std::shared_ptr toType() override { return Type::fromMapping(*this); } @@ -264,8 +263,8 @@ protected: class Block: public Statement { public: - Block(Location const& _location, std::vector> const& _statements) - : Statement(_location), m_statements(_statements) {} + Block(Location const& _location, std::vector> const& _statements): + Statement(_location), m_statements(_statements) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -278,9 +277,9 @@ class IfStatement: public Statement { public: IfStatement(Location const& _location, ASTPointer const& _condition, - ASTPointer const& _trueBody, ASTPointer const& _falseBody) - : Statement(_location), m_condition(_condition), - m_trueBody(_trueBody), m_falseBody(_falseBody) {} + ASTPointer const& _trueBody, ASTPointer const& _falseBody): + Statement(_location), + m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -301,8 +300,8 @@ class WhileStatement: public BreakableStatement { public: WhileStatement(Location const& _location, ASTPointer const& _condition, - ASTPointer const& _body) - : BreakableStatement(_location), m_condition(_condition), m_body(_body) {} + ASTPointer const& _body): + BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -330,8 +329,8 @@ public: class Return: public Statement { public: - Return(Location const& _location, ASTPointer _expression) - : Statement(_location), m_expression(_expression) {} + Return(Location const& _location, ASTPointer _expression): + Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -347,8 +346,8 @@ class VariableDefinition: public Statement { public: VariableDefinition(Location const& _location, ASTPointer _variable, - ASTPointer _value) - : Statement(_location), m_variable(_variable), m_value(_value) {} + ASTPointer _value): + Statement(_location), m_variable(_variable), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -377,9 +376,9 @@ class Assignment: public Expression { public: Assignment(Location const& _location, ASTPointer const& _leftHandSide, - Token::Value _assignmentOperator, ASTPointer const& _rightHandSide) - : Expression(_location), m_leftHandSide(_leftHandSide), - m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} + Token::Value _assignmentOperator, ASTPointer const& _rightHandSide): + Expression(_location), m_leftHandSide(_leftHandSide), + m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -395,9 +394,9 @@ class UnaryOperation: public Expression { public: UnaryOperation(Location const& _location, Token::Value _operator, - ASTPointer const& _subExpression, bool _isPrefix) - : Expression(_location), m_operator(_operator), - m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} + ASTPointer const& _subExpression, bool _isPrefix): + Expression(_location), m_operator(_operator), + m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -414,8 +413,8 @@ class BinaryOperation: public Expression { public: BinaryOperation(Location const& _location, ASTPointer const& _left, - Token::Value _operator, ASTPointer const& _right) - : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} + Token::Value _operator, ASTPointer const& _right): + Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -434,8 +433,8 @@ class FunctionCall: public Expression { public: FunctionCall(Location const& _location, ASTPointer const& _expression, - std::vector> const& _arguments) - : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} + std::vector> const& _arguments): + Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -448,8 +447,8 @@ class MemberAccess: public Expression { public: MemberAccess(Location const& _location, ASTPointer _expression, - ASTPointer const& _memberName) - : Expression(_location), m_expression(_expression), m_memberName(_memberName) {} + ASTPointer const& _memberName): + Expression(_location), m_expression(_expression), m_memberName(_memberName) {} virtual void accept(ASTVisitor& _visitor) override; const ASTString& getMemberName() const { return *m_memberName; } virtual void checkTypeRequirements() override; @@ -463,8 +462,8 @@ class IndexAccess: public Expression { public: IndexAccess(Location const& _location, ASTPointer const& _base, - ASTPointer const& _index) - : Expression(_location), m_base(_base), m_index(_index) {} + ASTPointer const& _index): + Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -482,8 +481,8 @@ public: class Identifier: public PrimaryExpression { public: - Identifier(Location const& _location, ASTPointer const& _name) - : PrimaryExpression(_location), m_name(_name) {} + Identifier(Location const& _location, ASTPointer const& _name): + PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -501,8 +500,8 @@ private: class ElementaryTypeNameExpression: public PrimaryExpression { public: - ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken) - : PrimaryExpression(_location), m_typeToken(_typeToken) {} + ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken): + PrimaryExpression(_location), m_typeToken(_typeToken) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; @@ -515,8 +514,8 @@ private: class Literal: public PrimaryExpression { public: - Literal(Location const& _location, Token::Value _token, ASTPointer const& _value) - : PrimaryExpression(_location), m_token(_token), m_value(_value) {} + Literal(Location const& _location, Token::Value _token, ASTPointer const& _value): + PrimaryExpression(_location), m_token(_token), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; virtual void checkTypeRequirements() override; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index af8f20157..c512bd3f9 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -28,8 +28,8 @@ namespace dev namespace solidity { -ASTPrinter::ASTPrinter(ASTPointer const& _ast, std::string const& _source) - : m_indentation(0), m_source(_source), m_ast(_ast) +ASTPrinter::ASTPrinter(ASTPointer const& _ast, std::string const& _source): + m_indentation(0), m_source(_source), m_ast(_ast) { } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 84b12a238..707b6ce14 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -32,10 +32,6 @@ namespace solidity { -NameAndTypeResolver::NameAndTypeResolver() -{ -} - void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { reset(); @@ -72,8 +68,9 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name } -DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map& _scopes, ASTNode& _astRoot) - : m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) +DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map& _scopes, + ASTNode& _astRoot): + m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) { _astRoot.accept(*this); } @@ -146,8 +143,8 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio } ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ParameterList* _returnParameters) - : m_resolver(_resolver), m_returnParameters(_returnParameters) + ParameterList* _returnParameters): + m_resolver(_resolver), m_returnParameters(_returnParameters) { _root.accept(*this); } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 5305565b3..7abcbb0c5 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -38,7 +38,7 @@ namespace solidity class NameAndTypeResolver: private boost::noncopyable { public: - NameAndTypeResolver(); + NameAndTypeResolver() {} void resolveNamesAndTypes(ContractDefinition& _contract); Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index 7b1fdeb28..408aa7bd6 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -43,7 +43,7 @@ ASTPointer Parser::parse(std::shared_ptr const& _sc class Parser::ASTNodeFactory { public: - ASTNodeFactory(Parser const& _parser) : m_parser(_parser), m_location(_parser.getPosition(), -1) {} + ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1) {} void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void setLocationEmpty() { m_location.end = m_location.start; } @@ -73,7 +73,6 @@ int Parser::getEndPosition() const return m_scanner->getCurrentLocation().end; } - ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); diff --git a/libsolidity/Scanner.h b/libsolidity/Scanner.h index 232b300a5..adae10dca 100644 --- a/libsolidity/Scanner.h +++ b/libsolidity/Scanner.h @@ -73,7 +73,7 @@ class ParserRecorder; class CharStream { public: - CharStream() : m_pos(0) {} + CharStream(): m_pos(0) {} explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {} int getPos() const { return m_pos; } bool isPastEndOfInput() const { return m_pos >= m_source.size(); } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 88abbc77d..7634951a1 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -85,8 +85,8 @@ std::shared_ptr IntegerType::smallestTypeForLiteral(std::string con return std::make_shared(256, Modifier::UNSIGNED); } -IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier) - : m_bits(_bits), m_modifier(_modifier) +IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): + m_bits(_bits), m_modifier(_modifier) { BOOST_ASSERT(_bits > 0 && _bits <= 256 && _bits % 8 == 0); if (isAddress()) From 61720f33bef16c09e9d2806ad7f74579080eff97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 23 Oct 2014 13:32:19 +0200 Subject: [PATCH 33/34] Revert "Prevent integer overflow in some gas calculations" This reverts commit 465e682dc61e6e0cf55a25a1bd94356a004ac673. --- libethereum/Executive.cpp | 2 +- libethereum/Interface.h | 2 +- libevm/VM.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index fe5b21761..193010cfa 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -66,7 +66,7 @@ bool Executive::setup(bytesConstRef _rlp) } // Check gas cost is enough. - u256 gasCost = u256(m_t.data.size()) * c_txDataGas + c_txGas; + u256 gasCost = m_t.data.size() * c_txDataGas + c_txGas; if (m_t.gas < gasCost) { diff --git a/libethereum/Interface.h b/libethereum/Interface.h index e746ae760..7ae650590 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -123,7 +123,7 @@ public: virtual Addresses addresses(int _block) const = 0; /// Get the fee associated for a transaction with the given data. - static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return u256(c_txDataGas) * _dataCount + c_txGas + _gas; } + static u256 txGas(unsigned _dataCount, u256 _gas = 0) { return c_txDataGas * _dataCount + c_txGas + _gas; } /// Get the remaining gas limit in this block. virtual u256 gasLimitRemaining() const = 0; diff --git a/libevm/VM.h b/libevm/VM.h index e092bdfed..2eb34ed04 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -168,13 +168,13 @@ template dev::bytesConstRef dev::eth::VM::go(Ext& _ext, OnOpFunc con case Instruction::CALL: require(7); - runGas = bigint(c_callGas) + m_stack[m_stack.size() - 1]; + runGas = c_callGas + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; case Instruction::CALLCODE: require(7); - runGas = bigint(c_callGas) + m_stack[m_stack.size() - 1]; + runGas = c_callGas + m_stack[m_stack.size() - 1]; newTempSize = std::max(memNeed(m_stack[m_stack.size() - 6], m_stack[m_stack.size() - 7]), memNeed(m_stack[m_stack.size() - 4], m_stack[m_stack.size() - 5])); break; From 51a0115fa5c97c89b49125244ca48601b52a0c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 23 Oct 2014 13:43:54 +0200 Subject: [PATCH 34/34] Revert u256 to int32_t change for gas fee constants --- libevm/FeeStructure.cpp | 16 +++++++++++++++- libevm/FeeStructure.h | 23 +++++++++++------------ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/libevm/FeeStructure.cpp b/libevm/FeeStructure.cpp index b9974a3e7..d29b9fef9 100644 --- a/libevm/FeeStructure.cpp +++ b/libevm/FeeStructure.cpp @@ -16,8 +16,22 @@ */ /** @file FeeStructure.cpp * @author Gav Wood - * @author Pawel Bylica * @date 2014 */ #include "FeeStructure.h" + +using namespace std; +using namespace dev; +using namespace dev::eth; + +u256 const dev::eth::c_stepGas = 1; +u256 const dev::eth::c_balanceGas = 20; +u256 const dev::eth::c_sha3Gas = 20; +u256 const dev::eth::c_sloadGas = 20; +u256 const dev::eth::c_sstoreGas = 100; +u256 const dev::eth::c_createGas = 100; +u256 const dev::eth::c_callGas = 20; +u256 const dev::eth::c_memoryGas = 1; +u256 const dev::eth::c_txDataGas = 5; +u256 const dev::eth::c_txGas = 500; diff --git a/libevm/FeeStructure.h b/libevm/FeeStructure.h index 6fa30dea4..76be9a398 100644 --- a/libevm/FeeStructure.h +++ b/libevm/FeeStructure.h @@ -16,29 +16,28 @@ */ /** @file FeeStructure.h * @author Gav Wood - * @author Pawel Bylica * @date 2014 */ #pragma once -#include +#include namespace dev { namespace eth { -static uint32_t const c_stepGas = 1; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. -static uint32_t const c_balanceGas = 20; ///< Once per BALANCE operation. -static uint32_t const c_sha3Gas = 20; ///< Once per SHA3 operation. -static uint32_t const c_sloadGas = 20; ///< Once per SLOAD operation. -static uint32_t const c_sstoreGas = 100; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). -static uint32_t const c_createGas = 100; ///< Once per CREATE operation & contract-creation transaction. -static uint32_t const c_callGas = 20; ///< Once per CALL operation & message call transaction. -static uint32_t const c_memoryGas = 1; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. -static uint32_t const c_txDataGas = 5; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. -static uint32_t const c_txGas = 500; ///< Per transaction. NOTE: Not payable on data of calls between transactions. +extern u256 const c_stepGas; ///< Once per operation, except for SSTORE, SLOAD, BALANCE, SHA3, CREATE, CALL. +extern u256 const c_balanceGas; ///< Once per BALANCE operation. +extern u256 const c_sha3Gas; ///< Once per SHA3 operation. +extern u256 const c_sloadGas; ///< Once per SLOAD operation. +extern u256 const c_sstoreGas; ///< Once per non-zero storage element in a CREATE call/transaction. Also, once/twice per SSTORE operation depending on whether the zeroness changes (twice iff it changes from zero; nothing at all if to zero) or doesn't (once). +extern u256 const c_createGas; ///< Once per CREATE operation & contract-creation transaction. +extern u256 const c_callGas; ///< Once per CALL operation & message call transaction. +extern u256 const c_memoryGas; ///< Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. +extern u256 const c_txDataGas; ///< Per byte of data attached to a transaction. NOTE: Not payable on data of calls between transactions. +extern u256 const c_txGas; ///< Per transaction. NOTE: Not payable on data of calls between transactions. } }