diff options
author | Christian <c@ethdev.com> | 2015-02-20 22:52:30 +0800 |
---|---|---|
committer | Christian <c@ethdev.com> | 2015-02-21 22:10:01 +0800 |
commit | 5e3208317922d20a5b3b760df872a589d87bf94d (patch) | |
tree | 982cb7d888c7ae395de3f533c8e8d1bdd27c5c27 | |
parent | 75498a48d83277240605b43e27197be36c02ce23 (diff) | |
download | dexon-solidity-5e3208317922d20a5b3b760df872a589d87bf94d.tar dexon-solidity-5e3208317922d20a5b3b760df872a589d87bf94d.tar.gz dexon-solidity-5e3208317922d20a5b3b760df872a589d87bf94d.tar.bz2 dexon-solidity-5e3208317922d20a5b3b760df872a589d87bf94d.tar.lz dexon-solidity-5e3208317922d20a5b3b760df872a589d87bf94d.tar.xz dexon-solidity-5e3208317922d20a5b3b760df872a589d87bf94d.tar.zst dexon-solidity-5e3208317922d20a5b3b760df872a589d87bf94d.zip |
Parsing of array types and basic implementation.
-rw-r--r-- | AST.h | 31 | ||||
-rw-r--r-- | ASTForward.h | 1 | ||||
-rw-r--r-- | ASTPrinter.cpp | 12 | ||||
-rw-r--r-- | ASTPrinter.h | 2 | ||||
-rw-r--r-- | ASTVisitor.h | 4 | ||||
-rw-r--r-- | AST_accept.h | 22 | ||||
-rw-r--r-- | CompilerUtils.cpp | 32 | ||||
-rw-r--r-- | CompilerUtils.h | 4 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 23 | ||||
-rw-r--r-- | ExpressionCompiler.h | 4 | ||||
-rw-r--r-- | NameAndTypeResolver.cpp | 6 | ||||
-rw-r--r-- | Parser.cpp | 195 | ||||
-rw-r--r-- | Parser.h | 40 | ||||
-rw-r--r-- | Types.cpp | 64 | ||||
-rw-r--r-- | Types.h | 41 |
15 files changed, 360 insertions, 121 deletions
@@ -441,7 +441,7 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - TypeName const* getTypeName() const { return m_typeName.get(); } + TypeName* getTypeName() { return m_typeName.get(); } ASTPointer<Expression> const& getValue() const { return m_value; } /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly @@ -588,7 +588,7 @@ public: /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared /// pointer until the types have been resolved using the @ref NameAndTypeResolver. /// If it returns an empty shared pointer after that, this indicates that the type was not found. - virtual std::shared_ptr<Type const> toType() const = 0; + virtual std::shared_ptr<Type const> toType() = 0; }; /** @@ -605,7 +605,7 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr<Type const> toType() const override { return Type::fromElementaryTypeName(m_type); } + virtual std::shared_ptr<Type const> toType() override { return Type::fromElementaryTypeName(m_type); } Token::Value getTypeName() const { return m_type; } @@ -623,7 +623,7 @@ public: TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr<Type const> toType() const override { return Type::fromUserDefinedTypeName(*this); } + virtual std::shared_ptr<Type const> toType() override { return Type::fromUserDefinedTypeName(*this); } ASTString const& getName() const { return *m_name; } void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } @@ -646,7 +646,7 @@ public: TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr<Type const> toType() const override { return Type::fromMapping(*this); } + virtual TypePointer toType() override { return Type::fromMapping(*m_keyType, *m_valueType); } ElementaryTypeName const& getKeyType() const { return *m_keyType; } TypeName const& getValueType() const { return *m_valueType; } @@ -656,6 +656,27 @@ private: ASTPointer<TypeName> m_valueType; }; +/** + * An array type, can be "typename[]" or "typename[<expression>]". + */ +class ArrayTypeName: public TypeName +{ +public: + ArrayTypeName(Location const& _location, ASTPointer<TypeName> const& _baseType, + ASTPointer<Expression> const& _length): + TypeName(_location), m_baseType(_baseType), m_length(_length) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + virtual std::shared_ptr<Type const> toType() override { return Type::fromArrayTypeName(*m_baseType, m_length.get()); } + + TypeName const& getBaseType() const { return *m_baseType; } + Expression const* getLength() const { return m_length.get(); } + +private: + ASTPointer<TypeName> m_baseType; + ASTPointer<Expression> m_length; ///< Length of the array, might be empty. +}; + /// @} /// Statements diff --git a/ASTForward.h b/ASTForward.h index 3a151b63..0ba485a2 100644 --- a/ASTForward.h +++ b/ASTForward.h @@ -53,6 +53,7 @@ class TypeName; class ElementaryTypeName; class UserDefinedTypeName; class Mapping; +class ArrayTypeName; class Statement; class Block; class PlaceholderStatement; diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp index 209bb73e..5bcc46df 100644 --- a/ASTPrinter.cpp +++ b/ASTPrinter.cpp @@ -155,6 +155,13 @@ bool ASTPrinter::visit(Mapping const& _node) return goDeeper(); } +bool ASTPrinter::visit(ArrayTypeName const& _node) +{ + writeLine("ArrayTypeName"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(Statement const& _node) { writeLine("Statement"); @@ -419,6 +426,11 @@ void ASTPrinter::endVisit(Mapping const&) m_indentation--; } +void ASTPrinter::endVisit(ArrayTypeName const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(Statement const&) { m_indentation--; diff --git a/ASTPrinter.h b/ASTPrinter.h index 7a0ef5a6..a1797383 100644 --- a/ASTPrinter.h +++ b/ASTPrinter.h @@ -58,6 +58,7 @@ public: bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(Mapping const& _node) override; + bool visit(ArrayTypeName const& _node) override; bool visit(Statement const& _node) override; bool visit(Block const& _node) override; bool visit(PlaceholderStatement const& _node) override; @@ -99,6 +100,7 @@ public: void endVisit(ElementaryTypeName const&) override; void endVisit(UserDefinedTypeName const&) override; void endVisit(Mapping const&) override; + void endVisit(ArrayTypeName const&) override; void endVisit(Statement const&) override; void endVisit(Block const&) override; void endVisit(PlaceholderStatement const&) override; diff --git a/ASTVisitor.h b/ASTVisitor.h index 2ecfbe4b..3eeb9c45 100644 --- a/ASTVisitor.h +++ b/ASTVisitor.h @@ -59,6 +59,7 @@ public: virtual bool visit(ElementaryTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; } virtual bool visit(Mapping&) { return true; } + virtual bool visit(ArrayTypeName&) { return true; } virtual bool visit(Statement&) { return true; } virtual bool visit(Block&) { return true; } virtual bool visit(PlaceholderStatement&) { return true; } @@ -102,6 +103,7 @@ public: virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { } virtual void endVisit(Mapping&) { } + virtual void endVisit(ArrayTypeName&) { } virtual void endVisit(Statement&) { } virtual void endVisit(Block&) { } virtual void endVisit(PlaceholderStatement&) { } @@ -149,6 +151,7 @@ public: virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; } virtual bool visit(Mapping const&) { return true; } + virtual bool visit(ArrayTypeName const&) { return true; } virtual bool visit(Statement const&) { return true; } virtual bool visit(Block const&) { return true; } virtual bool visit(PlaceholderStatement const&) { return true; } @@ -192,6 +195,7 @@ public: virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(UserDefinedTypeName const&) { } virtual void endVisit(Mapping const&) { } + virtual void endVisit(ArrayTypeName const&) { } virtual void endVisit(Statement const&) { } virtual void endVisit(Block const&) { } virtual void endVisit(PlaceholderStatement const&) { } diff --git a/AST_accept.h b/AST_accept.h index 5bd6993d..61b8280d 100644 --- a/AST_accept.h +++ b/AST_accept.h @@ -327,6 +327,28 @@ void Mapping::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void ArrayTypeName::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_baseType->accept(_visitor); + if (m_length) + m_length->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ArrayTypeName::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_baseType->accept(_visitor); + if (m_length) + m_length->accept(_visitor); + } + _visitor.endVisit(*this); +} + void Block::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 047bc6d6..c7ce9445 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -36,14 +36,14 @@ const unsigned int CompilerUtils::dataStartOffset = 4; unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) { - solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type."); + solAssert(_type.getCategory() != Type::Category::Array, "Unable to statically load dynamic type."); m_context << u256(_offset); return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); } void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) { - solAssert(_type.getCategory() != Type::Category::ByteArray, "Byte arrays not yet implemented."); + solAssert(_type.getCategory() != Type::Category::Array, "Arrays not yet implemented."); m_context << eth::Instruction::DUP1; unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); // update memory counter @@ -55,7 +55,7 @@ void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries) { - solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type."); + solAssert(_type.getCategory() != Type::Category::Array, "Unable to statically store dynamic type."); unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); if (numBytes > 0) m_context << u256(_offset) << eth::Instruction::MSTORE; @@ -64,11 +64,12 @@ unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries) { - if (_type.getCategory() == Type::Category::ByteArray) + if (_type.getCategory() == Type::Category::Array) { - auto const& type = dynamic_cast<ByteArrayType const&>(_type); + auto const& type = dynamic_cast<ArrayType const&>(_type); + solAssert(type.isByteArray(), "Non byte arrays not yet implemented here."); - if (type.getLocation() == ByteArrayType::Location::CallData) + if (type.getLocation() == ArrayType::Location::CallData) { // stack: target source_offset source_len m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5 @@ -79,7 +80,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } else { - solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Memory byte arrays not yet implemented."); + solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory byte arrays not yet implemented."); m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; // stack here: memory_offset storage_offset length_bytes // jump to end if length is zero @@ -163,16 +164,18 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari m_context << u256(length) << u256(0) << eth::Instruction::SHA3; } -void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType, - ByteArrayType const& _sourceType) const +void CompilerUtils::copyByteArrayToStorage( + ArrayType const& _targetType, ArrayType const& _sourceType) const { // stack layout: [source_ref] target_ref (top) // need to leave target_ref on the stack at the end - solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, ""); + solAssert(_targetType.getLocation() == ArrayType::Location::Storage, ""); + solAssert(_targetType.isByteArray(), "Non byte arrays not yet implemented here."); + solAssert(_sourceType.isByteArray(), "Non byte arrays not yet implemented here."); switch (_sourceType.getLocation()) { - case ByteArrayType::Location::CallData: + case ArrayType::Location::CallData: { // This also assumes that after "length" we only have zeros, i.e. it cannot be used to // slice a byte array from calldata. @@ -224,7 +227,7 @@ void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType, << eth::Instruction::POP << eth::Instruction::POP; break; } - case ByteArrayType::Location::Storage: + case ArrayType::Location::Storage: { // this copies source to target and also clears target if it was larger @@ -313,9 +316,10 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda return numBytes; } -void CompilerUtils::clearByteArray(ByteArrayType const& _type) const +void CompilerUtils::clearByteArray(ArrayType const& _type) const { - solAssert(_type.getLocation() == ByteArrayType::Location::Storage, ""); + solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); + solAssert(_type.isByteArray(), "Non byte arrays not yet implemented here."); // fetch length m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; diff --git a/CompilerUtils.h b/CompilerUtils.h index 5369d3bf..2fb97d80 100644 --- a/CompilerUtils.h +++ b/CompilerUtils.h @@ -82,11 +82,11 @@ public: /// Copies a byte array to a byte array in storage. /// Stack pre: [source_reference] target_reference /// Stack post: target_reference - void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; + void copyByteArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const; /// Clears the length and data elements of the byte array referenced on the stack. /// Stack pre: reference /// Stack post: - void clearByteArray(ByteArrayType const& _type) const; + void clearByteArray(ArrayType const& _type) const; /// Bytes we need to the start of call data. /// - The size in bytes of the function (hash) identifier. diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 461dfef1..cdad4840 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -530,20 +530,21 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) m_context << enumType->getMemberValue(_memberAccess.getMemberName()); break; } - case Type::Category::ByteArray: + case Type::Category::Array: { - solAssert(member == "length", "Illegal bytearray member."); - auto const& type = dynamic_cast<ByteArrayType const&>(*_memberAccess.getExpression().getType()); + solAssert(member == "length", "Illegal array member."); + auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.getExpression().getType()); + solAssert(type.isByteArray(), "Non byte arrays not yet implemented here."); switch (type.getLocation()) { - case ByteArrayType::Location::CallData: + case ArrayType::Location::CallData: m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; break; - case ByteArrayType::Location::Storage: + case ArrayType::Location::Storage: m_context << eth::Instruction::SLOAD; break; default: - solAssert(false, "Unsupported byte array location."); + solAssert(false, "Unsupported array location."); break; } break; @@ -1135,11 +1136,11 @@ void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location co else { solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "Wrong type conversation for assignment."); - if (m_dataType->getCategory() == Type::Category::ByteArray) + if (m_dataType->getCategory() == Type::Category::Array) { CompilerUtils(*m_context).copyByteArrayToStorage( - dynamic_cast<ByteArrayType const&>(*m_dataType), - dynamic_cast<ByteArrayType const&>(_sourceType)); + dynamic_cast<ArrayType const&>(*m_dataType), + dynamic_cast<ArrayType const&>(_sourceType)); if (_move) *m_context << eth::Instruction::POP; } @@ -1210,8 +1211,8 @@ void ExpressionCompiler::LValue::setToZero(Location const& _location) const break; } case LValueType::Storage: - if (m_dataType->getCategory() == Type::Category::ByteArray) - CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(*m_dataType)); + if (m_dataType->getCategory() == Type::Category::Array) + CompilerUtils(*m_context).clearByteArray(dynamic_cast<ArrayType const&>(*m_dataType)); else if (m_dataType->getCategory() == Type::Category::Struct) { // stack layout: ref diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h index 889c58b1..31bcc924 100644 --- a/ExpressionCompiler.h +++ b/ExpressionCompiler.h @@ -39,7 +39,7 @@ namespace solidity { class CompilerContext; class Type; class IntegerType; -class ByteArrayType; +class ArrayType; class StaticStringType; /** @@ -165,7 +165,7 @@ private: /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue void retrieveValueFromStorage(bool _remove = false) const; /// Copies from a byte array to a byte array in storage, both references on the stack. - void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; + void copyByteArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const; CompilerContext* m_context; LValueType m_type = LValueType::None; diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp index 15e1ac6f..f6ee2f1d 100644 --- a/NameAndTypeResolver.cpp +++ b/NameAndTypeResolver.cpp @@ -334,10 +334,10 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) if (_variable.getTypeName()) { TypePointer type = _variable.getTypeName()->toType(); - // All byte array parameter types should point to call data + // All array parameter types should point to call data if (_variable.isExternalFunctionParameter()) - if (auto const* byteArrayType = dynamic_cast<ByteArrayType const*>(type.get())) - type = byteArrayType->copyForLocation(ByteArrayType::Location::CallData); + if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get())) + type = arrayType->copyForLocation(ArrayType::Location::CallData); _variable.setType(type); if (!_variable.getType()) @@ -41,8 +41,11 @@ class Parser::ASTNodeFactory public: ASTNodeFactory(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {} + ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode): + m_parser(_parser), m_location(_childNode->getLocation()) {} void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + void setLocation(Location const& _location) { m_location = _location; } void setLocationEmpty() { m_location.end = m_location.start; } /// Set the end position to the one of the given node. void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; } @@ -299,12 +302,20 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition() return nodeFactory.createNode<EnumDefinition>(name, members); } -ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) +ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( + VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType) { - ASTNodeFactory nodeFactory(*this); - ASTPointer<TypeName> type = parseTypeName(_options.allowVar); - if (type != nullptr) - nodeFactory.setEndPositionFromNode(type); + ASTNodeFactory nodeFactory = _lookAheadArrayType ? + ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this); + ASTPointer<TypeName> type; + if (_lookAheadArrayType) + type = _lookAheadArrayType; + else + { + type = parseTypeName(_options.allowVar); + if (type != nullptr) + nodeFactory.setEndPositionFromNode(type); + } bool isIndexed = false; ASTPointer<ASTString> identifier; Token::Value token = m_scanner->getCurrentToken(); @@ -407,6 +418,7 @@ ASTPointer<Identifier> Parser::parseIdentifier() ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) { + ASTNodeFactory nodeFactory(*this); ASTPointer<TypeName> type; Token::Value token = m_scanner->getCurrentToken(); if (Token::isElementaryTypeName(token)) @@ -421,9 +433,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) m_scanner->next(); } else if (token == Token::Mapping) - { type = parseMapping(); - } else if (token == Token::Identifier) { ASTNodeFactory nodeFactory(*this); @@ -432,6 +442,18 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) } else BOOST_THROW_EXCEPTION(createParserError("Expected type name")); + + // Parse "[...]" postfixes for arrays. + while (m_scanner->getCurrentToken() == Token::LBrack) + { + m_scanner->next(); + ASTPointer<Expression> length; + if (m_scanner->getCurrentToken() != Token::RBrack) + length = parseExpression(); + nodeFactory.markEndPosition(); + expectToken(Token::RBrack); + type = nodeFactory.createNode<ArrayTypeName>(type, length); + } return type; } @@ -530,7 +552,7 @@ ASTPointer<Statement> Parser::parseStatement() } // fall-through default: - statement = parseVarDeclOrExprStmt(); + statement = parseSimpleStatement(); } expectToken(Token::Semicolon); return statement; @@ -579,7 +601,7 @@ ASTPointer<ForStatement> Parser::parseForStatement() // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen? if (m_scanner->getCurrentToken() != Token::Semicolon) - initExpression = parseVarDeclOrExprStmt(); + initExpression = parseSimpleStatement(); expectToken(Token::Semicolon); if (m_scanner->getCurrentToken() != Token::Semicolon) @@ -598,48 +620,88 @@ ASTPointer<ForStatement> Parser::parseForStatement() body); } -ASTPointer<Statement> Parser::parseVarDeclOrExprStmt() +ASTPointer<Statement> Parser::parseSimpleStatement() { - if (peekVariableDeclarationStatement()) + // These two cases are very hard to distinguish: + // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9; + // In the first case, x is a type name, in the second it is the name of a variable. + int isVariableDeclaration = peekVariableDeclarationStatement(); + if (isVariableDeclaration == 1) return parseVariableDeclarationStatement(); - else + else if (isVariableDeclaration == -1) return parseExpressionStatement(); + + // At this point, we have '(Identifier|ElementaryTypeName) "["'. + // We parse '(Identifier|ElementaryTypeName) ( "[" Expression "]" )+' and then decide whether to hand this over + // to ExpressionStatement or create a VariableDeclarationStatement out of it. + ASTPointer<PrimaryExpression> primary; + if (m_scanner->getCurrentToken() == Token::Identifier) + primary = parseIdentifier(); + else + { + primary = ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->getCurrentToken()); + m_scanner->next(); + } + vector<pair<ASTPointer<Expression>, Location>> indices; + solAssert(m_scanner->getCurrentToken() == Token::LBrack, ""); + Location indexLocation = primary->getLocation(); + bool encounteredEmptyBrackets = false; + do + { + expectToken(Token::LBrack); + ASTPointer<Expression> index; + if (m_scanner->getCurrentToken() == Token::RBrack) + encounteredEmptyBrackets = true; + else + index = parseExpression(); + indexLocation.end = getEndPosition(); + indices.push_back(make_pair(index, indexLocation)); + expectToken(Token::RBrack); + } + while (m_scanner->getCurrentToken() == Token::LBrack); + + if (m_scanner->getCurrentToken() == Token::Identifier || encounteredEmptyBrackets) + return parseVariableDeclarationStatement(typeNameFromArrayIndexStructure(primary, indices)); + else + return parseExpressionStatement(expressionFromArrayIndexStructure(primary, indices)); } -ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement() +ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement( + ASTPointer<TypeName> const& _lookAheadArrayType) { - ASTNodeFactory nodeFactory(*this); VarDeclParserOptions options; options.allowVar = true; options.allowInitialValue = true; - ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options); + ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType); + ASTNodeFactory nodeFactory(*this, variable); return nodeFactory.createNode<VariableDeclarationStatement>(variable); } -ASTPointer<ExpressionStatement> Parser::parseExpressionStatement() +ASTPointer<ExpressionStatement> Parser::parseExpressionStatement( + ASTPointer<Expression> const& _lookAheadArrayExpression) { - ASTNodeFactory nodeFactory(*this); - ASTPointer<Expression> expression = parseExpression(); - nodeFactory.setEndPositionFromNode(expression); - return nodeFactory.createNode<ExpressionStatement>(expression); + ASTPointer<Expression> expression = parseExpression(_lookAheadArrayExpression); + return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(expression); } -ASTPointer<Expression> Parser::parseExpression() +ASTPointer<Expression> Parser::parseExpression( + ASTPointer<Expression> const& _lookAheadArrayExpression) { - ASTNodeFactory nodeFactory(*this); - ASTPointer<Expression> expression = parseBinaryExpression(); + ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadArrayExpression); if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) return expression; Token::Value assignmentOperator = expectAssignmentOperator(); ASTPointer<Expression> rightHandSide = parseExpression(); + ASTNodeFactory nodeFactory(*this, expression); nodeFactory.setEndPositionFromNode(rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); } -ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence) +ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence, + ASTPointer<Expression> const& _lookAheadArrayExpression) { - ASTNodeFactory nodeFactory(*this); - ASTPointer<Expression> expression = parseUnaryExpression(); + ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadArrayExpression); + ASTNodeFactory nodeFactory(*this, expression); int precedence = Token::precedence(m_scanner->getCurrentToken()); for (; precedence >= _minPrecedence; --precedence) while (Token::precedence(m_scanner->getCurrentToken()) == precedence) @@ -653,11 +715,13 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence) return expression; } -ASTPointer<Expression> Parser::parseUnaryExpression() +ASTPointer<Expression> Parser::parseUnaryExpression( + ASTPointer<Expression> const& _lookAheadArrayExpression) { - ASTNodeFactory nodeFactory(*this); + ASTNodeFactory nodeFactory = _lookAheadArrayExpression ? + ASTNodeFactory(*this, _lookAheadArrayExpression) : ASTNodeFactory(*this); Token::Value token = m_scanner->getCurrentToken(); - if (Token::isUnaryOp(token) || Token::isCountOp(token)) + if (!_lookAheadArrayExpression && (Token::isUnaryOp(token) || Token::isCountOp(token))) { // prefix expression m_scanner->next(); @@ -668,7 +732,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression() else { // potential postfix expression - ASTPointer<Expression> subExpression = parseLeftHandSideExpression(); + ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadArrayExpression); token = m_scanner->getCurrentToken(); if (!Token::isCountOp(token)) return subExpression; @@ -678,11 +742,16 @@ ASTPointer<Expression> Parser::parseUnaryExpression() } } -ASTPointer<Expression> Parser::parseLeftHandSideExpression() +ASTPointer<Expression> Parser::parseLeftHandSideExpression( + ASTPointer<Expression> const& _lookAheadArrayExpression) { - ASTNodeFactory nodeFactory(*this); + ASTNodeFactory nodeFactory = _lookAheadArrayExpression ? + ASTNodeFactory(*this, _lookAheadArrayExpression) : ASTNodeFactory(*this); + ASTPointer<Expression> expression; - if (m_scanner->getCurrentToken() == Token::New) + if (_lookAheadArrayExpression) + expression = _lookAheadArrayExpression; + else if (m_scanner->getCurrentToken() == Token::New) { expectToken(Token::New); ASTPointer<Identifier> contractName(parseIdentifier()); @@ -774,10 +843,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() m_scanner->next(); } else - { BOOST_THROW_EXCEPTION(createParserError("Expected primary expression.")); - return ASTPointer<Expression>(); // this is not reached - } break; } return expression; @@ -824,18 +890,55 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars return ret; } +int Parser::peekVariableDeclarationStatement() const +{ + // Distinguish between variable declaration (and potentially assignment) and expression statement + // (which include assignments to other expressions and pre-declared variables). + // We have a variable declaration if we get a keyword that specifies a type name. + // If it is an identifier or an elementary type name followed by an identifier, we also have + // a variable declaration. + // If we get an identifier followed by a "[", it can be both ("type[9] a;" or "arr[9] = 7;"). + // In all other cases, we have an expression statement. + Token::Value token(m_scanner->getCurrentToken()); + bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); + if (token == Token::Mapping || token == Token::Var || + (mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier)) + return 1; + if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack) + return 0; + return -1; +} -bool Parser::peekVariableDeclarationStatement() +ASTPointer<TypeName> Parser::typeNameFromArrayIndexStructure( + ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices) { - // distinguish between variable declaration (and potentially assignment) and expression statement - // (which include assignments to other expressions and pre-declared variables) - // We have a variable declaration if we get a keyword that specifies a type name, or - // in the case of a user-defined type, we have two identifiers following each other. - return (m_scanner->getCurrentToken() == Token::Mapping || - m_scanner->getCurrentToken() == Token::Var || - ((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || - m_scanner->getCurrentToken() == Token::Identifier) && - m_scanner->peekNextToken() == Token::Identifier)); + ASTNodeFactory nodeFactory(*this, _primary); + ASTPointer<TypeName> type; + if (auto identifier = dynamic_cast<Identifier const*>(_primary.get())) + type = nodeFactory.createNode<UserDefinedTypeName>(make_shared<ASTString>(identifier->getName())); + else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get())) + type = nodeFactory.createNode<ElementaryTypeName>(typeName->getTypeToken()); + else + solAssert(false, "Invalid type name for array look-ahead."); + for (auto const& lengthExpression: _indices) + { + nodeFactory.setLocation(lengthExpression.second); + type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first); + } + return type; +} + +ASTPointer<Expression> Parser::expressionFromArrayIndexStructure( + ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices) +{ + ASTNodeFactory nodeFactory(*this, _primary); + ASTPointer<Expression> expression(_primary); + for (auto const& index: _indices) + { + nodeFactory.setLocation(index.second); + expression = nodeFactory.createNode<IndexAccess>(expression, index.first); + } + return expression; } void Parser::expectToken(Token::Value _value) @@ -64,7 +64,9 @@ private: ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumValue> parseEnumValue(); - ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); + ASTPointer<VariableDeclaration> parseVariableDeclaration( + VarDeclParserOptions const& _options = VarDeclParserOptions(), + ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<EventDefinition> parseEventDefinition(); ASTPointer<ModifierInvocation> parseModifierInvocation(); @@ -77,13 +79,20 @@ private: ASTPointer<IfStatement> parseIfStatement(); ASTPointer<WhileStatement> parseWhileStatement(); ASTPointer<ForStatement> parseForStatement(); - ASTPointer<Statement> parseVarDeclOrExprStmt(); - ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(); - ASTPointer<ExpressionStatement> parseExpressionStatement(); - ASTPointer<Expression> parseExpression(); - ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4); - ASTPointer<Expression> parseUnaryExpression(); - ASTPointer<Expression> parseLeftHandSideExpression(); + /// A "simple statement" can be a variable declaration statement or an expression statement. + ASTPointer<Statement> parseSimpleStatement(); + ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement( + ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); + ASTPointer<ExpressionStatement> parseExpressionStatement( + ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>()); + ASTPointer<Expression> parseExpression( + ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>()); + ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4, + ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>()); + ASTPointer<Expression> parseUnaryExpression( + ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>()); + ASTPointer<Expression> parseLeftHandSideExpression( + ASTPointer<Expression> const& _lookAheadArrayExpression = ASTPointer<Expression>()); ASTPointer<Expression> parsePrimaryExpression(); std::vector<ASTPointer<Expression>> parseFunctionCallListArguments(); std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments(); @@ -92,9 +101,18 @@ private: ///@{ ///@name Helper functions - /// Peeks ahead in the scanner to determine if a variable declaration statement is going to follow - bool peekVariableDeclarationStatement(); - + /// Performs limited look-ahead to distinguish between variable declaration and expression statement. + /// @returns 1 if it is a variable declaration, -1 if it is an expression statement and 0 if + /// it might be an array-typed variable declaration or an index access to an existing variable. + int peekVariableDeclarationStatement() const; + /// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]". + ASTPointer<TypeName> typeNameFromArrayIndexStructure( + ASTPointer<PrimaryExpression> const& _primary, + std::vector<std::pair<ASTPointer<Expression>, Location>> const& _indices); + /// Returns an expression parsed in look-ahead fashion from something like "a[8][2**70]". + ASTPointer<Expression> expressionFromArrayIndexStructure( + ASTPointer<PrimaryExpression> const& _primary, + std::vector<std::pair<ASTPointer<Expression>, Location>> const& _indices); /// If current token value is not _value, throw exception otherwise advance token. void expectToken(Token::Value _value); Token::Value expectAssignmentOperator(); @@ -58,7 +58,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken) else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0)); else if (_typeToken == Token::Bytes) - return make_shared<ByteArrayType>(ByteArrayType::Location::Storage); + return make_shared<ArrayType>(ArrayType::Location::Storage); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + std::string(Token::toString(_typeToken)) + " to type.")); @@ -83,17 +83,35 @@ TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) return TypePointer(); } -TypePointer Type::fromMapping(Mapping const& _typeName) +TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType) { - TypePointer keyType = _typeName.getKeyType().toType(); + TypePointer keyType = _keyType.toType(); if (!keyType) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); - TypePointer valueType = _typeName.getValueType().toType(); + TypePointer valueType = _valueType.toType(); if (!valueType) - BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); + BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name.")); return make_shared<MappingType>(keyType, valueType); } +TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length) +{ + TypePointer baseType = _baseTypeName.toType(); + if (!baseType) + BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name.")); + if (_length) + { + if (!_length->getType()) + _length->checkTypeRequirements(); + auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get()); + if (!length) + BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length.")); + return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr)); + } + else + return make_shared<ArrayType>(ArrayType::Location::Storage, baseType); +} + TypePointer Type::forLiteral(Literal const& _literal) { switch (_literal.getToken()) @@ -517,27 +535,27 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); } -bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const +bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const { return _convertTo.getCategory() == getCategory(); } -TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const +TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const { if (_operator == Token::Delete) return make_shared<VoidType>(); return TypePointer(); } -bool ByteArrayType::operator==(Type const& _other) const +bool ArrayType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) return false; - ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other); + ArrayType const& other = dynamic_cast<ArrayType const&>(_other); return other.m_location == m_location; } -unsigned ByteArrayType::getSizeOnStack() const +unsigned ArrayType::getSizeOnStack() const { if (m_location == Location::CallData) // offset, length (stack top) @@ -547,12 +565,30 @@ unsigned ByteArrayType::getSizeOnStack() const return 1; } -shared_ptr<ByteArrayType> ByteArrayType::copyForLocation(ByteArrayType::Location _location) const +string ArrayType::toString() const { - return make_shared<ByteArrayType>(_location); + if (isByteArray()) + return "bytes"; + string ret = getBaseType()->toString() + "["; + if (!isDynamicallySized()) + ret += getLength().str(); + return ret + "]"; +} + +shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const +{ + auto copy = make_shared<ArrayType>(_location); + copy->m_isByteArray = m_isByteArray; + if (m_baseType->getCategory() == Type::Category::Array) + copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location); + else + copy->m_baseType = m_baseType; + copy->m_hasDynamicLength = m_hasDynamicLength; + copy->m_length = m_length; + return copy; } -const MemberList ByteArrayType::s_byteArrayMemberList = MemberList({{"length", make_shared<IntegerType>(256)}}); +const MemberList ArrayType::s_arrayTypeMemberList = MemberList({{"length", make_shared<IntegerType>(256)}}); bool ContractType::operator==(Type const& _other) const { @@ -1033,7 +1069,7 @@ MagicType::MagicType(MagicType::Kind _kind): m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, {"gas", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)}, - {"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}}); + {"data", make_shared<ArrayType>(ArrayType::Location::CallData)}}); break; case Kind::Transaction: m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, @@ -36,8 +36,6 @@ namespace dev namespace solidity { -// @todo realMxN, dynamic strings, text, arrays - class Type; // forward class FunctionType; // forward using TypePointer = std::shared_ptr<Type const>; @@ -78,7 +76,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type public: enum class Category { - Integer, IntegerConstant, Bool, Real, ByteArray, + Integer, IntegerConstant, Bool, Real, Array, String, Contract, Struct, Function, Enum, Mapping, Void, TypeType, Modifier, Magic }; @@ -89,8 +87,8 @@ public: static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(std::string const& _name); static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); - static TypePointer fromMapping(Mapping const& _typeName); - static TypePointer fromFunction(FunctionDefinition const& _function); + static TypePointer fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType); + static TypePointer fromArrayTypeName(TypeName& _baseTypeName, Expression* _length); /// @} /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does @@ -283,30 +281,47 @@ public: /** * The type of a byte array, prototype for a general array. */ -class ByteArrayType: public Type +class ArrayType: public Type { public: enum class Location { Storage, CallData, Memory }; - virtual Category getCategory() const override { return Category::ByteArray; } - explicit ByteArrayType(Location _location): m_location(_location) {} + virtual Category getCategory() const override { return Category::Array; } + + /// Constructor for a byte array ("bytes") + explicit ArrayType(Location _location): + m_location(_location), m_isByteArray(true), m_baseType(std::make_shared<IntegerType>(8)) {} + /// Constructor for a dynamically sized array type ("type[]") + ArrayType(Location _location, const TypePointer &_baseType): + m_location(_location), m_baseType(_baseType) {} + /// Constructor for a fixed-size array type ("type[20]") + ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length): + m_location(_location), m_baseType(_baseType), m_hasDynamicLength(false), m_length(_length) {} + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(const Type& _other) const override; - virtual bool isDynamicallySized() const { return true; } + virtual bool isDynamicallySized() const { return m_hasDynamicLength; } virtual unsigned getSizeOnStack() const override; - virtual std::string toString() const override { return "bytes"; } - virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; } + virtual std::string toString() const override; + virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; } Location getLocation() const { return m_location; } + bool isByteArray() const { return m_isByteArray; } + TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;} + u256 const& getLength() const { return m_length; } /// @returns a copy of this type with location changed to @a _location /// @todo this might move as far up as Type later - std::shared_ptr<ByteArrayType> copyForLocation(Location _location) const; + std::shared_ptr<ArrayType> copyForLocation(Location _location) const; private: Location m_location; - static const MemberList s_byteArrayMemberList; + bool m_isByteArray = false; ///< Byte arrays ("bytes") have different semantics from ordinary arrays. + TypePointer m_baseType; + bool m_hasDynamicLength = true; + u256 m_length; + static const MemberList s_arrayTypeMemberList; }; /** |