diff --git a/CMakeLists.txt b/CMakeLists.txt index 40a81c03a..49a56beba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option(LLL "Build LLL" OFF) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) option(LLLC_LINK_STATIC "Link lllc executable statically on supported platforms" OFF) option(INSTALL_LLLC "Include lllc executable in installation" ${LLL}) +option(YUL_FORMAT "Build yul-format command line tool" ON) # Setup cccache. include(EthCcache) @@ -72,6 +73,11 @@ if (NOT EMSCRIPTEN) endif() endif() +# tools +if (YUL_FORMAT) + add_subdirectory(tools/yul-format) +endif() + if (TESTS AND NOT EMSCRIPTEN) add_subdirectory(test) endif() diff --git a/tools/yul-format/CMakeLists.txt b/tools/yul-format/CMakeLists.txt new file mode 100644 index 000000000..679950870 --- /dev/null +++ b/tools/yul-format/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(yul-format main.cpp) +target_link_libraries(yul-format PRIVATE yul ${Boost_PROGRAM_OPTIONS_LIBRARIES}) diff --git a/tools/yul-format/main.cpp b/tools/yul-format/main.cpp new file mode 100644 index 000000000..c92e0cdce --- /dev/null +++ b/tools/yul-format/main.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +using boost::optional; +using namespace std; + +namespace po = boost::program_options; + +boost::optional 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(source); + shared_ptr ast = parser.parse(scanner, true); + + if (_errorReporter.hasErrors()) + return boost::none; + else + return {yul::AsmPrinter{true}(*ast)}; +} + +struct Flags +{ + langutil::EVMVersion evmVersion; + std::string sourceName; + std::string sourceCode; +}; + +boost::optional parseArgs(int argc, const char* argv[]) +{ + po::options_description options( + R"(yul-format, the Yul source code pretty printer. +Usage: yul-format [Options] < input +Reads a single source from stdin and prints it with proper formatting. + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23); + + options.add_options() + ("help", "Show this help screen.") + ( + "evm-version", + po::value()->value_name("version"), + "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg (default)." + ) + ("input-file", po::value(), "Input file to format."); + + po::positional_options_description filesPositions; + filesPositions.add("input-file", -1); + + po::variables_map arguments; + try + { + po::command_line_parser cmdLineParser(argc, argv); + cmdLineParser.options(options).positional(filesPositions); + po::store(cmdLineParser.run(), arguments); + } + catch (po::error const& _exception) + { + cerr << _exception.what() << endl; + return boost::none; + } + + if (arguments.count("help")) + { + cout << options; + return boost::none; + } + + langutil::EVMVersion evmVersion = *langutil::EVMVersion::fromString("petersburg"); + if (arguments.count("evm-version")) + { + string versionOptionStr = arguments["evm-version"].as(); + boost::optional value = langutil::EVMVersion::fromString(versionOptionStr); + if (!value) + { + cerr << "Invalid option for --evm-version: " << versionOptionStr << endl; + return boost::none; + } + evmVersion = *value; + } + if (arguments.count("input-file")) + return Flags{ + evmVersion, + arguments["input-file"].as(), + dev::readFileAsString(arguments["input-file"].as()) + }; + else + return Flags{ + evmVersion, + "stdin", + dev::readStandardInput() + }; +} + +int main(int argc, const char* argv[]) +{ + boost::optional flags = parseArgs(argc, argv); + if (!flags) + return EXIT_FAILURE; + + langutil::ErrorList errors; + langutil::ErrorReporter errorReporter{errors}; + + optional const pretty = prettyPrint( + flags->sourceCode, + flags->sourceName, + yul::EVMDialect::strictAssemblyForEVM(flags->evmVersion), + errorReporter + ); + + if (!errorReporter.hasErrors()) + cout << *pretty; + else + for (shared_ptr const& error : errors) + langutil::SourceReferenceFormatterHuman{cerr, true}.printErrorInformation(*error); + + return EXIT_SUCCESS; +}