diff options
author | chriseth <chris@ethereum.org> | 2016-09-01 17:02:50 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-01 17:02:50 +0800 |
commit | b5d941d3d9f32193c7f9094dee20511585508f6a (patch) | |
tree | 05b5156cbc8fb901102890994842412f18504d06 /libsolidity/analysis/SemVerHandler.cpp | |
parent | 4a26adfb7d4e962de094f4c6f02139181fac1699 (diff) | |
parent | 4abba77ddc0b4402597d13d5c29adcf5cac82e11 (diff) | |
download | dexon-solidity-b5d941d3d9f32193c7f9094dee20511585508f6a.tar.gz dexon-solidity-b5d941d3d9f32193c7f9094dee20511585508f6a.tar.zst dexon-solidity-b5d941d3d9f32193c7f9094dee20511585508f6a.zip |
Merge pull request #935 from chriseth/pragma
Version pragma
Diffstat (limited to 'libsolidity/analysis/SemVerHandler.cpp')
-rw-r--r-- | libsolidity/analysis/SemVerHandler.cpp | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp new file mode 100644 index 00000000..c7b212b2 --- /dev/null +++ b/libsolidity/analysis/SemVerHandler.cpp @@ -0,0 +1,290 @@ +/* + 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 <chris@ethereum.org> + * @date 2016 + * Utilities to handle semantic versioning. + */ + +#include <libsolidity/analysis/SemVerHandler.h> +#include <functional> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SemVerVersion::SemVerVersion(string const& _versionString) +{ + auto i = _versionString.begin(); + auto end = _versionString.end(); + + for (unsigned level = 0; level < 3; ++level) + { + unsigned v = 0; + for (; i != end && '0' <= *i && *i <= '9'; ++i) + v = v * 10 + (*i - '0'); + numbers[level] = v; + if (level < 2) + { + if (i == end || *i != '.') + throw SemVerError(); + else + ++i; + } + } + if (i != end && *i == '-') + { + auto prereleaseStart = ++i; + while (i != end && *i != '+') ++i; + prerelease = string(prereleaseStart, i); + } + if (i != end && *i == '+') + { + auto buildStart = ++i; + while (i != end) ++i; + build = string(buildStart, i); + } + if (i != end) + throw SemVerError(); +} + +bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _version) const +{ + if (prefix == Token::BitNot) + { + MatchComponent comp = *this; + + comp.prefix = Token::GreaterThanOrEqual; + if (!comp.matches(_version)) + return false; + + if (levelsPresent >= 2) + comp.levelsPresent = 2; + else + comp.levelsPresent = 1; + comp.prefix = Token::LessThanOrEqual; + return comp.matches(_version); + } + else if (prefix == Token::BitXor) + { + MatchComponent comp = *this; + + comp.prefix = Token::GreaterThanOrEqual; + if (!comp.matches(_version)) + return false; + + if (comp.version.numbers[0] == 0) + comp.levelsPresent = 2; + else + comp.levelsPresent = 1; + comp.prefix = Token::LessThanOrEqual; + return comp.matches(_version); + } + else + { + int cmp = 0; + bool didCompare = false; + for (unsigned i = 0; i < levelsPresent && cmp == 0; i++) + if (version.numbers[i] != unsigned(-1)) + { + didCompare = true; + cmp = _version.numbers[i] - version.numbers[i]; + } + if (cmp == 0 && !_version.prerelease.empty() && didCompare) + cmp = -1; + if (prefix == Token::Assign) + return cmp == 0; + else if (prefix == Token::LessThan) + return cmp < 0; + else if (prefix == Token::LessThanOrEqual) + return cmp <= 0; + else if (prefix == Token::GreaterThan) + return cmp > 0; + else if (prefix == Token::GreaterThanOrEqual) + return cmp >= 0; + else + solAssert(false, "Invalid SemVer expression"); + return false; + } +} + +bool SemVerMatchExpression::Conjunction::matches(SemVerVersion const& _version) const +{ + for (auto const& component: components) + if (!component.matches(_version)) + return false; + return true; +} + +bool SemVerMatchExpression::matches(SemVerVersion const& _version) const +{ + if (!isValid()) + return false; + for (auto const& range: m_disjunction) + if (range.matches(_version)) + return true; + return false; +} + +SemVerMatchExpression SemVerMatchExpressionParser::parse() +{ + reset(); + + try + { + while (true) + { + parseMatchExpression(); + if (m_pos >= m_tokens.size()) + break; + if (currentToken() != Token::Or) + throw SemVerError(); + nextToken(); + } + } + catch (SemVerError const&) + { + reset(); + } + + return m_expression; +} + + +void SemVerMatchExpressionParser::reset() +{ + m_expression = SemVerMatchExpression(); + m_pos = 0; + m_posInside = 0; +} + +void SemVerMatchExpressionParser::parseMatchExpression() +{ + // component - component (range) + // or component component* (conjunction) + + SemVerMatchExpression::Conjunction range; + range.components.push_back(parseMatchComponent()); + if (currentToken() == Token::Sub) + { + range.components[0].prefix = Token::GreaterThanOrEqual; + nextToken(); + range.components.push_back(parseMatchComponent()); + range.components[1].prefix = Token::LessThanOrEqual; + } + else + while (currentToken() != Token::Or && currentToken() != Token::Illegal) + range.components.push_back(parseMatchComponent()); + m_expression.m_disjunction.push_back(range); +} + +SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchComponent() +{ + SemVerMatchExpression::MatchComponent component; + Token::Value token = currentToken(); + if ( + token == Token::BitXor || + token == Token::BitNot || + token == Token::LessThan || + token == Token::LessThanOrEqual|| + token == Token::GreaterThan || + token == Token::GreaterThanOrEqual || + token == Token::Assign + ) + { + component.prefix = token; + nextToken(); + } + else + component.prefix = Token::Assign; + + component.levelsPresent = 0; + while (component.levelsPresent < 3) + { + component.version.numbers[component.levelsPresent] = parseVersionPart(); + component.levelsPresent++; + if (currentChar() == '.') + nextChar(); + else + break; + } + // TODO we do not support pre and build version qualifiers for now in match expressions + // (but we do support them in the actual versions) + return component; +} + +unsigned SemVerMatchExpressionParser::parseVersionPart() +{ + auto startPos = m_pos; + char c = currentChar(); + nextChar(); + if (c == 'x' || c == 'X' || c == '*') + return unsigned(-1); + else if (c == '0') + return 0; + else if ('1' <= c && c <= '9') + { + unsigned v = c - '0'; + // If we skip to the next token, the current number is terminated. + while (m_pos == startPos && '0' <= currentChar() && currentChar() <= '9') + { + c = currentChar(); + if (v * 10 < v || v * 10 + (c - '0') < v * 10) + throw SemVerError(); + v = v * 10 + c - '0'; + nextChar(); + } + return v; + } + else + throw SemVerError(); +} + +char SemVerMatchExpressionParser::currentChar() const +{ + if (m_pos >= m_literals.size()) + return char(-1); + if (m_posInside >= m_literals[m_pos].size()) + return char(-1); + return m_literals[m_pos][m_posInside]; +} + +char SemVerMatchExpressionParser::nextChar() +{ + if (m_pos < m_literals.size()) + { + if (m_posInside + 1 >= m_literals[m_pos].size()) + nextToken(); + else + ++m_posInside; + } + return currentChar(); +} + +Token::Value SemVerMatchExpressionParser::currentToken() const +{ + if (m_pos < m_tokens.size()) + return m_tokens[m_pos]; + else + return Token::Illegal; +} + +void SemVerMatchExpressionParser::nextToken() +{ + ++m_pos; + m_posInside = 0; +} |