From e85fe2348f0ca293594546b89933aafe9604bb47 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Sun, 8 Feb 2015 19:23:17 +0800 Subject: [PATCH 1/3] add exponent operator https://www.pivotaltracker.com/n/projects/1189488/stories/83746404 --- libsolidity/ExpressionCompiler.cpp | 3 +++ libsolidity/Scanner.cpp | 10 ++++++++-- libsolidity/Token.h | 5 +++-- libsolidity/Types.cpp | 10 ++++++++++ test/SolidityEndToEndTest.cpp | 30 ++++++++++++++++++++++++++++++ test/SolidityParser.cpp | 11 +++++++++++ 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 1d3f23e69..beda01322 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -650,6 +650,9 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty case Token::Mod: m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); break; + case Token::Exp: + m_context << eth::Instruction::EXP; + break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator.")); } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index fc4bdb6b5..fbe3ea974 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -465,8 +465,14 @@ void Scanner::scanToken() token = Token::Sub; break; case '*': - // * *= - token = selectToken('=', Token::AssignMul, Token::Mul); + // * ** *= + advance(); + if (m_char == '*') + token = selectToken(Token::Exp); + else if (m_char == '=') + token = selectToken(Token::AssignMul); + else + token = Token::Mul; break; case '%': // % %= diff --git a/libsolidity/Token.h b/libsolidity/Token.h index b913f0cec..ac8c618fa 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -118,6 +118,7 @@ namespace solidity T(Mul, "*", 13) \ T(Div, "/", 13) \ T(Mod, "%", 13) \ + T(Exp, "**", 14) \ \ /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ @@ -361,10 +362,10 @@ public: // Predicates static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; } static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; } - static bool isBinaryOp(Value op) { return Comma <= op && op <= Mod; } + static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; } static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || op == Add || op == Mul || op == Equal || op == NotEqual; } - static bool isArithmeticOp(Value op) { return Add <= op && op <= Mod; } + static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; } static bool isCompareOp(Value op) { return Equal <= op && op <= In; } static Value AssignmentToBinaryOp(Value op) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6fd9e8b4c..dcc0738d7 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -320,6 +320,16 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty return TypePointer(); value = m_value % other.m_value; break; + case Token::Exp: + if (other.m_value < 0) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("exponent can't be negative")); + else + { + value = boost::multiprecision::powm(m_value, other.m_value, bigint(2) << 256); + if (value >= (bigint(1) << 256)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("exp result overflowed")); + } + break; default: return TypePointer(); } diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 31b80894e..748110145 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -56,6 +56,36 @@ BOOST_AUTO_TEST_CASE(empty_contract) BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()).empty()); } +BOOST_AUTO_TEST_CASE(exp_operator) +{ + char const* sourceCode = R"( + contract test { + function f(uint a) returns(uint d) { return 2 ** a; } + })"; + compileAndRun(sourceCode); + testSolidityAgainstCppOnRange("f(uint256)", [](u256 const& a) -> u256 { return u256(1 << a.convert_to()); }, 0, 16); +} + +BOOST_AUTO_TEST_CASE(exp_operator_const) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint d) { return 2 ** 3; } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(8))); +} + +BOOST_AUTO_TEST_CASE(exp_operator_const_signed) +{ + char const* sourceCode = R"( + contract test { + function f() returns(int d) { return -2 ** 3; } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(-8))); +} + BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = "contract test {\n" diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 7af99567b..84f36170f 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -387,6 +387,17 @@ BOOST_AUTO_TEST_CASE(complex_expression) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(exp_expression) +{ + char const* text = R"( + contract test { + function fun(uint256 a) { + uint256 x = 3 ** a; + } + })"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_CASE(while_loop) { char const* text = "contract test {\n" From aabc234e457075197b70b673c4002994e417a2ed Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Mon, 9 Feb 2015 23:15:36 +0800 Subject: [PATCH 2/3] add two more exp tests --- test/SolidityNameAndTypeResolution.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index f4be31f4b..b529f0b70 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -974,6 +974,24 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } +BOOST_AUTO_TEST_CASE(exp_operator_negative_exponent) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint d) { return 2 ** -3; } + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), InternalCompilerError); +} + +BOOST_AUTO_TEST_CASE(exp_operator_const_overflowed) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint d) { return 10 ** 256; } + })"; + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), InternalCompilerError); +} + BOOST_AUTO_TEST_SUITE_END() } From 1372154ff2955068b30b00d352726822a6fc28f6 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 10 Feb 2015 22:43:13 +0800 Subject: [PATCH 3/3] small fixes per chris's comments --- libsolidity/Types.cpp | 12 ++++++------ test/SolidityEndToEndTest.cpp | 2 +- test/SolidityNameAndTypeResolution.cpp | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index dcc0738d7..5f573a6da 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -26,6 +26,8 @@ #include #include +#include + using namespace std; namespace dev @@ -322,13 +324,11 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty break; case Token::Exp: if (other.m_value < 0) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("exponent can't be negative")); + return TypePointer(); + else if (other.m_value > std::numeric_limits::max()) + return TypePointer(); else - { - value = boost::multiprecision::powm(m_value, other.m_value, bigint(2) << 256); - if (value >= (bigint(1) << 256)) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("exp result overflowed")); - } + value = boost::multiprecision::pow(m_value, other.m_value.convert_to()); break; default: return TypePointer(); diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 748110145..13a666fbf 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_const_signed) { char const* sourceCode = R"( contract test { - function f() returns(int d) { return -2 ** 3; } + function f() returns(int d) { return (-2) ** 3; } })"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(-8))); diff --git a/test/SolidityNameAndTypeResolution.cpp b/test/SolidityNameAndTypeResolution.cpp index b529f0b70..d013f5c5e 100644 --- a/test/SolidityNameAndTypeResolution.cpp +++ b/test/SolidityNameAndTypeResolution.cpp @@ -980,16 +980,16 @@ BOOST_AUTO_TEST_CASE(exp_operator_negative_exponent) contract test { function f() returns(uint d) { return 2 ** -3; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), InternalCompilerError); + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } -BOOST_AUTO_TEST_CASE(exp_operator_const_overflowed) +BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) { char const* sourceCode = R"( contract test { - function f() returns(uint d) { return 10 ** 256; } + function f() returns(uint d) { return 2 ** 10000000000; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), InternalCompilerError); + BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } BOOST_AUTO_TEST_SUITE_END()