Browse Source
Conflicts: libevm/ExtVMFace.h libevm/VM.h test/vm.cpp test/vm.h windows/LibEthereum.vcxproj.filterscl-refactor
Paweł Bylica
10 years ago
134 changed files with 8633 additions and 970 deletions
@ -1,9 +1,12 @@ |
|||||
#pragma once |
#pragma once |
||||
|
|
||||
#include "Common.h" |
#include "Common.h" |
||||
|
#include "CryptoPP.h" |
||||
|
#include "EC.h" |
||||
#include "FileSystem.h" |
#include "FileSystem.h" |
||||
#include "MemoryDB.h" |
#include "MemoryDB.h" |
||||
#include "OverlayDB.h" |
#include "OverlayDB.h" |
||||
#include "SHA3.h" |
#include "SHA3.h" |
||||
|
#include "SHA3MAC.h" |
||||
#include "TrieCommon.h" |
#include "TrieCommon.h" |
||||
#include "TrieDB.h" |
#include "TrieDB.h" |
||||
|
@ -1,43 +0,0 @@ |
|||||
/*
|
|
||||
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 <http://www.gnu.org/licenses/>.
|
|
||||
*/ |
|
||||
/** @file CryptoHeaders.h
|
|
||||
* @author Tim Hughes <tim@twistedfury.com> |
|
||||
* @date 2014 |
|
||||
*/ |
|
||||
#pragma once |
|
||||
|
|
||||
// need to leave this one disabled
|
|
||||
#pragma GCC diagnostic ignored "-Wunused-function" |
|
||||
|
|
||||
#pragma warning(push) |
|
||||
#pragma warning(disable:4100 4244) |
|
||||
#pragma GCC diagnostic push |
|
||||
#pragma GCC diagnostic ignored "-Wconversion" |
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter" |
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable" |
|
||||
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" |
|
||||
#pragma GCC diagnostic ignored "-Wextra" |
|
||||
#include <sha.h> |
|
||||
#include <sha3.h> |
|
||||
#include <ripemd.h> |
|
||||
#include <aes.h> |
|
||||
#include <pwdbased.h> |
|
||||
#include <modes.h> |
|
||||
#include <filters.h> |
|
||||
#include <secp256k1/secp256k1.h> |
|
||||
#pragma warning(pop) |
|
||||
#pragma GCC diagnostic pop |
|
@ -0,0 +1,74 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file CryptoPP.cpp
|
||||
|
* @author Alex Leverington <nessence@gmail.com> |
||||
|
* @date 2014 |
||||
|
*/ |
||||
|
|
||||
|
#include "CryptoPP.h" |
||||
|
|
||||
|
using namespace dev; |
||||
|
using namespace dev::crypto; |
||||
|
using namespace CryptoPP; |
||||
|
|
||||
|
ECP::Point pp::PointFromPublic(Public const& _p) |
||||
|
{ |
||||
|
ECP::Point p; |
||||
|
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pub; |
||||
|
pub.AccessGroupParameters().Initialize(pp::secp256k1()); |
||||
|
|
||||
|
bytes prefixedKey(pub.GetGroupParameters().GetEncodedElementSize(true)); |
||||
|
prefixedKey[0] = 0x04; |
||||
|
assert(Public::size == prefixedKey.size() - 1); |
||||
|
memcpy(&prefixedKey[1], _p.data(), prefixedKey.size() - 1); |
||||
|
|
||||
|
pub.GetGroupParameters().GetCurve().DecodePoint(p, prefixedKey.data(), prefixedKey.size()); |
||||
|
return std::move(p); |
||||
|
} |
||||
|
|
||||
|
Integer pp::ExponentFromSecret(Secret const& _s) |
||||
|
{ |
||||
|
static_assert(Secret::size == 32, "Secret key must be 32 bytes."); |
||||
|
return std::move(Integer(_s.data(), Secret::size)); |
||||
|
} |
||||
|
|
||||
|
void pp::PublicFromExponent(Integer const& _e, Public& _p) |
||||
|
{ |
||||
|
CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> k; |
||||
|
k.AccessGroupParameters().Initialize(secp256k1()); |
||||
|
k.SetPrivateExponent(_e); |
||||
|
|
||||
|
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> p; |
||||
|
p.AccessGroupParameters().Initialize(secp256k1()); |
||||
|
k.MakePublicKey(p); |
||||
|
pp::PublicFromDL_PublicKey_EC(p, _p); |
||||
|
} |
||||
|
|
||||
|
void pp::PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p) |
||||
|
{ |
||||
|
bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true)); |
||||
|
_k.GetGroupParameters().GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false); |
||||
|
|
||||
|
static_assert(Public::size == 64, "Public key must be 64 bytes."); |
||||
|
assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true)); |
||||
|
memcpy(_p.data(), &prefixedKey[1], Public::size); |
||||
|
} |
||||
|
|
||||
|
void pp::SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s) |
||||
|
{ |
||||
|
_k.GetPrivateExponent().Encode(_s.data(), Secret::size); |
||||
|
} |
@ -0,0 +1,85 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file CryptoPP.h
|
||||
|
* @author Alex Leverington <nessence@gmail.com> |
||||
|
* @date 2014 |
||||
|
* |
||||
|
* CryptoPP headers and helper methods |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
// need to leave this one disabled for link-time. blame cryptopp.
|
||||
|
#pragma GCC diagnostic ignored "-Wunused-function" |
||||
|
#pragma warning(push) |
||||
|
#pragma warning(disable:4100 4244) |
||||
|
#pragma GCC diagnostic push |
||||
|
#pragma GCC diagnostic ignored "-Wconversion" |
||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter" |
||||
|
#pragma GCC diagnostic ignored "-Wunused-variable" |
||||
|
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" |
||||
|
#pragma GCC diagnostic ignored "-Wextra" |
||||
|
#include <sha.h> |
||||
|
#include <sha3.h> |
||||
|
#include <ripemd.h> |
||||
|
#include <aes.h> |
||||
|
#include <pwdbased.h> |
||||
|
#include <modes.h> |
||||
|
#include <filters.h> |
||||
|
#include <eccrypto.h> |
||||
|
#include <ecp.h> |
||||
|
#include <files.h> |
||||
|
#include <osrng.h> |
||||
|
#include <oids.h> |
||||
|
#include <secp256k1/secp256k1.h> |
||||
|
#pragma warning(pop) |
||||
|
#pragma GCC diagnostic pop |
||||
|
#include "Common.h" |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace crypto |
||||
|
{ |
||||
|
|
||||
|
namespace pp |
||||
|
{ |
||||
|
|
||||
|
/// RNG used by CryptoPP
|
||||
|
inline CryptoPP::AutoSeededRandomPool& PRNG() { static CryptoPP::AutoSeededRandomPool prng; return prng; } |
||||
|
|
||||
|
/// EC curve used by CryptoPP
|
||||
|
inline CryptoPP::OID const& secp256k1() { static CryptoPP::OID curve = CryptoPP::ASN1::secp256k1(); return curve; } |
||||
|
|
||||
|
/// Conversion from bytes to cryptopp point
|
||||
|
CryptoPP::ECP::Point PointFromPublic(Public const& _p); |
||||
|
|
||||
|
/// Conversion from bytes to cryptopp exponent
|
||||
|
CryptoPP::Integer ExponentFromSecret(Secret const& _s); |
||||
|
|
||||
|
/// Conversion from cryptopp exponent Integer to bytes
|
||||
|
void PublicFromExponent(CryptoPP::Integer const& _k, Public& _s); |
||||
|
|
||||
|
/// Conversion from cryptopp public key to bytes
|
||||
|
void PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p); |
||||
|
|
||||
|
/// Conversion from cryptopp private key to bytes
|
||||
|
void SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,77 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file EC.cpp
|
||||
|
* @author Alex Leverington <nessence@gmail.com> |
||||
|
* @date 2014 |
||||
|
* |
||||
|
* Shared EC classes and functions. |
||||
|
*/ |
||||
|
|
||||
|
#pragma warning(push) |
||||
|
#pragma warning(disable:4100 4244) |
||||
|
#pragma GCC diagnostic push |
||||
|
#pragma GCC diagnostic ignored "-Wconversion" |
||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter" |
||||
|
#pragma GCC diagnostic ignored "-Wunused-variable" |
||||
|
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" |
||||
|
#pragma GCC diagnostic ignored "-Wextra" |
||||
|
#include <files.h> |
||||
|
#pragma warning(pop) |
||||
|
#pragma GCC diagnostic pop |
||||
|
#include "CryptoPP.h" |
||||
|
#include "SHA3.h" |
||||
|
#include "EC.h" |
||||
|
|
||||
|
// CryptoPP and dev conflict so dev and pp namespace are used explicitly
|
||||
|
using namespace std; |
||||
|
using namespace dev; |
||||
|
using namespace dev::crypto; |
||||
|
using namespace CryptoPP; |
||||
|
|
||||
|
void dev::crypto::encrypt(Public const& _key, bytes& io_cipher) |
||||
|
{ |
||||
|
ECIES<ECP>::Encryptor e; |
||||
|
e.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1()); |
||||
|
e.AccessKey().SetPublicElement(pp::PointFromPublic(_key)); |
||||
|
size_t plen = io_cipher.size(); |
||||
|
bytes c; |
||||
|
c.resize(e.CiphertextLength(plen)); |
||||
|
// todo: use StringSource with _plain as input and output.
|
||||
|
e.Encrypt(pp::PRNG(), io_cipher.data(), plen, c.data()); |
||||
|
memset(io_cipher.data(), 0, io_cipher.size()); |
||||
|
io_cipher = std::move(c); |
||||
|
} |
||||
|
|
||||
|
void dev::crypto::decrypt(Secret const& _k, bytes& io_text) |
||||
|
{ |
||||
|
CryptoPP::ECIES<CryptoPP::ECP>::Decryptor d; |
||||
|
d.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1()); |
||||
|
d.AccessKey().SetPrivateExponent(pp::ExponentFromSecret(_k)); |
||||
|
size_t clen = io_text.size(); |
||||
|
bytes p; |
||||
|
p.resize(d.MaxPlaintextLength(io_text.size())); |
||||
|
// todo: use StringSource with _c as input and output.
|
||||
|
DecodingResult r = d.Decrypt(pp::PRNG(), io_text.data(), clen, p.data()); |
||||
|
if (!r.isValidCoding) |
||||
|
{ |
||||
|
io_text.clear(); |
||||
|
return; |
||||
|
} |
||||
|
io_text.resize(r.messageLength); |
||||
|
io_text = std::move(p); |
||||
|
} |
||||
|
|
@ -0,0 +1,41 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file EC.h
|
||||
|
* @author Alex Leverington <nessence@gmail.com> |
||||
|
* @date 2014 |
||||
|
* |
||||
|
* Shared EC classes and functions. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "Common.h" |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace crypto |
||||
|
{ |
||||
|
|
||||
|
/// Encrypts text (in place).
|
||||
|
void encrypt(Public const& _k, bytes& io_cipher); |
||||
|
|
||||
|
/// Decrypts text (in place).
|
||||
|
void decrypt(Secret const& _k, bytes& io_text); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,39 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file SHA3MAC.cpp
|
||||
|
* @author Alex Leverington <nessence@gmail.com> |
||||
|
* @date 2014 |
||||
|
* |
||||
|
* SHA3 MAC |
||||
|
*/ |
||||
|
|
||||
|
#include "CryptoPP.h" |
||||
|
#include "SHA3MAC.h" |
||||
|
|
||||
|
using namespace dev; |
||||
|
using namespace dev::crypto; |
||||
|
using namespace CryptoPP; |
||||
|
|
||||
|
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output) |
||||
|
{ |
||||
|
CryptoPP::SHA3_256 ctx; |
||||
|
ctx.Update((byte*)_secret.data(), _secret.size()); |
||||
|
ctx.Update((byte*)_plain.data(), _plain.size()); |
||||
|
assert(_output.size() >= 32); |
||||
|
ctx.Final(_output.data()); |
||||
|
} |
||||
|
|
@ -0,0 +1,38 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file SHA3MAC.h
|
||||
|
* @author Alex Leverington <nessence@gmail.com> |
||||
|
* @date 2014 |
||||
|
* |
||||
|
* SHA3 MAC |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <libdevcore/Common.h> |
||||
|
#include <libdevcore/FixedHash.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace crypto |
||||
|
{ |
||||
|
|
||||
|
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,481 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity abstract syntax tree. |
||||
|
*/ |
||||
|
|
||||
|
#include <algorithm> |
||||
|
|
||||
|
#include <libsolidity/AST.h> |
||||
|
#include <libsolidity/ASTVisitor.h> |
||||
|
#include <libsolidity/Exceptions.h> |
||||
|
|
||||
|
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_typeName) |
||||
|
m_typeName->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 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 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); |
||||
|
} |
||||
|
|
||||
|
TypeError ASTNode::createTypeError(std::string const& _description) |
||||
|
{ |
||||
|
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); |
||||
|
} |
||||
|
|
||||
|
void Statement::expectType(Expression& _expression, const Type& _expectedType) |
||||
|
{ |
||||
|
_expression.checkTypeRequirements(); |
||||
|
if (!_expression.getType()->isImplicitlyConvertibleTo(_expectedType)) |
||||
|
BOOST_THROW_EXCEPTION(_expression.createTypeError("Type not implicitly convertible to expected type.")); |
||||
|
//@todo provide more information to the exception
|
||||
|
} |
||||
|
|
||||
|
void Block::checkTypeRequirements() |
||||
|
{ |
||||
|
for (std::shared_ptr<Statement> const& statement: m_statements) |
||||
|
statement->checkTypeRequirements(); |
||||
|
} |
||||
|
|
||||
|
void IfStatement::checkTypeRequirements() |
||||
|
{ |
||||
|
expectType(*m_condition, BoolType()); |
||||
|
m_trueBody->checkTypeRequirements(); |
||||
|
if (m_falseBody) |
||||
|
m_falseBody->checkTypeRequirements(); |
||||
|
} |
||||
|
|
||||
|
void WhileStatement::checkTypeRequirements() |
||||
|
{ |
||||
|
expectType(*m_condition, BoolType()); |
||||
|
m_body->checkTypeRequirements(); |
||||
|
} |
||||
|
|
||||
|
void Continue::checkTypeRequirements() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
void Break::checkTypeRequirements() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
void Return::checkTypeRequirements() |
||||
|
{ |
||||
|
BOOST_ASSERT(m_returnParameters); |
||||
|
if (m_returnParameters->getParameters().size() != 1) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("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
|
||||
|
expectType(*m_expression, *m_returnParameters->getParameters().front()->getType()); |
||||
|
} |
||||
|
|
||||
|
void 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_value->checkTypeRequirements(); |
||||
|
m_variable->setType(m_value->getType()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Assignment::checkTypeRequirements() |
||||
|
{ |
||||
|
//@todo lefthandside actually has to be assignable
|
||||
|
// add a feature to the type system to check that
|
||||
|
m_leftHandSide->checkTypeRequirements(); |
||||
|
expectType(*m_rightHandSide, *m_leftHandSide->getType()); |
||||
|
m_type = m_leftHandSide->getType(); |
||||
|
if (m_assigmentOperator != Token::ASSIGN) |
||||
|
{ |
||||
|
// complex assignment
|
||||
|
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void UnaryOperation::checkTypeRequirements() |
||||
|
{ |
||||
|
// INC, DEC, NOT, BIT_NOT, DELETE
|
||||
|
m_subExpression->checkTypeRequirements(); |
||||
|
m_type = m_subExpression->getType(); |
||||
|
if (m_type->acceptsUnaryOperator(m_operator)) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); |
||||
|
} |
||||
|
|
||||
|
void 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(createTypeError("No common type found in binary operation.")); |
||||
|
if (Token::isCompareOp(m_operator)) |
||||
|
m_type = std::make_shared<BoolType>(); |
||||
|
else |
||||
|
{ |
||||
|
BOOST_ASSERT(Token::isBinaryOp(m_operator)); |
||||
|
m_type = m_commonType; |
||||
|
if (!m_commonType->acceptsBinaryOperator(m_operator)) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("Operator not compatible with type.")); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void FunctionCall::checkTypeRequirements() |
||||
|
{ |
||||
|
m_expression->checkTypeRequirements(); |
||||
|
for (ASTPointer<Expression> const& argument: m_arguments) |
||||
|
argument->checkTypeRequirements(); |
||||
|
Type const& expressionType = *m_expression->getType(); |
||||
|
Type::Category const category = expressionType.getCategory(); |
||||
|
if (category == Type::Category::TYPE) |
||||
|
{ |
||||
|
TypeType const* type = dynamic_cast<TypeType const*>(&expressionType); |
||||
|
BOOST_ASSERT(type); |
||||
|
//@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) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for " |
||||
|
"explicit type conersion.")); |
||||
|
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType())) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("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
|
||||
|
// and then ask if that is implicitly convertible to the struct represented by the
|
||||
|
// function parameters
|
||||
|
FunctionType const* function = dynamic_cast<FunctionType const*>(&expressionType); |
||||
|
BOOST_ASSERT(function); |
||||
|
FunctionDefinition const& fun = function->getFunction(); |
||||
|
std::vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters(); |
||||
|
if (parameters.size() != m_arguments.size()) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("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(createTypeError("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<VoidType>(); |
||||
|
else |
||||
|
m_type = fun.getReturnParameterList()->getParameters().front()->getType(); |
||||
|
} |
||||
|
else |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("Type does not support invocation.")); |
||||
|
} |
||||
|
|
||||
|
void MemberAccess::checkTypeRequirements() |
||||
|
{ |
||||
|
BOOST_ASSERT(false); // not yet implemented
|
||||
|
// m_type = ;
|
||||
|
} |
||||
|
|
||||
|
void IndexAccess::checkTypeRequirements() |
||||
|
{ |
||||
|
BOOST_ASSERT(false); // not yet implemented
|
||||
|
// m_type = ;
|
||||
|
} |
||||
|
|
||||
|
void Identifier::checkTypeRequirements() |
||||
|
{ |
||||
|
BOOST_ASSERT(m_referencedDeclaration); |
||||
|
//@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<VariableDeclaration*>(m_referencedDeclaration); |
||||
|
if (variable) |
||||
|
{ |
||||
|
if (!variable->getType()) |
||||
|
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type " |
||||
|
"could be determined.")); |
||||
|
m_type = variable->getType(); |
||||
|
return; |
||||
|
} |
||||
|
//@todo can we unify these with TypeName::toType()?
|
||||
|
StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration); |
||||
|
if (structDef) |
||||
|
{ |
||||
|
// note that we do not have a struct type here
|
||||
|
m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef)); |
||||
|
return; |
||||
|
} |
||||
|
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration); |
||||
|
if (functionDef) |
||||
|
{ |
||||
|
// 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<FunctionType>(*functionDef); |
||||
|
return; |
||||
|
} |
||||
|
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration); |
||||
|
if (contractDef) |
||||
|
{ |
||||
|
m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef)); |
||||
|
return; |
||||
|
} |
||||
|
BOOST_ASSERT(false); // declaration reference of unknown/forbidden type
|
||||
|
} |
||||
|
|
||||
|
void ElementaryTypeNameExpression::checkTypeRequirements() |
||||
|
{ |
||||
|
m_type = std::make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); |
||||
|
} |
||||
|
|
||||
|
void Literal::checkTypeRequirements() |
||||
|
{ |
||||
|
m_type = Type::forLiteral(*this); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,537 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity abstract syntax tree. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
|
||||
|
#include <string> |
||||
|
#include <vector> |
||||
|
#include <memory> |
||||
|
#include <boost/noncopyable.hpp> |
||||
|
#include <libsolidity/ASTForward.h> |
||||
|
#include <libsolidity/BaseTypes.h> |
||||
|
#include <libsolidity/Token.h> |
||||
|
#include <libsolidity/Types.h> |
||||
|
#include <libsolidity/Exceptions.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
class ASTVisitor; |
||||
|
|
||||
|
class ASTNode: private boost::noncopyable |
||||
|
{ |
||||
|
public: |
||||
|
explicit ASTNode(Location const& _location): m_location(_location) {} |
||||
|
|
||||
|
virtual ~ASTNode() {} |
||||
|
|
||||
|
virtual void accept(ASTVisitor& _visitor) = 0; |
||||
|
template <class T> |
||||
|
static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor) |
||||
|
{ |
||||
|
for (ASTPointer<T>& element: _list) |
||||
|
element->accept(_visitor); |
||||
|
} |
||||
|
|
||||
|
Location const& getLocation() const { return m_location; } |
||||
|
|
||||
|
/// Creates a @ref TypeError exception and decorates it with the location of the node and
|
||||
|
/// the given description
|
||||
|
TypeError createTypeError(std::string const& _description); |
||||
|
|
||||
|
private: |
||||
|
Location m_location; |
||||
|
}; |
||||
|
|
||||
|
class Declaration: public ASTNode |
||||
|
{ |
||||
|
public: |
||||
|
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): |
||||
|
ASTNode(_location), m_name(_name) {} |
||||
|
|
||||
|
const ASTString& getName() const { return *m_name; } |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<ASTString> m_name; |
||||
|
}; |
||||
|
|
||||
|
class ContractDefinition: public Declaration |
||||
|
{ |
||||
|
public: |
||||
|
ContractDefinition(Location const& _location, |
||||
|
ASTPointer<ASTString> const& _name, |
||||
|
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, |
||||
|
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, |
||||
|
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions): |
||||
|
Declaration(_location, _name), |
||||
|
m_definedStructs(_definedStructs), |
||||
|
m_stateVariables(_stateVariables), |
||||
|
m_definedFunctions(_definedFunctions) |
||||
|
{} |
||||
|
|
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
|
||||
|
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() { return m_definedStructs; } |
||||
|
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() { return m_stateVariables; } |
||||
|
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() { return m_definedFunctions; } |
||||
|
|
||||
|
private: |
||||
|
std::vector<ASTPointer<StructDefinition>> m_definedStructs; |
||||
|
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; |
||||
|
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; |
||||
|
}; |
||||
|
|
||||
|
class StructDefinition: public Declaration |
||||
|
{ |
||||
|
public: |
||||
|
StructDefinition(Location const& _location, |
||||
|
ASTPointer<ASTString> const& _name, |
||||
|
std::vector<ASTPointer<VariableDeclaration>> const& _members): |
||||
|
Declaration(_location, _name), m_members(_members) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
|
||||
|
private: |
||||
|
std::vector<ASTPointer<VariableDeclaration>> 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, |
||||
|
std::vector<ASTPointer<VariableDeclaration>> const& _parameters): |
||||
|
ASTNode(_location), m_parameters(_parameters) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
|
||||
|
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() { return m_parameters; } |
||||
|
|
||||
|
private: |
||||
|
std::vector<ASTPointer<VariableDeclaration>> m_parameters; |
||||
|
}; |
||||
|
|
||||
|
class FunctionDefinition: public Declaration |
||||
|
{ |
||||
|
public: |
||||
|
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, bool _isPublic, |
||||
|
ASTPointer<ParameterList> const& _parameters, |
||||
|
bool _isDeclaredConst, |
||||
|
ASTPointer<ParameterList> const& _returnParameters, |
||||
|
ASTPointer<Block> 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; } |
||||
|
bool isDeclaredConst() const { return m_isDeclaredConst; } |
||||
|
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); } |
||||
|
ParameterList& getParameterList() { return *m_parameters; } |
||||
|
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } |
||||
|
Block& getBody() { return *m_body; } |
||||
|
|
||||
|
private: |
||||
|
bool m_isPublic; |
||||
|
ASTPointer<ParameterList> m_parameters; |
||||
|
bool m_isDeclaredConst; |
||||
|
ASTPointer<ParameterList> m_returnParameters; |
||||
|
ASTPointer<Block> m_body; |
||||
|
}; |
||||
|
|
||||
|
class VariableDeclaration: public Declaration |
||||
|
{ |
||||
|
public: |
||||
|
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type, |
||||
|
ASTPointer<ASTString> const& _name): |
||||
|
Declaration(_location, _name), m_typeName(_type) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
|
||||
|
bool isTypeGivenExplicitly() const { return bool(m_typeName); } |
||||
|
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.
|
||||
|
std::shared_ptr<Type const> const& getType() const { return m_type; } |
||||
|
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
|
||||
|
|
||||
|
std::shared_ptr<Type const> m_type; |
||||
|
}; |
||||
|
|
||||
|
/// types
|
||||
|
/// @{
|
||||
|
|
||||
|
class TypeName: public ASTNode |
||||
|
{ |
||||
|
public: |
||||
|
explicit TypeName(Location const& _location): ASTNode(_location) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
|
||||
|
virtual std::shared_ptr<Type> toType() = 0; |
||||
|
}; |
||||
|
|
||||
|
/// 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) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual std::shared_ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); } |
||||
|
|
||||
|
Token::Value getType() const { return m_type; } |
||||
|
|
||||
|
private: |
||||
|
Token::Value m_type; |
||||
|
}; |
||||
|
|
||||
|
class UserDefinedTypeName: public TypeName |
||||
|
{ |
||||
|
public: |
||||
|
UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name): |
||||
|
TypeName(_location), m_name(_name) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual std::shared_ptr<Type> 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: |
||||
|
ASTPointer<ASTString> m_name; |
||||
|
|
||||
|
StructDefinition* m_referencedStruct; |
||||
|
}; |
||||
|
|
||||
|
class Mapping: public TypeName |
||||
|
{ |
||||
|
public: |
||||
|
Mapping(Location const& _location, ASTPointer<ElementaryTypeName> const& _keyType, |
||||
|
ASTPointer<TypeName> const& _valueType): |
||||
|
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual std::shared_ptr<Type> toType() override { return Type::fromMapping(*this); } |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<ElementaryTypeName> m_keyType; |
||||
|
ASTPointer<TypeName> m_valueType; |
||||
|
}; |
||||
|
|
||||
|
/// @}
|
||||
|
|
||||
|
/// Statements
|
||||
|
/// @{
|
||||
|
|
||||
|
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 void 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 |
||||
|
{ |
||||
|
public: |
||||
|
Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements): |
||||
|
Statement(_location), m_statements(_statements) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
|
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
private: |
||||
|
std::vector<ASTPointer<Statement>> m_statements; |
||||
|
}; |
||||
|
|
||||
|
class IfStatement: public Statement |
||||
|
{ |
||||
|
public: |
||||
|
IfStatement(Location const& _location, ASTPointer<Expression> const& _condition, |
||||
|
ASTPointer<Statement> const& _trueBody, ASTPointer<Statement> const& _falseBody): |
||||
|
Statement(_location), |
||||
|
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_condition; |
||||
|
ASTPointer<Statement> m_trueBody; |
||||
|
ASTPointer<Statement> 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 |
||||
|
{ |
||||
|
public: |
||||
|
WhileStatement(Location const& _location, ASTPointer<Expression> const& _condition, |
||||
|
ASTPointer<Statement> const& _body): |
||||
|
BreakableStatement(_location), m_condition(_condition), m_body(_body) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_condition; |
||||
|
ASTPointer<Statement> m_body; |
||||
|
}; |
||||
|
|
||||
|
class Continue: public Statement |
||||
|
{ |
||||
|
public: |
||||
|
Continue(Location const& _location): Statement(_location) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
}; |
||||
|
|
||||
|
class Break: public Statement |
||||
|
{ |
||||
|
public: |
||||
|
Break(Location const& _location): Statement(_location) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
}; |
||||
|
|
||||
|
class Return: public Statement |
||||
|
{ |
||||
|
public: |
||||
|
Return(Location const& _location, ASTPointer<Expression> _expression): |
||||
|
Statement(_location), m_expression(_expression) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_expression; //< value to return, optional
|
||||
|
|
||||
|
ParameterList* m_returnParameters; //< extracted from the function declaration
|
||||
|
}; |
||||
|
|
||||
|
class VariableDefinition: public Statement |
||||
|
{ |
||||
|
public: |
||||
|
VariableDefinition(Location const& _location, ASTPointer<VariableDeclaration> _variable, |
||||
|
ASTPointer<Expression> _value): |
||||
|
Statement(_location), m_variable(_variable), m_value(_value) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<VariableDeclaration> m_variable; |
||||
|
ASTPointer<Expression> m_value; ///< can be missing
|
||||
|
}; |
||||
|
|
||||
|
class Expression: public Statement |
||||
|
{ |
||||
|
public: |
||||
|
Expression(Location const& _location): Statement(_location) {} |
||||
|
std::shared_ptr<Type const> const& getType() const { return m_type; } |
||||
|
|
||||
|
protected: |
||||
|
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
|
||||
|
std::shared_ptr<Type const> m_type; |
||||
|
}; |
||||
|
|
||||
|
/// @}
|
||||
|
|
||||
|
/// Expressions
|
||||
|
/// @{
|
||||
|
|
||||
|
class Assignment: public Expression |
||||
|
{ |
||||
|
public: |
||||
|
Assignment(Location const& _location, ASTPointer<Expression> const& _leftHandSide, |
||||
|
Token::Value _assignmentOperator, ASTPointer<Expression> const& _rightHandSide): |
||||
|
Expression(_location), m_leftHandSide(_leftHandSide), |
||||
|
m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
Token::Value getAssignmentOperator() const { return m_assigmentOperator; } |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_leftHandSide; |
||||
|
Token::Value m_assigmentOperator; |
||||
|
ASTPointer<Expression> m_rightHandSide; |
||||
|
}; |
||||
|
|
||||
|
class UnaryOperation: public Expression |
||||
|
{ |
||||
|
public: |
||||
|
UnaryOperation(Location const& _location, Token::Value _operator, |
||||
|
ASTPointer<Expression> 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; |
||||
|
|
||||
|
Token::Value getOperator() const { return m_operator; } |
||||
|
bool isPrefixOperation() const { return m_isPrefix; } |
||||
|
|
||||
|
private: |
||||
|
Token::Value m_operator; |
||||
|
ASTPointer<Expression> m_subExpression; |
||||
|
bool m_isPrefix; |
||||
|
}; |
||||
|
|
||||
|
class BinaryOperation: public Expression |
||||
|
{ |
||||
|
public: |
||||
|
BinaryOperation(Location const& _location, ASTPointer<Expression> const& _left, |
||||
|
Token::Value _operator, ASTPointer<Expression> const& _right): |
||||
|
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
Token::Value getOperator() const { return m_operator; } |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_left; |
||||
|
Token::Value m_operator; |
||||
|
ASTPointer<Expression> m_right; |
||||
|
|
||||
|
std::shared_ptr<Type const> m_commonType; |
||||
|
}; |
||||
|
|
||||
|
/// Can be ordinary function call, type cast or struct construction.
|
||||
|
class FunctionCall: public Expression |
||||
|
{ |
||||
|
public: |
||||
|
FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression, |
||||
|
std::vector<ASTPointer<Expression>> const& _arguments): |
||||
|
Expression(_location), m_expression(_expression), m_arguments(_arguments) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_expression; |
||||
|
std::vector<ASTPointer<Expression>> m_arguments; |
||||
|
}; |
||||
|
|
||||
|
class MemberAccess: public Expression |
||||
|
{ |
||||
|
public: |
||||
|
MemberAccess(Location const& _location, ASTPointer<Expression> _expression, |
||||
|
ASTPointer<ASTString> 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; |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_expression; |
||||
|
ASTPointer<ASTString> m_memberName; |
||||
|
}; |
||||
|
|
||||
|
class IndexAccess: public Expression |
||||
|
{ |
||||
|
public: |
||||
|
IndexAccess(Location const& _location, ASTPointer<Expression> const& _base, |
||||
|
ASTPointer<Expression> const& _index): |
||||
|
Expression(_location), m_base(_base), m_index(_index) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
private: |
||||
|
ASTPointer<Expression> m_base; |
||||
|
ASTPointer<Expression> m_index; |
||||
|
}; |
||||
|
|
||||
|
class PrimaryExpression: public Expression |
||||
|
{ |
||||
|
public: |
||||
|
PrimaryExpression(Location const& _location): Expression(_location) {} |
||||
|
}; |
||||
|
|
||||
|
class Identifier: public PrimaryExpression |
||||
|
{ |
||||
|
public: |
||||
|
Identifier(Location const& _location, ASTPointer<ASTString> const& _name): |
||||
|
PrimaryExpression(_location), m_name(_name) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) 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: |
||||
|
ASTPointer<ASTString> m_name; |
||||
|
|
||||
|
//! Declaration the name refers to.
|
||||
|
Declaration* m_referencedDeclaration; |
||||
|
}; |
||||
|
|
||||
|
class ElementaryTypeNameExpression: public PrimaryExpression |
||||
|
{ |
||||
|
public: |
||||
|
ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken): |
||||
|
PrimaryExpression(_location), m_typeToken(_typeToken) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
Token::Value getTypeToken() const { return m_typeToken; } |
||||
|
|
||||
|
private: |
||||
|
Token::Value m_typeToken; |
||||
|
}; |
||||
|
|
||||
|
class Literal: public PrimaryExpression |
||||
|
{ |
||||
|
public: |
||||
|
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value): |
||||
|
PrimaryExpression(_location), m_token(_token), m_value(_value) {} |
||||
|
virtual void accept(ASTVisitor& _visitor) override; |
||||
|
virtual void checkTypeRequirements() override; |
||||
|
|
||||
|
Token::Value getToken() const { return m_token; } |
||||
|
ASTString const& getValue() const { return *m_value; } |
||||
|
|
||||
|
private: |
||||
|
Token::Value m_token; |
||||
|
ASTPointer<ASTString> m_value; |
||||
|
}; |
||||
|
|
||||
|
/// @}
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,78 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Forward-declarations of AST classes. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <string> |
||||
|
#include <memory> |
||||
|
#include <vector> |
||||
|
|
||||
|
// Forward-declare all AST node types
|
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
class ASTNode; |
||||
|
class Declaration; |
||||
|
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 Identifier; |
||||
|
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 <class T> |
||||
|
using ASTPointer = std::shared_ptr<T>; |
||||
|
|
||||
|
using ASTString = std::string; |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,443 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. |
||||
|
*/ |
||||
|
|
||||
|
#include <libsolidity/ASTPrinter.h> |
||||
|
#include <libsolidity/AST.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, std::string const& _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::toString(_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"); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(Assignment& _node) |
||||
|
{ |
||||
|
writeLine(std::string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(UnaryOperation& _node) |
||||
|
{ |
||||
|
writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + |
||||
|
") " + Token::toString(_node.getOperator())); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(BinaryOperation& _node) |
||||
|
{ |
||||
|
writeLine(std::string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(FunctionCall& _node) |
||||
|
{ |
||||
|
writeLine("FunctionCall"); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(MemberAccess& _node) |
||||
|
{ |
||||
|
writeLine("MemberAccess to member " + _node.getMemberName()); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(IndexAccess& _node) |
||||
|
{ |
||||
|
writeLine("IndexAccess"); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(PrimaryExpression& _node) |
||||
|
{ |
||||
|
writeLine("PrimaryExpression"); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(Identifier& _node) |
||||
|
{ |
||||
|
writeLine(std::string("Identifier ") + _node.getName()); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) |
||||
|
{ |
||||
|
writeLine(std::string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
bool ASTPrinter::visit(Literal& _node) |
||||
|
{ |
||||
|
char const* tokenString = Token::toString(_node.getToken()); |
||||
|
if (!tokenString) |
||||
|
tokenString = "[no token]"; |
||||
|
writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); |
||||
|
printType(_node); |
||||
|
printSourcePart(_node); |
||||
|
return goDeeper(); |
||||
|
} |
||||
|
|
||||
|
void ASTPrinter::endVisit(ASTNode&) |
||||
|
{ |
||||
|
m_indentation--; |
||||
|
} |
||||
|
|
||||
|
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(Identifier&) |
||||
|
{ |
||||
|
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) << "|" << std::endl; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void ASTPrinter::printType(Expression const& _expression) |
||||
|
{ |
||||
|
if (_expression.getType()) |
||||
|
*m_ostream << getIndentation() << " Type: " << _expression.getType()->toString() << "\n"; |
||||
|
else |
||||
|
*m_ostream << getIndentation() << " Type unknown.\n"; |
||||
|
} |
||||
|
|
||||
|
std::string ASTPrinter::getIndentation() const |
||||
|
{ |
||||
|
return std::string(m_indentation * 2, ' '); |
||||
|
} |
||||
|
|
||||
|
void ASTPrinter::writeLine(std::string const& _line) |
||||
|
{ |
||||
|
*m_ostream << getIndentation() << _line << std::endl; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,117 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <ostream> |
||||
|
#include <libsolidity/ASTVisitor.h> |
||||
|
|
||||
|
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(ASTPointer<ASTNode> const& _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) 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) 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); |
||||
|
void printType(Expression const& _expression); |
||||
|
std::string getIndentation() const; |
||||
|
void writeLine(std::string const& _line); |
||||
|
bool goDeeper() { m_indentation++; return true; } |
||||
|
|
||||
|
int m_indentation; |
||||
|
std::string m_source; |
||||
|
ASTPointer<ASTNode> m_ast; |
||||
|
std::ostream* m_ostream; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,104 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* AST visitor base class. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <libsolidity/ASTForward.h> |
||||
|
#include <string> |
||||
|
|
||||
|
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(Identifier&) { 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(Identifier&) { } |
||||
|
virtual void endVisit(ElementaryTypeNameExpression&) { } |
||||
|
virtual void endVisit(Literal&) { } |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Some elementary types for the parser. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <ostream> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
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) { } |
||||
|
|
||||
|
bool IsValid() const { return start >= 0 && end >= start; } |
||||
|
|
||||
|
int start; |
||||
|
int end; |
||||
|
}; |
||||
|
|
||||
|
/// Stream output for Location (used e.g. in boost exceptions).
|
||||
|
inline std::ostream& operator<<(std::ostream& _out, Location const& _location) |
||||
|
{ |
||||
|
return _out << "[" << _location.start << "," << _location.end << ")"; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
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} devcore) |
||||
|
target_link_libraries(${EXECUTABLE} evmface) |
||||
|
|
||||
|
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) |
||||
|
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) |
||||
|
|
@ -0,0 +1,42 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity exception hierarchy. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <string> |
||||
|
#include <libdevcore/Exceptions.h> |
||||
|
#include <libsolidity/BaseTypes.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
struct ParserError: virtual Exception {}; |
||||
|
struct TypeError: virtual Exception {}; |
||||
|
struct DeclarationError: virtual Exception {}; |
||||
|
|
||||
|
typedef boost::error_info<struct tag_sourcePosition, int> errinfo_sourcePosition; |
||||
|
typedef boost::error_info<struct tag_sourceLocation, Location> errinfo_sourceLocation; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,202 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Parser part that determines the declarations corresponding to names and the types of expressions. |
||||
|
*/ |
||||
|
|
||||
|
#include <libsolidity/NameAndTypeResolver.h> |
||||
|
|
||||
|
#include <libsolidity/AST.h> |
||||
|
#include <libsolidity/Exceptions.h> |
||||
|
#include <boost/assert.hpp> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
|
||||
|
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) |
||||
|
{ |
||||
|
reset(); |
||||
|
DeclarationRegistrationHelper registrar(m_scopes, _contract); |
||||
|
m_currentScope = &m_scopes[&_contract]; |
||||
|
//@todo structs
|
||||
|
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) |
||||
|
ReferencesResolver resolver(*variable, *this, nullptr); |
||||
|
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) |
||||
|
{ |
||||
|
m_currentScope = &m_scopes[function.get()]; |
||||
|
ReferencesResolver referencesResolver(*function, *this, |
||||
|
function->getReturnParameterList().get()); |
||||
|
} |
||||
|
// 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 (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) |
||||
|
{ |
||||
|
m_currentScope = &m_scopes[function.get()]; |
||||
|
function->getBody().checkTypeRequirements(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void NameAndTypeResolver::reset() |
||||
|
{ |
||||
|
m_scopes.clear(); |
||||
|
m_currentScope = nullptr; |
||||
|
} |
||||
|
|
||||
|
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) |
||||
|
{ |
||||
|
return m_currentScope->resolveName(_name, _recursive); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _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&) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node) |
||||
|
{ |
||||
|
std::map<ASTNode*, Scope>::iterator iter; |
||||
|
bool newlyAdded; |
||||
|
std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope)); |
||||
|
BOOST_ASSERT(newlyAdded); |
||||
|
m_currentScope = &iter->second; |
||||
|
} |
||||
|
|
||||
|
void DeclarationRegistrationHelper::closeCurrentScope() |
||||
|
{ |
||||
|
BOOST_ASSERT(m_currentScope); |
||||
|
m_currentScope = m_currentScope->getOuterScope(); |
||||
|
} |
||||
|
|
||||
|
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) |
||||
|
{ |
||||
|
BOOST_ASSERT(m_currentScope); |
||||
|
if (!m_currentScope->registerDeclaration(_declaration)) |
||||
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_declaration.getLocation()) |
||||
|
<< errinfo_comment("Identifier already declared.")); |
||||
|
//@todo the exception should also contain the location of the first declaration
|
||||
|
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()) |
||||
|
_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); |
||||
|
_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) |
||||
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) |
||||
|
<< errinfo_comment("Undeclared identifier.")); |
||||
|
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration); |
||||
|
//@todo later, contracts are also valid types
|
||||
|
if (!referencedStruct) |
||||
|
BOOST_THROW_EXCEPTION(_typeName.createTypeError("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) |
||||
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) |
||||
|
<< errinfo_comment("Undeclared identifier.")); |
||||
|
_identifier.setReferencedDeclaration(*declaration); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,100 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Parser part that determines the declarations corresponding to names and the types of expressions. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <map> |
||||
|
#include <boost/noncopyable.hpp> |
||||
|
|
||||
|
#include <libsolidity/Scope.h> |
||||
|
#include <libsolidity/ASTVisitor.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
//! 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() {} |
||||
|
|
||||
|
void resolveNamesAndTypes(ContractDefinition& _contract); |
||||
|
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); |
||||
|
|
||||
|
private: |
||||
|
void reset(); |
||||
|
|
||||
|
//! 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<ASTNode*, Scope> m_scopes; |
||||
|
|
||||
|
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<ASTNode*, Scope>& _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); |
||||
|
|
||||
|
std::map<ASTNode*, Scope>& 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; |
||||
|
|
||||
|
NameAndTypeResolver& m_resolver; |
||||
|
ParameterList* m_returnParameters; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,544 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity parser. |
||||
|
*/ |
||||
|
|
||||
|
#include <libdevcore/Log.h> |
||||
|
#include <libsolidity/BaseTypes.h> |
||||
|
#include <libsolidity/Parser.h> |
||||
|
#include <libsolidity/Scanner.h> |
||||
|
#include <libsolidity/Exceptions.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
ASTPointer<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _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(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(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; } |
||||
|
|
||||
|
template <class NodeType, typename... Args> |
||||
|
ASTPointer<NodeType> createNode(Args&& ... _args) |
||||
|
{ |
||||
|
if (m_location.end < 0) |
||||
|
markEndPosition(); |
||||
|
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
Parser const& m_parser; |
||||
|
Location m_location; |
||||
|
}; |
||||
|
|
||||
|
int Parser::getPosition() const |
||||
|
{ |
||||
|
return m_scanner->getCurrentLocation().start; |
||||
|
} |
||||
|
|
||||
|
int Parser::getEndPosition() const |
||||
|
{ |
||||
|
return m_scanner->getCurrentLocation().end; |
||||
|
} |
||||
|
|
||||
|
ASTPointer<ContractDefinition> Parser::parseContractDefinition() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
expectToken(Token::CONTRACT); |
||||
|
ASTPointer<ASTString> name = expectIdentifierToken(); |
||||
|
expectToken(Token::LBRACE); |
||||
|
std::vector<ASTPointer<StructDefinition>> structs; |
||||
|
std::vector<ASTPointer<VariableDeclaration>> stateVariables; |
||||
|
std::vector<ASTPointer<FunctionDefinition>> 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)) |
||||
|
{ |
||||
|
bool const allowVar = false; |
||||
|
stateVariables.push_back(parseVariableDeclaration(allowVar)); |
||||
|
expectToken(Token::SEMICOLON); |
||||
|
} |
||||
|
else |
||||
|
BOOST_THROW_EXCEPTION(createParserError("Function, variable or struct declaration expected.")); |
||||
|
} |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expectToken(Token::RBRACE); |
||||
|
expectToken(Token::EOS); |
||||
|
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
expectToken(Token::FUNCTION); |
||||
|
ASTPointer<ASTString> name(expectIdentifierToken()); |
||||
|
ASTPointer<ParameterList> parameters(parseParameterList()); |
||||
|
bool isDeclaredConst = false; |
||||
|
if (m_scanner->getCurrentToken() == Token::CONST) |
||||
|
{ |
||||
|
isDeclaredConst = true; |
||||
|
m_scanner->next(); |
||||
|
} |
||||
|
ASTPointer<ParameterList> returnParameters; |
||||
|
if (m_scanner->getCurrentToken() == Token::RETURNS) |
||||
|
{ |
||||
|
bool const 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<ParameterList>(std::vector<ASTPointer<VariableDeclaration>>()); |
||||
|
} |
||||
|
ASTPointer<Block> block = parseBlock(); |
||||
|
nodeFactory.setEndPositionFromNode(block); |
||||
|
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters, |
||||
|
isDeclaredConst, returnParameters, block); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<StructDefinition> Parser::parseStructDefinition() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
expectToken(Token::STRUCT); |
||||
|
ASTPointer<ASTString> name = expectIdentifierToken(); |
||||
|
std::vector<ASTPointer<VariableDeclaration>> members; |
||||
|
expectToken(Token::LBRACE); |
||||
|
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<StructDefinition>(name, members); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar) |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
ASTPointer<TypeName> type = parseTypeName(_allowVar); |
||||
|
nodeFactory.markEndPosition(); |
||||
|
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken()); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) |
||||
|
{ |
||||
|
ASTPointer<TypeName> type; |
||||
|
Token::Value token = m_scanner->getCurrentToken(); |
||||
|
if (Token::isElementaryTypeName(token)) |
||||
|
{ |
||||
|
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token); |
||||
|
m_scanner->next(); |
||||
|
} |
||||
|
else if (token == Token::VAR) |
||||
|
{ |
||||
|
if (!_allowVar) |
||||
|
BOOST_THROW_EXCEPTION(createParserError("Expected explicit type name.")); |
||||
|
m_scanner->next(); |
||||
|
} |
||||
|
else if (token == Token::MAPPING) |
||||
|
{ |
||||
|
type = parseMapping(); |
||||
|
} |
||||
|
else if (token == Token::IDENTIFIER) |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
nodeFactory.markEndPosition(); |
||||
|
type = nodeFactory.createNode<UserDefinedTypeName>(expectIdentifierToken()); |
||||
|
} |
||||
|
else |
||||
|
BOOST_THROW_EXCEPTION(createParserError("Expected type name")); |
||||
|
return type; |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Mapping> Parser::parseMapping() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
expectToken(Token::MAPPING); |
||||
|
expectToken(Token::LPAREN); |
||||
|
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken())) |
||||
|
BOOST_THROW_EXCEPTION(createParserError("Expected elementary type name for mapping key type")); |
||||
|
ASTPointer<ElementaryTypeName> keyType; |
||||
|
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken()); |
||||
|
m_scanner->next(); |
||||
|
expectToken(Token::ARROW); |
||||
|
bool const allowVar = false; |
||||
|
ASTPointer<TypeName> valueType = parseTypeName(allowVar); |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expectToken(Token::RPAREN); |
||||
|
return nodeFactory.createNode<Mapping>(keyType, valueType); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty) |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
std::vector<ASTPointer<VariableDeclaration>> parameters; |
||||
|
expectToken(Token::LPAREN); |
||||
|
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(allowVar)); |
||||
|
} |
||||
|
} |
||||
|
nodeFactory.markEndPosition(); |
||||
|
m_scanner->next(); |
||||
|
return nodeFactory.createNode<ParameterList>(parameters); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Block> Parser::parseBlock() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
expectToken(Token::LBRACE); |
||||
|
std::vector<ASTPointer<Statement>> statements; |
||||
|
while (m_scanner->getCurrentToken() != Token::RBRACE) |
||||
|
statements.push_back(parseStatement()); |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expectToken(Token::RBRACE); |
||||
|
return nodeFactory.createNode<Block>(statements); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Statement> Parser::parseStatement() |
||||
|
{ |
||||
|
ASTPointer<Statement> statement; |
||||
|
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: |
||||
|
statement = ASTNodeFactory(*this).createNode<Continue>(); |
||||
|
break; |
||||
|
case Token::BREAK: |
||||
|
statement = ASTNodeFactory(*this).createNode<Break>(); |
||||
|
break; |
||||
|
case Token::RETURN: |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
ASTPointer<Expression> expression; |
||||
|
if (m_scanner->next() != Token::SEMICOLON) |
||||
|
{ |
||||
|
expression = parseExpression(); |
||||
|
nodeFactory.setEndPositionFromNode(expression); |
||||
|
} |
||||
|
statement = nodeFactory.createNode<Return>(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->peekNextToken() == Token::IDENTIFIER)) |
||||
|
statement = parseVariableDefinition(); |
||||
|
else // "ordinary" expression
|
||||
|
statement = parseExpression(); |
||||
|
} |
||||
|
expectToken(Token::SEMICOLON); |
||||
|
return statement; |
||||
|
} |
||||
|
|
||||
|
ASTPointer<IfStatement> Parser::parseIfStatement() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
expectToken(Token::IF); |
||||
|
expectToken(Token::LPAREN); |
||||
|
ASTPointer<Expression> condition = parseExpression(); |
||||
|
expectToken(Token::RPAREN); |
||||
|
ASTPointer<Statement> trueBody = parseStatement(); |
||||
|
ASTPointer<Statement> falseBody; |
||||
|
if (m_scanner->getCurrentToken() == Token::ELSE) |
||||
|
{ |
||||
|
m_scanner->next(); |
||||
|
falseBody = parseStatement(); |
||||
|
nodeFactory.setEndPositionFromNode(falseBody); |
||||
|
} |
||||
|
else |
||||
|
nodeFactory.setEndPositionFromNode(trueBody); |
||||
|
return nodeFactory.createNode<IfStatement>(condition, trueBody, falseBody); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<WhileStatement> Parser::parseWhileStatement() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
expectToken(Token::WHILE); |
||||
|
expectToken(Token::LPAREN); |
||||
|
ASTPointer<Expression> condition = parseExpression(); |
||||
|
expectToken(Token::RPAREN); |
||||
|
ASTPointer<Statement> body = parseStatement(); |
||||
|
nodeFactory.setEndPositionFromNode(body); |
||||
|
return nodeFactory.createNode<WhileStatement>(condition, body); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<VariableDefinition> Parser::parseVariableDefinition() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
bool const allowVar = true; |
||||
|
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar); |
||||
|
ASTPointer<Expression> value; |
||||
|
if (m_scanner->getCurrentToken() == Token::ASSIGN) |
||||
|
{ |
||||
|
m_scanner->next(); |
||||
|
value = parseExpression(); |
||||
|
nodeFactory.setEndPositionFromNode(value); |
||||
|
} |
||||
|
else |
||||
|
nodeFactory.setEndPositionFromNode(variable); |
||||
|
return nodeFactory.createNode<VariableDefinition>(variable, value); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Expression> Parser::parseExpression() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
ASTPointer<Expression> expression = parseBinaryExpression(); |
||||
|
if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) |
||||
|
return expression; |
||||
|
Token::Value assignmentOperator = expectAssignmentOperator(); |
||||
|
ASTPointer<Expression> rightHandSide = parseExpression(); |
||||
|
nodeFactory.setEndPositionFromNode(rightHandSide); |
||||
|
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence) |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
ASTPointer<Expression> 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(); |
||||
|
ASTPointer<Expression> right = parseBinaryExpression(precedence + 1); |
||||
|
nodeFactory.setEndPositionFromNode(right); |
||||
|
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right); |
||||
|
} |
||||
|
} |
||||
|
return expression; |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Expression> Parser::parseUnaryExpression() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
Token::Value token = m_scanner->getCurrentToken(); |
||||
|
if (Token::isUnaryOp(token) || Token::isCountOp(token)) |
||||
|
{ |
||||
|
// prefix expression
|
||||
|
m_scanner->next(); |
||||
|
ASTPointer<Expression> subExpression = parseUnaryExpression(); |
||||
|
nodeFactory.setEndPositionFromNode(subExpression); |
||||
|
return nodeFactory.createNode<UnaryOperation>(token, subExpression, true); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// potential postfix expression
|
||||
|
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(); |
||||
|
token = m_scanner->getCurrentToken(); |
||||
|
if (!Token::isCountOp(token)) |
||||
|
return subExpression; |
||||
|
nodeFactory.markEndPosition(); |
||||
|
m_scanner->next(); |
||||
|
return nodeFactory.createNode<UnaryOperation>(token, subExpression, false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Expression> Parser::parseLeftHandSideExpression() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
ASTPointer<Expression> expression = parsePrimaryExpression(); |
||||
|
while (true) |
||||
|
{ |
||||
|
switch (m_scanner->getCurrentToken()) |
||||
|
{ |
||||
|
case Token::LBRACK: |
||||
|
{ |
||||
|
m_scanner->next(); |
||||
|
ASTPointer<Expression> index = parseExpression(); |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expectToken(Token::RBRACK); |
||||
|
expression = nodeFactory.createNode<IndexAccess>(expression, index); |
||||
|
} |
||||
|
break; |
||||
|
case Token::PERIOD: |
||||
|
{ |
||||
|
m_scanner->next(); |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken()); |
||||
|
} |
||||
|
break; |
||||
|
case Token::LPAREN: |
||||
|
{ |
||||
|
m_scanner->next(); |
||||
|
std::vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expectToken(Token::RPAREN); |
||||
|
expression = nodeFactory.createNode<FunctionCall>(expression, arguments); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
return expression; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ASTPointer<Expression> Parser::parsePrimaryExpression() |
||||
|
{ |
||||
|
ASTNodeFactory nodeFactory(*this); |
||||
|
Token::Value token = m_scanner->getCurrentToken(); |
||||
|
ASTPointer<Expression> expression; |
||||
|
switch (token) |
||||
|
{ |
||||
|
case Token::TRUE_LITERAL: |
||||
|
case Token::FALSE_LITERAL: |
||||
|
expression = nodeFactory.createNode<Literal>(token, ASTPointer<ASTString>()); |
||||
|
m_scanner->next(); |
||||
|
break; |
||||
|
case Token::NUMBER: |
||||
|
case Token::STRING_LITERAL: |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); |
||||
|
break; |
||||
|
case Token::IDENTIFIER: |
||||
|
nodeFactory.markEndPosition(); |
||||
|
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); |
||||
|
break; |
||||
|
case Token::LPAREN: |
||||
|
{ |
||||
|
m_scanner->next(); |
||||
|
ASTPointer<Expression> expression = parseExpression(); |
||||
|
expectToken(Token::RPAREN); |
||||
|
return expression; |
||||
|
} |
||||
|
default: |
||||
|
if (Token::isElementaryTypeName(token)) |
||||
|
{ |
||||
|
// used for casts
|
||||
|
expression = nodeFactory.createNode<ElementaryTypeNameExpression>(token); |
||||
|
m_scanner->next(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression.")); |
||||
|
return ASTPointer<Expression>(); // this is not reached
|
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
return expression; |
||||
|
} |
||||
|
|
||||
|
std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments() |
||||
|
{ |
||||
|
std::vector<ASTPointer<Expression>> 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) |
||||
|
BOOST_THROW_EXCEPTION(createParserError(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)) |
||||
|
BOOST_THROW_EXCEPTION(createParserError("Expected assignment operator")); |
||||
|
m_scanner->next(); |
||||
|
return op; |
||||
|
} |
||||
|
|
||||
|
ASTPointer<ASTString> Parser::expectIdentifierToken() |
||||
|
{ |
||||
|
if (m_scanner->getCurrentToken() != Token::IDENTIFIER) |
||||
|
BOOST_THROW_EXCEPTION(createParserError("Expected identifier")); |
||||
|
return getLiteralAndAdvance(); |
||||
|
} |
||||
|
|
||||
|
ASTPointer<ASTString> Parser::getLiteralAndAdvance() |
||||
|
{ |
||||
|
ASTPointer<ASTString> identifier = std::make_shared<ASTString>(m_scanner->getCurrentLiteral()); |
||||
|
m_scanner->next(); |
||||
|
return identifier; |
||||
|
} |
||||
|
|
||||
|
ParserError Parser::createParserError(std::string const& _description) const |
||||
|
{ |
||||
|
return ParserError() << errinfo_sourcePosition(getPosition()) << errinfo_comment(_description); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,86 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity parser. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "libsolidity/AST.h" |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
class Scanner; |
||||
|
|
||||
|
class Parser |
||||
|
{ |
||||
|
public: |
||||
|
ASTPointer<ContractDefinition> parse(std::shared_ptr<Scanner> const& _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
|
||||
|
/// @{
|
||||
|
ASTPointer<ContractDefinition> parseContractDefinition(); |
||||
|
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic); |
||||
|
ASTPointer<StructDefinition> parseStructDefinition(); |
||||
|
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar); |
||||
|
ASTPointer<TypeName> parseTypeName(bool _allowVar); |
||||
|
ASTPointer<Mapping> parseMapping(); |
||||
|
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true); |
||||
|
ASTPointer<Block> parseBlock(); |
||||
|
ASTPointer<Statement> parseStatement(); |
||||
|
ASTPointer<IfStatement> parseIfStatement(); |
||||
|
ASTPointer<WhileStatement> parseWhileStatement(); |
||||
|
ASTPointer<VariableDefinition> parseVariableDefinition(); |
||||
|
ASTPointer<Expression> parseExpression(); |
||||
|
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4); |
||||
|
ASTPointer<Expression> parseUnaryExpression(); |
||||
|
ASTPointer<Expression> parseLeftHandSideExpression(); |
||||
|
ASTPointer<Expression> parsePrimaryExpression(); |
||||
|
std::vector<ASTPointer<Expression>> parseFunctionCallArguments(); |
||||
|
/// @}
|
||||
|
|
||||
|
/// Helper functions
|
||||
|
/// @{
|
||||
|
/// If current token value is not _value, throw exception otherwise advance token.
|
||||
|
void expectToken(Token::Value _value); |
||||
|
Token::Value expectAssignmentOperator(); |
||||
|
ASTPointer<ASTString> expectIdentifierToken(); |
||||
|
ASTPointer<ASTString> getLiteralAndAdvance(); |
||||
|
/// @}
|
||||
|
|
||||
|
/// Creates a @ref ParserError exception and annotates it with the current position and the
|
||||
|
/// given @a _description.
|
||||
|
ParserError createParserError(std::string const& _description) const; |
||||
|
|
||||
|
std::shared_ptr<Scanner> m_scanner; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,702 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
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 <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity scanner. |
||||
|
*/ |
||||
|
|
||||
|
#include <algorithm> |
||||
|
#include <tuple> |
||||
|
|
||||
|
#include <libsolidity/Scanner.h> |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
} // end anonymous namespace
|
||||
|
|
||||
|
Scanner::Scanner(CharStream const& _source) |
||||
|
{ |
||||
|
reset(_source); |
||||
|
} |
||||
|
|
||||
|
void Scanner::reset(CharStream const& _source) |
||||
|
{ |
||||
|
m_source = _source; |
||||
|
m_char = m_source.get(); |
||||
|
skipWhitespace(); |
||||
|
scanToken(); |
||||
|
next(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength) |
||||
|
{ |
||||
|
BOOST_ASSERT(_expectedLength <= 4); // prevent overflow
|
||||
|
char x = 0; |
||||
|
for (int i = 0; i < _expectedLength; i++) |
||||
|
{ |
||||
|
int d = HexValue(m_char); |
||||
|
if (d < 0) |
||||
|
{ |
||||
|
rollback(i); |
||||
|
return false; |
||||
|
} |
||||
|
x = x * 16 + d; |
||||
|
advance(); |
||||
|
} |
||||
|
o_scannedNumber = 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; |
||||
|
scanToken(); |
||||
|
return m_current_token.token; |
||||
|
} |
||||
|
|
||||
|
Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) |
||||
|
{ |
||||
|
advance(); |
||||
|
if (m_char == _next) |
||||
|
return selectToken(_then); |
||||
|
else |
||||
|
return _else; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool Scanner::skipWhitespace() |
||||
|
{ |
||||
|
int const start_position = getSourcePos(); |
||||
|
while (IsWhiteSpace(m_char)) |
||||
|
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 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.start = getSourcePos(); |
||||
|
switch (m_char) |
||||
|
{ |
||||
|
case '\n': // 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(); |
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
addLiteralChar(c); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
Token::Value Scanner::scanString() |
||||
|
{ |
||||
|
char const 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(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
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)) |
||||
|
return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X'
|
||||
|
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)) |
||||
|
return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E'
|
||||
|
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('a') \ |
||||
|
KEYWORD("address", Token::ADDRESS) \ |
||||
|
KEYWORD_GROUP('b') \ |
||||
|
KEYWORD("break", Token::BREAK) \ |
||||
|
KEYWORD("bool", Token::BOOL) \ |
||||
|
KEYWORD_GROUP('c') \ |
||||
|
KEYWORD("case", Token::CASE) \ |
||||
|
KEYWORD("const", Token::CONST) \ |
||||
|
KEYWORD("continue", Token::CONTINUE) \ |
||||
|
KEYWORD("contract", Token::CONTRACT) \ |
||||
|
KEYWORD_GROUP('d') \ |
||||
|
KEYWORD("default", Token::DEFAULT) \ |
||||
|
KEYWORD("delete", Token::DELETE) \ |
||||
|
KEYWORD("do", Token::DO) \ |
||||
|
KEYWORD_GROUP('e') \ |
||||
|
KEYWORD("else", Token::ELSE) \ |
||||
|
KEYWORD("extends", Token::EXTENDS) \ |
||||
|
KEYWORD_GROUP('f') \ |
||||
|
KEYWORD("false", Token::FALSE_LITERAL) \ |
||||
|
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("in", Token::IN) \ |
||||
|
KEYWORD("int", Token::INT) \ |
||||
|
KEYWORD("int32", Token::INT32) \ |
||||
|
KEYWORD("int64", Token::INT64) \ |
||||
|
KEYWORD("int128", Token::INT128) \ |
||||
|
KEYWORD("int256", Token::INT256) \ |
||||
|
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("private", Token::PRIVATE) \ |
||||
|
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("true", Token::TRUE_LITERAL) \ |
||||
|
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_GROUP('w') \ |
||||
|
KEYWORD("while", Token::WHILE) \ |
||||
|
|
||||
|
|
||||
|
static Token::Value KeywordOrIdentifierToken(std::string const& input) |
||||
|
{ |
||||
|
BOOST_ASSERT(!input.empty()); |
||||
|
int const kMinLength = 2; |
||||
|
int const 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. */ \ |
||||
|
int const 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); |
||||
|
} |
||||
|
|
||||
|
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
|
||||
|
using size_type = std::string::size_type; |
||||
|
size_type searchStart = std::min<size_type>(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<int, int> CharStream::translatePositionToLineColumn(int _position) const |
||||
|
{ |
||||
|
using size_type = std::string::size_type; |
||||
|
size_type searchPosition = std::min<size_type>(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<int, int>(lineNumber, searchPosition - lineStart); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,204 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
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 <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity scanner. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <boost/assert.hpp> |
||||
|
|
||||
|
#include <libdevcore/Common.h> |
||||
|
#include <libdevcore/Log.h> |
||||
|
#include <libdevcore/CommonData.h> |
||||
|
#include <libsolidity/BaseTypes.h> |
||||
|
#include <libsolidity/Token.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
|
||||
|
class AstRawString; |
||||
|
class AstValueFactory; |
||||
|
class ParserRecorder; |
||||
|
|
||||
|
class CharStream |
||||
|
{ |
||||
|
public: |
||||
|
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(); } |
||||
|
char get() const { return m_source[m_pos]; } |
||||
|
char advanceAndGet(); |
||||
|
char rollback(size_t _amount); |
||||
|
|
||||
|
/// 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<int, int> translatePositionToLineColumn(int _position) const; |
||||
|
/// @}
|
||||
|
|
||||
|
private: |
||||
|
std::string m_source; |
||||
|
size_t m_pos; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
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(CharStream const& _source); |
||||
|
|
||||
|
/// Resets the scanner as if newly constructed with _input as input.
|
||||
|
void reset(CharStream const& _source); |
||||
|
|
||||
|
/// Returns the next token and advances input.
|
||||
|
Token::Value next(); |
||||
|
|
||||
|
/// Information about the current token
|
||||
|
/// @{
|
||||
|
|
||||
|
/// Returns the current token
|
||||
|
Token::Value getCurrentToken() { return m_current_token.token; } |
||||
|
Location getCurrentLocation() const { return m_current_token.location; } |
||||
|
const std::string& getCurrentLiteral() const { return m_current_token.literal; } |
||||
|
/// @}
|
||||
|
|
||||
|
/// 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<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } |
||||
|
/// @}
|
||||
|
|
||||
|
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(); } |
||||
|
/// @}
|
||||
|
|
||||
|
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; } |
||||
|
/// 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); |
||||
|
|
||||
|
bool scanHexNumber(char& o_scannedNumber, int _expectedLength); |
||||
|
|
||||
|
// 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; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Scope - object that holds declaration of names. |
||||
|
*/ |
||||
|
|
||||
|
#include <libsolidity/Scope.h> |
||||
|
#include <libsolidity/AST.h> |
||||
|
|
||||
|
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) |
||||
|
return m_outerScope->resolveName(_name, true); |
||||
|
return nullptr; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Scope - object that holds declaration of names. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <map> |
||||
|
#include <boost/noncopyable.hpp> |
||||
|
|
||||
|
#include <libsolidity/ASTForward.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
class Scope |
||||
|
{ |
||||
|
public: |
||||
|
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); |
||||
|
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; |
||||
|
Scope* getOuterScope() const { return m_outerScope; } |
||||
|
|
||||
|
private: |
||||
|
Scope* m_outerScope; |
||||
|
std::map<ASTString, Declaration*> m_declarations; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,93 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Formatting functions for errors referencing positions and locations in the source. |
||||
|
*/ |
||||
|
|
||||
|
#include <libsolidity/SourceReferenceFormatter.h> |
||||
|
#include <libsolidity/Scanner.h> |
||||
|
#include <libsolidity/Exceptions.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
void SourceReferenceFormatter::printSourceLocation(std::ostream& _stream, |
||||
|
Location const& _location, |
||||
|
Scanner const& _scanner) |
||||
|
{ |
||||
|
int startLine; |
||||
|
int startColumn; |
||||
|
std::tie(startLine, startColumn) = _scanner.translatePositionToLineColumn(_location.start); |
||||
|
_stream << "starting at line " << (startLine + 1) << ", column " << (startColumn + 1) << "\n"; |
||||
|
int endLine; |
||||
|
int endColumn; |
||||
|
std::tie(endLine, endColumn) = _scanner.translatePositionToLineColumn(_location.end); |
||||
|
if (startLine == endLine) |
||||
|
{ |
||||
|
_stream << _scanner.getLineAtPosition(_location.start) << std::endl |
||||
|
<< std::string(startColumn, ' ') << "^"; |
||||
|
if (endColumn > startColumn + 2) |
||||
|
_stream << std::string(endColumn - startColumn - 2, '-'); |
||||
|
if (endColumn > startColumn + 1) |
||||
|
_stream << "^"; |
||||
|
_stream << std::endl; |
||||
|
} |
||||
|
else |
||||
|
_stream << _scanner.getLineAtPosition(_location.start) << std::endl |
||||
|
<< std::string(startColumn, ' ') << "^\n" |
||||
|
<< "Spanning multiple lines.\n"; |
||||
|
} |
||||
|
|
||||
|
void SourceReferenceFormatter::printSourcePosition(std::ostream& _stream, |
||||
|
int _position, |
||||
|
const Scanner& _scanner) |
||||
|
{ |
||||
|
int line; |
||||
|
int column; |
||||
|
std::tie(line, column) = _scanner.translatePositionToLineColumn(_position); |
||||
|
_stream << "at line " << (line + 1) << ", column " << (column + 1) << std::endl |
||||
|
<< _scanner.getLineAtPosition(_position) << std::endl |
||||
|
<< std::string(column, ' ') << "^" << std::endl; |
||||
|
} |
||||
|
|
||||
|
void SourceReferenceFormatter::printExceptionInformation(std::ostream& _stream, |
||||
|
Exception const& _exception, |
||||
|
std::string const& _name, |
||||
|
Scanner const& _scanner) |
||||
|
{ |
||||
|
_stream << _name; |
||||
|
if (std::string const* description = boost::get_error_info<errinfo_comment>(_exception)) |
||||
|
_stream << ": " << *description; |
||||
|
|
||||
|
if (int const* position = boost::get_error_info<errinfo_sourcePosition>(_exception)) |
||||
|
{ |
||||
|
_stream << " "; |
||||
|
printSourcePosition(_stream, *position, _scanner); |
||||
|
} |
||||
|
if (Location const* location = boost::get_error_info<errinfo_sourceLocation>(_exception)) |
||||
|
{ |
||||
|
_stream << " "; |
||||
|
printSourceLocation(_stream, *location, _scanner); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Formatting functions for errors referencing positions and locations in the source. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <ostream> |
||||
|
#include <libsolidity/BaseTypes.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
|
||||
|
class Exception; // forward
|
||||
|
|
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
class Scanner; // forward
|
||||
|
|
||||
|
struct SourceReferenceFormatter |
||||
|
{ |
||||
|
public: |
||||
|
static void printSourceLocation(std::ostream& _stream, Location const& _location, Scanner const& _scanner); |
||||
|
static void printSourcePosition(std::ostream& _stream, int _position, Scanner const& _scanner); |
||||
|
static void printExceptionInformation(std::ostream& _stream, Exception const& _exception, |
||||
|
std::string const& _name, Scanner const& _scanner); |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,84 @@ |
|||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
#include <libsolidity/Token.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
#define T(name, string, precedence) #name, |
||||
|
char const* const Token::m_name[NUM_TOKENS] = |
||||
|
{ |
||||
|
TOKEN_LIST(T, T) |
||||
|
}; |
||||
|
#undef T |
||||
|
|
||||
|
|
||||
|
#define T(name, string, precedence) string, |
||||
|
char const* const Token::m_string[NUM_TOKENS] = |
||||
|
{ |
||||
|
TOKEN_LIST(T, T) |
||||
|
}; |
||||
|
#undef T |
||||
|
|
||||
|
|
||||
|
#define T(name, string, precedence) precedence, |
||||
|
int8_t const Token::m_precedence[NUM_TOKENS] = |
||||
|
{ |
||||
|
TOKEN_LIST(T, T) |
||||
|
}; |
||||
|
#undef T |
||||
|
|
||||
|
|
||||
|
#define KT(a, b, c) 'T', |
||||
|
#define KK(a, b, c) 'K', |
||||
|
char const Token::m_tokenType[] = |
||||
|
{ |
||||
|
TOKEN_LIST(KT, KK) |
||||
|
}; |
||||
|
#undef KT |
||||
|
#undef KK |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,334 @@ |
|||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <boost/assert.hpp> |
||||
|
|
||||
|
#include <libdevcore/Common.h> |
||||
|
#include <libdevcore/Log.h> |
||||
|
|
||||
|
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() 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) \ |
||||
|
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(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(LT, "<", 10) \ |
||||
|
T(GT, ">", 10) \ |
||||
|
T(LTE, "<=", 10) \ |
||||
|
T(GTE, ">=", 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) \ |
||||
|
\ |
||||
|
/* Keywords */ \ |
||||
|
K(BREAK, "break", 0) \ |
||||
|
K(CASE, "case", 0) \ |
||||
|
K(CONST, "const", 0) \ |
||||
|
K(CONTINUE, "continue", 0) \ |
||||
|
K(CONTRACT, "contract", 0) \ |
||||
|
K(DEFAULT, "default", 0) \ |
||||
|
K(DO, "do", 0) \ |
||||
|
K(ELSE, "else", 0) \ |
||||
|
K(EXTENDS, "extends", 0) \ |
||||
|
K(FOR, "for", 0) \ |
||||
|
K(FUNCTION, "function", 0) \ |
||||
|
K(IF, "if", 0) \ |
||||
|
K(IMPORT, "import", 0) \ |
||||
|
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(VAR, "var", 0) \ |
||||
|
K(WHILE, "while", 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 */ \ |
||||
|
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 */ \ |
||||
|
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) \ |
||||
|
\ |
||||
|
/* 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 char const* 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 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; } |
||||
|
static bool isOrderedRelationalCompareOp(Value op) |
||||
|
{ |
||||
|
return op == LT || op == LTE || op == GT || op == GTE; |
||||
|
} |
||||
|
static bool isEqualityOp(Value op) { return op == EQ; } |
||||
|
static bool isInequalityOp(Value op) { return op == NE; } |
||||
|
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 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 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); |
||||
|
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 <= 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); } |
||||
|
|
||||
|
// 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 char const* 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) |
||||
|
{ |
||||
|
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
|
||||
|
return m_precedence[tok]; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
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]; |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,174 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity data types |
||||
|
*/ |
||||
|
|
||||
|
#include <libdevcore/CommonIO.h> |
||||
|
#include <libsolidity/Types.h> |
||||
|
#include <libsolidity/AST.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
std::shared_ptr<Type> 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<IntegerType>(bits, |
||||
|
modifier == 0 ? IntegerType::Modifier::SIGNED : |
||||
|
modifier == 1 ? IntegerType::Modifier::UNSIGNED : |
||||
|
IntegerType::Modifier::HASH); |
||||
|
} |
||||
|
else if (_typeToken == Token::ADDRESS) |
||||
|
return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS); |
||||
|
else if (_typeToken == Token::BOOL) |
||||
|
return std::make_shared<BoolType>(); |
||||
|
else |
||||
|
BOOST_ASSERT(false); // @todo add other tyes
|
||||
|
return std::shared_ptr<Type>(); |
||||
|
} |
||||
|
|
||||
|
std::shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) |
||||
|
{ |
||||
|
return std::make_shared<StructType>(*_typeName.getReferencedStruct()); |
||||
|
} |
||||
|
|
||||
|
std::shared_ptr<Type> Type::fromMapping(Mapping const&) |
||||
|
{ |
||||
|
BOOST_ASSERT(false); //@todo not yet implemented
|
||||
|
return std::shared_ptr<Type>(); |
||||
|
} |
||||
|
|
||||
|
std::shared_ptr<Type> Type::forLiteral(Literal const& _literal) |
||||
|
{ |
||||
|
switch (_literal.getToken()) |
||||
|
{ |
||||
|
case Token::TRUE_LITERAL: |
||||
|
case Token::FALSE_LITERAL: |
||||
|
return std::make_shared<BoolType>(); |
||||
|
case Token::NUMBER: |
||||
|
return IntegerType::smallestTypeForLiteral(_literal.getValue()); |
||||
|
case Token::STRING_LITERAL: |
||||
|
return std::shared_ptr<Type>(); // @todo
|
||||
|
default: |
||||
|
return std::shared_ptr<Type>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
std::shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(std::string const&) |
||||
|
{ |
||||
|
//@todo
|
||||
|
return std::make_shared<IntegerType>(256, Modifier::UNSIGNED); |
||||
|
} |
||||
|
|
||||
|
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): |
||||
|
m_bits(_bits), m_modifier(_modifier) |
||||
|
{ |
||||
|
if (isAddress()) |
||||
|
_bits = 160; |
||||
|
BOOST_ASSERT(_bits > 0 && _bits <= 256 && _bits % 8 == 0); |
||||
|
} |
||||
|
|
||||
|
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const |
||||
|
{ |
||||
|
if (_convertTo.getCategory() != Category::INTEGER) |
||||
|
return false; |
||||
|
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_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(Type const& _convertTo) const |
||||
|
{ |
||||
|
return _convertTo.getCategory() == Category::INTEGER; |
||||
|
} |
||||
|
|
||||
|
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const |
||||
|
{ |
||||
|
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 |
||||
|
{ |
||||
|
return _operator == Token::DELETE || (!isAddress() && _operator == Token::BIT_NOT); |
||||
|
} |
||||
|
|
||||
|
std::string IntegerType::toString() const |
||||
|
{ |
||||
|
if (isAddress()) |
||||
|
return "address"; |
||||
|
std::string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint"); |
||||
|
return prefix + dev::toString(m_bits); |
||||
|
} |
||||
|
|
||||
|
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)
|
||||
|
if (_convertTo.getCategory() == Category::INTEGER) |
||||
|
{ |
||||
|
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); |
||||
|
if (!convertTo.isAddress()) |
||||
|
return true; |
||||
|
} |
||||
|
return isImplicitlyConvertibleTo(_convertTo); |
||||
|
} |
||||
|
|
||||
|
bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const |
||||
|
{ |
||||
|
if (_convertTo.getCategory() != Category::CONTRACT) |
||||
|
return false; |
||||
|
ContractType const& convertTo = dynamic_cast<ContractType const&>(_convertTo); |
||||
|
return &m_contract == &convertTo.m_contract; |
||||
|
} |
||||
|
|
||||
|
bool StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const |
||||
|
{ |
||||
|
if (_convertTo.getCategory() != Category::STRUCT) |
||||
|
return false; |
||||
|
StructType const& convertTo = dynamic_cast<StructType const&>(_convertTo); |
||||
|
return &m_struct == &convertTo.m_struct; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,197 @@ |
|||||
|
/*
|
||||
|
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 <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2014 |
||||
|
* Solidity data types |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <string> |
||||
|
#include <boost/noncopyable.hpp> |
||||
|
#include <boost/assert.hpp> |
||||
|
#include <libsolidity/ASTForward.h> |
||||
|
#include <libsolidity/Token.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
// @todo realMxN, string<N>, 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 std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken); |
||||
|
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); |
||||
|
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName); |
||||
|
|
||||
|
static std::shared_ptr<Type> forLiteral(Literal const& _literal); |
||||
|
|
||||
|
virtual Category getCategory() const = 0; |
||||
|
virtual bool isImplicitlyConvertibleTo(Type const&) const { return false; } |
||||
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const |
||||
|
{ |
||||
|
return isImplicitlyConvertibleTo(_convertTo); |
||||
|
} |
||||
|
virtual bool acceptsBinaryOperator(Token::Value) const { return false; } |
||||
|
virtual bool acceptsUnaryOperator(Token::Value) const { return false; } |
||||
|
|
||||
|
virtual std::string toString() const = 0; |
||||
|
}; |
||||
|
|
||||
|
class IntegerType: public Type |
||||
|
{ |
||||
|
public: |
||||
|
enum class Modifier |
||||
|
{ |
||||
|
UNSIGNED, SIGNED, HASH, ADDRESS |
||||
|
}; |
||||
|
virtual Category getCategory() const override { return Category::INTEGER; } |
||||
|
|
||||
|
static std::shared_ptr<IntegerType> smallestTypeForLiteral(std::string const& _literal); |
||||
|
|
||||
|
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); |
||||
|
|
||||
|
virtual bool isImplicitlyConvertibleTo(Type const& _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; |
||||
|
|
||||
|
virtual std::string toString() 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(Type const& _convertTo) const override |
||||
|
{ |
||||
|
return _convertTo.getCategory() == Category::BOOL; |
||||
|
} |
||||
|
virtual bool isExplicitlyConvertibleTo(Type const& _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; |
||||
|
} |
||||
|
virtual std::string toString() const override { return "bool"; } |
||||
|
}; |
||||
|
|
||||
|
class ContractType: public Type |
||||
|
{ |
||||
|
public: |
||||
|
virtual Category getCategory() const override { return Category::CONTRACT; } |
||||
|
ContractType(ContractDefinition const& _contract): m_contract(_contract) {} |
||||
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; |
||||
|
|
||||
|
virtual std::string toString() const override { return "contract{...}"; } |
||||
|
|
||||
|
private: |
||||
|
ContractDefinition const& m_contract; |
||||
|
}; |
||||
|
|
||||
|
class StructType: public Type |
||||
|
{ |
||||
|
public: |
||||
|
virtual Category getCategory() const override { 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; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
virtual std::string toString() const override { return "struct{...}"; } |
||||
|
|
||||
|
private: |
||||
|
StructDefinition const& m_struct; |
||||
|
}; |
||||
|
|
||||
|
class FunctionType: public Type |
||||
|
{ |
||||
|
public: |
||||
|
virtual Category getCategory() const override { return Category::FUNCTION; } |
||||
|
FunctionType(FunctionDefinition const& _function): m_function(_function) {} |
||||
|
|
||||
|
FunctionDefinition const& getFunction() const { return m_function; } |
||||
|
|
||||
|
virtual std::string toString() const override { return "function(...)returns(...)"; } |
||||
|
|
||||
|
private: |
||||
|
FunctionDefinition const& m_function; |
||||
|
}; |
||||
|
|
||||
|
class MappingType: public Type |
||||
|
{ |
||||
|
public: |
||||
|
virtual Category getCategory() const override { return Category::MAPPING; } |
||||
|
MappingType() {} |
||||
|
virtual std::string toString() const override { return "mapping(...=>...)"; } |
||||
|
|
||||
|
private: |
||||
|
//@todo
|
||||
|
}; |
||||
|
|
||||
|
//@todo should be changed into "empty anonymous struct"
|
||||
|
class VoidType: public Type |
||||
|
{ |
||||
|
public: |
||||
|
virtual Category getCategory() const override { return Category::VOID; } |
||||
|
VoidType() {} |
||||
|
virtual std::string toString() const override { return "void"; } |
||||
|
}; |
||||
|
|
||||
|
class TypeType: public Type |
||||
|
{ |
||||
|
public: |
||||
|
virtual Category getCategory() const override { return Category::TYPE; } |
||||
|
TypeType(std::shared_ptr<Type const> const& _actualType): m_actualType(_actualType) {} |
||||
|
|
||||
|
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; } |
||||
|
|
||||
|
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<Type const> m_actualType; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
ContractDefinition = 'contract' Identifier '{' ContractPart* '}' |
||||
|
ContractPart = VariableDeclaration ';' | StructDefinition | |
||||
|
FunctionDefinition | 'public:' | 'private:' |
||||
|
|
||||
|
StructDefinition = 'struct' Identifier '{' |
||||
|
( 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 = ElementaryTypeName | Identifier | Mapping |
||||
|
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' |
||||
|
|
||||
|
Block = '{' Statement* '}' |
||||
|
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? ';' |
||||
|
VariableDefinition = VariableDeclaration ( = Expression )? ';' |
||||
|
|
||||
|
Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | |
||||
|
MemberAccess | PrimaryExpression |
||||
|
// The expression syntax is actually much more complicated |
||||
|
Assignment = Expression (AssignmentOp Expression) |
||||
|
FunctionCall = Expression '(' ( Expression ( ',' Expression )* ) ')' |
||||
|
MemberAccess = Expression '.' Identifier |
||||
|
IndexAccess = Expression '[' Expresison ']' |
||||
|
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue