mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
WIP
This commit is contained in:
parent
e27313bc82
commit
953e8a178b
@ -45,7 +45,6 @@ using namespace dev::solidity;
|
||||
|
||||
pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||
{
|
||||
// TODO Would be nice to pretty-print this while retaining comments.
|
||||
string ir = generate(_contract);
|
||||
|
||||
yul::AssemblyStack asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
||||
@ -56,6 +55,10 @@ pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||
errorMessage += langutil::SourceReferenceFormatter::formatErrorInformation(*error);
|
||||
solAssert(false, "Invalid IR generated:\n" + errorMessage + "\n" + ir);
|
||||
}
|
||||
|
||||
// NB: pretty-print is retaining documentation comments (those with tripple-slash).
|
||||
string const irPrettyPrinted = asmStack.parseTree().toString(true);
|
||||
|
||||
asmStack.optimize();
|
||||
|
||||
string warning =
|
||||
@ -66,7 +69,7 @@ pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||
" * !USE AT YOUR OWN RISK! *\n"
|
||||
" *******************************************************/\n\n";
|
||||
|
||||
return {warning + ir, warning + asmStack.print()};
|
||||
return {warning + irPrettyPrinted, warning + asmStack.print()};
|
||||
}
|
||||
|
||||
string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
|
@ -94,7 +94,7 @@ public:
|
||||
bool operator()(Break const&);
|
||||
bool operator()(Continue const&);
|
||||
bool operator()(Block const& _block);
|
||||
bool operator()(Comment const& _comment) { return true; }
|
||||
bool operator()(Comment const&) { return true; }
|
||||
|
||||
private:
|
||||
/// Visits the statement and expects it to deposit one item onto the stack.
|
||||
|
@ -243,13 +243,23 @@ string AsmPrinter::operator()(Continue const&) const
|
||||
|
||||
string AsmPrinter::operator()(Block const& _block) const
|
||||
{
|
||||
size_t const outerBlockStatementIndex = exchange(m_blockStatementIndex, 0);
|
||||
ScopeGuard const _restoreBlockStatementIndex{[=]() { m_blockStatementIndex = outerBlockStatementIndex; }};
|
||||
auto const incrementBlockStatementIndex = [&](string _s) -> string
|
||||
{
|
||||
++m_blockStatementIndex;
|
||||
return _s;
|
||||
};
|
||||
|
||||
if (_block.statements.empty())
|
||||
return "{ }";
|
||||
string body = boost::algorithm::join(
|
||||
_block.statements | boost::adaptors::transformed(boost::apply_visitor(*this)),
|
||||
_block.statements |
|
||||
boost::adaptors::transformed(boost::apply_visitor(*this)) |
|
||||
boost::adaptors::transformed(incrementBlockStatementIndex),
|
||||
"\n"
|
||||
);
|
||||
if (body.size() < 30 && body.find('\n') == string::npos)
|
||||
if (body.size() < 30 && body.find('\n') == string::npos && body.find("//") == string::npos)
|
||||
return "{ " + body + " }";
|
||||
else
|
||||
{
|
||||
@ -258,10 +268,30 @@ string AsmPrinter::operator()(Block const& _block) const
|
||||
}
|
||||
}
|
||||
|
||||
std::string AsmPrinter::operator()(Comment const& _comment) const
|
||||
string AsmPrinter::operator()(Comment const& _comment) const
|
||||
{
|
||||
// Leading newline for better readability of comments.
|
||||
return "\n// " + _comment.text;
|
||||
auto const static trimWhitespace = [](string _s) -> string
|
||||
{
|
||||
auto const static isnospace = [](int _ch) { return !std::isspace(_ch); };
|
||||
_s.erase(_s.begin(), std::find_if(_s.begin(), _s.end(), isnospace));
|
||||
_s.erase(find_if(_s.rbegin(), _s.rend(), isnospace).base(), _s.end());
|
||||
return _s;
|
||||
};
|
||||
auto const trimmedText = trimWhitespace(_comment.text);
|
||||
|
||||
vector<string> lines;
|
||||
boost::split(lines, trimmedText, [](auto _ch) -> bool { return _ch == '\n'; });
|
||||
|
||||
stringstream out;
|
||||
|
||||
if (m_blockStatementIndex > 0)
|
||||
out << '\n'; // One leading newline for better readability of comments.
|
||||
|
||||
for (size_t i = 0; i < lines.size() - 1; ++i)
|
||||
out << "// " << trimWhitespace(lines[i]) + "\n";
|
||||
|
||||
out << "// " << trimWhitespace(lines.back());
|
||||
return out.str();
|
||||
}
|
||||
|
||||
string AsmPrinter::formatTypedName(TypedName _variable) const
|
||||
|
@ -60,6 +60,7 @@ private:
|
||||
std::string appendTypeName(YulString _type) const;
|
||||
|
||||
bool m_yul = false;
|
||||
mutable size_t m_blockStatementIndex;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ Scanner const& AssemblyStack::scanner() const
|
||||
return *m_scanner;
|
||||
}
|
||||
|
||||
bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
|
||||
bool AssemblyStack::parse(std::string const& _sourceName, std::string const& _source)
|
||||
{
|
||||
m_errors.clear();
|
||||
m_analysisSuccessful = false;
|
||||
@ -85,6 +85,19 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
|
||||
return false;
|
||||
solAssert(m_parserResult, "");
|
||||
solAssert(m_parserResult->code, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
yul::Object const& AssemblyStack::parseTree() const noexcept
|
||||
{
|
||||
yulAssert(m_parserResult, "Parser result must be available.");
|
||||
return *m_parserResult;
|
||||
}
|
||||
|
||||
bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source)
|
||||
{
|
||||
if (!parse(_sourceName, _source))
|
||||
return false;
|
||||
|
||||
return analyzeParsed();
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ public:
|
||||
/// @returns the scanner used during parsing
|
||||
langutil::Scanner const& scanner() const;
|
||||
|
||||
bool parse(std::string const& _sourceName, std::string const& _source);
|
||||
|
||||
/// Runs parsing and analysis steps, returns false if input cannot be assembled.
|
||||
/// Multiple calls overwrite the previous state.
|
||||
bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source);
|
||||
@ -84,6 +86,9 @@ public:
|
||||
/// Run the assembly step (should only be called after parseAndAnalyze).
|
||||
MachineAssemblyObject assemble(Machine _machine) const;
|
||||
|
||||
/// @returns parsed source code as AST.
|
||||
yul::Object const& parseTree() const noexcept;
|
||||
|
||||
/// @returns the errors generated during parsing, analysis (and potentially assembly).
|
||||
langutil::ErrorList const& errors() const { return m_errors; }
|
||||
|
||||
|
@ -181,11 +181,6 @@ bool SyntacticallyEqual::statementEqual(Block const& _lhs, Block const& _rhs)
|
||||
});
|
||||
}
|
||||
|
||||
bool SyntacticallyEqual::statementEqual(Comment const& _lhs, Comment const& _rhs)
|
||||
{
|
||||
return _lhs.text == _rhs.text;
|
||||
}
|
||||
|
||||
bool SyntacticallyEqual::visitDeclaration(TypedName const& _lhs, TypedName const& _rhs)
|
||||
{
|
||||
if (_lhs.type != _rhs.type)
|
||||
|
@ -58,7 +58,8 @@ public:
|
||||
bool statementEqual(Break const&, Break const&) { return true; }
|
||||
bool statementEqual(Continue const&, Continue const&) { return true; }
|
||||
bool statementEqual(Block const& _lhs, Block const& _rhs);
|
||||
bool statementEqual(Comment const& _lhs, Comment const& _rhs);
|
||||
bool statementEqual(Comment const&, Comment const&) { return true; }
|
||||
|
||||
private:
|
||||
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
||||
bool statementEqual(Label const& _lhs, Label const& _rhs);
|
||||
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity 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.
|
||||
|
||||
solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AssemblyStack.h>
|
||||
@ -18,34 +35,31 @@ using namespace std;
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
boost::optional<std::string> prettyPrint(
|
||||
std::string const& _sourceCode,
|
||||
std::string const& _sourceName,
|
||||
yul::EVMDialect const& _evmDialect,
|
||||
langutil::ErrorReporter& _errorReporter
|
||||
)
|
||||
{
|
||||
langutil::EVMVersion const evmVersion = *langutil::EVMVersion::fromString("petersburg");
|
||||
yul::EVMDialect const& evmDialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion);
|
||||
yul::Parser parser{_errorReporter, evmDialect};
|
||||
langutil::CharStream source{_sourceCode, _sourceName};
|
||||
auto scanner = make_shared<langutil::Scanner>(source);
|
||||
shared_ptr<yul::Block> ast = parser.parse(scanner, true);
|
||||
|
||||
if (_errorReporter.hasErrors())
|
||||
return boost::none;
|
||||
else
|
||||
return {yul::AsmPrinter{true}(*ast)};
|
||||
}
|
||||
|
||||
struct Flags
|
||||
{
|
||||
langutil::EVMVersion evmVersion;
|
||||
yul::AssemblyStack::Language language;
|
||||
std::string sourceName;
|
||||
std::string sourceCode;
|
||||
};
|
||||
|
||||
boost::optional<Flags> parseArgs(int argc, const char* argv[])
|
||||
static boost::optional<yul::AssemblyStack::Language> parseAssemblyLanguageId(string const& name)
|
||||
{
|
||||
using Language = yul::AssemblyStack::Language;
|
||||
|
||||
if (name == "yul")
|
||||
return {Language::Yul};
|
||||
else if (name == "assembly")
|
||||
return {Language::Assembly};
|
||||
else if (name == "strict-assembly")
|
||||
return {Language::StrictAssembly};
|
||||
else if (name == "ewasm")
|
||||
return {Language::EWasm};
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
boost::optional<Flags> parseArgs(int _argc, char const* _argv[])
|
||||
{
|
||||
po::options_description options(
|
||||
R"(yul-format, the Yul source code pretty printer.
|
||||
@ -57,12 +71,13 @@ Allowed options)",
|
||||
po::options_description::m_default_line_length - 23);
|
||||
|
||||
options.add_options()
|
||||
("help", "Show this help screen.")
|
||||
("help,h", "Show this help screen.")
|
||||
(
|
||||
"evm-version",
|
||||
po::value<string>()->value_name("version"),
|
||||
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg (default)."
|
||||
)
|
||||
("lang", po::value<string>(), "Language to format. One of yul, assembly, strict-assembly, ewasm.")
|
||||
("input-file", po::value<string>(), "Input file to format.");
|
||||
|
||||
po::positional_options_description filesPositions;
|
||||
@ -71,7 +86,7 @@ Allowed options)",
|
||||
po::variables_map arguments;
|
||||
try
|
||||
{
|
||||
po::command_line_parser cmdLineParser(argc, argv);
|
||||
po::command_line_parser cmdLineParser(_argc, _argv);
|
||||
cmdLineParser.options(options).positional(filesPositions);
|
||||
po::store(cmdLineParser.run(), arguments);
|
||||
}
|
||||
@ -87,7 +102,7 @@ Allowed options)",
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
langutil::EVMVersion evmVersion = *langutil::EVMVersion::fromString("petersburg");
|
||||
langutil::EVMVersion evmVersion = langutil::EVMVersion::petersburg();
|
||||
if (arguments.count("evm-version"))
|
||||
{
|
||||
string versionOptionStr = arguments["evm-version"].as<string>();
|
||||
@ -99,40 +114,47 @@ Allowed options)",
|
||||
}
|
||||
evmVersion = *value;
|
||||
}
|
||||
yul::AssemblyStack::Language lang = yul::AssemblyStack::Language::Yul;
|
||||
if (arguments.count("lang"))
|
||||
{
|
||||
auto parsedLangId = parseAssemblyLanguageId(arguments["lang"].as<string>());
|
||||
if (!parsedLangId)
|
||||
{
|
||||
cerr << "Invalid language ID. Try --help.\n";
|
||||
return boost::none;
|
||||
}
|
||||
else
|
||||
lang = *parsedLangId;
|
||||
}
|
||||
tuple<string, string> const input = [&]() -> tuple<string, string>
|
||||
{
|
||||
if (arguments.count("input-file"))
|
||||
return Flags{
|
||||
evmVersion,
|
||||
return make_tuple(
|
||||
arguments["input-file"].as<string>(),
|
||||
dev::readFileAsString(arguments["input-file"].as<string>())
|
||||
};
|
||||
);
|
||||
else
|
||||
return make_tuple(string{"stdin"}, dev::readStandardInput());
|
||||
}();
|
||||
return Flags{
|
||||
evmVersion,
|
||||
"stdin",
|
||||
dev::readStandardInput()
|
||||
lang,
|
||||
get<0>(input),
|
||||
get<1>(input)
|
||||
};
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
int main(int argc, char const* argv[])
|
||||
{
|
||||
boost::optional<Flags> flags = parseArgs(argc, argv);
|
||||
if (!flags)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
langutil::ErrorList errors;
|
||||
langutil::ErrorReporter errorReporter{errors};
|
||||
|
||||
optional<string> const pretty = prettyPrint(
|
||||
flags->sourceCode,
|
||||
flags->sourceName,
|
||||
yul::EVMDialect::strictAssemblyForEVM(flags->evmVersion),
|
||||
errorReporter
|
||||
);
|
||||
|
||||
if (!errorReporter.hasErrors())
|
||||
cout << *pretty;
|
||||
yul::AssemblyStack stack{flags->evmVersion, flags->language, {}};
|
||||
if (stack.parse(flags->sourceName, flags->sourceCode))
|
||||
cout << yul::AsmPrinter{true}(*stack.parseTree().code);
|
||||
else
|
||||
for (shared_ptr<langutil::Error const> const& error : errors)
|
||||
for (shared_ptr<langutil::Error const> const& error : stack.errors())
|
||||
langutil::SourceReferenceFormatterHuman{cerr, true}.printErrorInformation(*error);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
Loading…
Reference in New Issue
Block a user