#include <libsolidity/formal/ExpressionFormatter.h>
#include <libsolutil/Algorithms.h>
#include <libsolutil/CommonData.h>
#include <boost/algorithm/string.hpp>
#include <range/v3/algorithm/for_each.hpp>
#include <map>
#include <vector>
#include <string>
using namespace std;
using boost::algorithm::starts_with;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::smtutil;
using namespace solidity::frontend::smt;
namespace solidity::frontend::smt
{
namespace
{
string formatDatatypeAccessor(smtutil::Expression const& _expr, vector<string> const& _args)
{
auto const& op = _expr.name;
if (op == "dt_accessor_keccak256")
return "keccak256";
if (op == "dt_accessor_sha256")
return "sha256";
if (op == "dt_accessor_ripemd160")
return "ripemd160";
if (op == "dt_accessor_ecrecover")
return "ecrecover";
string accessorStr = "accessor_";
string type = op.substr(op.rfind(accessorStr) + accessorStr.size());
solAssert(_expr.arguments.size() == 1, "");
if (type == "length")
return _args.at(0) + ".length";
if (type == "array")
return _args.at(0);
if (
starts_with(type, "block") ||
starts_with(type, "msg") ||
starts_with(type, "tx") ||
starts_with(type, "abi")
)
return type;
if (starts_with(type, "t_function_abi"))
return type;
return _args.at(0) + "." + type;
}
string formatGenericOp(smtutil::Expression const& _expr, vector<string> const& _args)
{
return _expr.name + "(" + boost::algorithm::join(_args, ", ") + ")";
}
string formatInfixOp(string const& _op, vector<string> const& _args)
{
return "(" + boost::algorithm::join(_args, " " + _op + " ") + ")";
}
string formatArrayOp(smtutil::Expression const& _expr, vector<string> const& _args)
{
if (_expr.name == "select")
{
auto const& a0 = _args.at(0);
static set<string> const ufs{"keccak256", "sha256", "ripemd160", "ecrecover"};
if (ufs.count(a0) || starts_with(a0, "t_function_abi"))
return _args.at(0) + "(" + _args.at(1) + ")";
return _args.at(0) + "[" + _args.at(1) + "]";
}
if (_expr.name == "store")
return "(" + _args.at(0) + "[" + _args.at(1) + "] := " + _args.at(2) + ")";
return formatGenericOp(_expr, _args);
}
string formatUnaryOp(smtutil::Expression const& _expr, vector<string> const& _args)
{
if (_expr.name == "not")
return "!" + _args.at(0);
return formatGenericOp(_expr, _args);
}
}
smtutil::Expression substitute(smtutil::Expression _from, map<string, string> const& _subst)
{
if (_from.name == "forall" || _from.name == "exists")
return smtutil::Expression(true);
if (_subst.count(_from.name))
_from.name = _subst.at(_from.name);
for (auto& arg: _from.arguments)
arg = substitute(arg, _subst);
return _from;
}
string toSolidityStr(smtutil::Expression const& _expr)
{
auto const& op = _expr.name;
auto const& args = _expr.arguments;
auto strArgs = util::applyMap(args, [](auto const& _arg) { return toSolidityStr(_arg); });
if (args.empty())
return op;
if (starts_with(op, "dt_accessor"))
return formatDatatypeAccessor(_expr, strArgs);
static map<string, string> const infixOps{
{"and", "&&"},
{"or", "||"},
{"implies", "=>"},
{"=", "="},
{">", ">"},
{">=", ">="},
{"<", "<"},
{"<=", "<="},
{"+", "+"},
{"-", "-"},
{"*", "*"},
{"/", "/"},
{"div", "/"},
{"mod", "%"}
};
if (infixOps.count(op))
return formatInfixOp(infixOps.at(op), strArgs);
static set<string> const arrayOps{"select", "store", "const_array"};
if (arrayOps.count(op))
return formatArrayOp(_expr, strArgs);
if (args.size() == 1)
return formatUnaryOp(_expr, strArgs);
return op + "(" + boost::algorithm::join(strArgs, ", ") + ")";
}
}