1 /*
2 This file is part of solidity.
3
4 solidity is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 solidity is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with solidity. If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 /**
19 * @author Christian <chris@ethereum.org>
20 * @date 2016
21 * Utilities to handle semantic versioning.
22 */
23
24 #include <liblangutil/SemVerHandler.h>
25
26 #include <liblangutil/Exceptions.h>
27
28 #include <functional>
29 #include <limits>
30
31 using namespace std;
32 using namespace solidity;
33 using namespace solidity::langutil;
34
SemVerMatchExpressionParser(vector<Token> _tokens,vector<string> _literals)35 SemVerMatchExpressionParser::SemVerMatchExpressionParser(vector<Token> _tokens, vector<string> _literals):
36 m_tokens(std::move(_tokens)), m_literals(std::move(_literals))
37 {
38 solAssert(m_tokens.size() == m_literals.size(), "");
39 }
40
SemVerVersion(string const & _versionString)41 SemVerVersion::SemVerVersion(string const& _versionString)
42 {
43 auto i = _versionString.begin();
44 auto end = _versionString.end();
45
46 for (unsigned level = 0; level < 3; ++level)
47 {
48 unsigned v = 0;
49 for (; i != end && '0' <= *i && *i <= '9'; ++i)
50 v = v * 10 + unsigned(*i - '0');
51 numbers[level] = v;
52 if (level < 2)
53 {
54 if (i == end || *i != '.')
55 BOOST_THROW_EXCEPTION(SemVerError());
56 else
57 ++i;
58 }
59 }
60 if (i != end && *i == '-')
61 {
62 auto prereleaseStart = ++i;
63 while (i != end && *i != '+') ++i;
64 prerelease = string(prereleaseStart, i);
65 }
66 if (i != end && *i == '+')
67 {
68 auto buildStart = ++i;
69 while (i != end) ++i;
70 build = string(buildStart, i);
71 }
72 if (i != end)
73 BOOST_THROW_EXCEPTION(SemVerError());
74 }
75
matches(SemVerVersion const & _version) const76 bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _version) const
77 {
78 if (prefix == Token::BitNot)
79 {
80 MatchComponent comp = *this;
81
82 comp.prefix = Token::GreaterThanOrEqual;
83 if (!comp.matches(_version))
84 return false;
85
86 if (levelsPresent >= 2)
87 comp.levelsPresent = 2;
88 else
89 comp.levelsPresent = 1;
90 comp.prefix = Token::LessThanOrEqual;
91 return comp.matches(_version);
92 }
93 else if (prefix == Token::BitXor)
94 {
95 MatchComponent comp = *this;
96
97 comp.prefix = Token::GreaterThanOrEqual;
98 if (!comp.matches(_version))
99 return false;
100
101 if (comp.version.numbers[0] == 0 && comp.levelsPresent != 1)
102 comp.levelsPresent = 2;
103 else
104 comp.levelsPresent = 1;
105 comp.prefix = Token::LessThanOrEqual;
106 return comp.matches(_version);
107 }
108 else
109 {
110 int cmp = 0;
111 bool didCompare = false;
112 for (unsigned i = 0; i < levelsPresent && cmp == 0; i++)
113 if (version.numbers[i] != std::numeric_limits<unsigned>::max())
114 {
115 didCompare = true;
116 cmp = static_cast<int>(_version.numbers[i] - version.numbers[i]);
117 }
118
119 if (cmp == 0 && !_version.prerelease.empty() && didCompare)
120 cmp = -1;
121
122 switch (prefix)
123 {
124 case Token::Assign:
125 return cmp == 0;
126 case Token::LessThan:
127 return cmp < 0;
128 case Token::LessThanOrEqual:
129 return cmp <= 0;
130 case Token::GreaterThan:
131 return cmp > 0;
132 case Token::GreaterThanOrEqual:
133 return cmp >= 0;
134 default:
135 solAssert(false, "Invalid SemVer expression");
136 }
137 return false;
138 }
139 }
140
matches(SemVerVersion const & _version) const141 bool SemVerMatchExpression::Conjunction::matches(SemVerVersion const& _version) const
142 {
143 for (auto const& component: components)
144 if (!component.matches(_version))
145 return false;
146 return true;
147 }
148
matches(SemVerVersion const & _version) const149 bool SemVerMatchExpression::matches(SemVerVersion const& _version) const
150 {
151 if (!isValid())
152 return false;
153 for (auto const& range: m_disjunction)
154 if (range.matches(_version))
155 return true;
156 return false;
157 }
158
parse()159 optional<SemVerMatchExpression> SemVerMatchExpressionParser::parse()
160 {
161 reset();
162
163 if (m_tokens.empty())
164 return nullopt;
165
166 try
167 {
168 while (true)
169 {
170 parseMatchExpression();
171 if (m_pos >= m_tokens.size())
172 break;
173 if (currentToken() != Token::Or)
174 BOOST_THROW_EXCEPTION(SemVerError());
175 nextToken();
176 }
177 }
178 catch (SemVerError const&)
179 {
180 reset();
181 return nullopt;
182 }
183
184 return m_expression;
185 }
186
187
reset()188 void SemVerMatchExpressionParser::reset()
189 {
190 m_expression = SemVerMatchExpression();
191 m_pos = 0;
192 m_posInside = 0;
193 }
194
parseMatchExpression()195 void SemVerMatchExpressionParser::parseMatchExpression()
196 {
197 // component - component (range)
198 // or component component* (conjunction)
199
200 SemVerMatchExpression::Conjunction range;
201 range.components.push_back(parseMatchComponent());
202 if (currentToken() == Token::Sub)
203 {
204 range.components[0].prefix = Token::GreaterThanOrEqual;
205 nextToken();
206 range.components.push_back(parseMatchComponent());
207 range.components[1].prefix = Token::LessThanOrEqual;
208 }
209 else
210 while (currentToken() != Token::Or && currentToken() != Token::Illegal)
211 range.components.push_back(parseMatchComponent());
212 m_expression.m_disjunction.push_back(range);
213 }
214
parseMatchComponent()215 SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchComponent()
216 {
217 SemVerMatchExpression::MatchComponent component;
218 Token token = currentToken();
219
220 switch (token)
221 {
222 case Token::BitXor:
223 case Token::BitNot:
224 case Token::LessThan:
225 case Token::LessThanOrEqual:
226 case Token::GreaterThan:
227 case Token::GreaterThanOrEqual:
228 case Token::Assign:
229 component.prefix = token;
230 nextToken();
231 break;
232 default:
233 component.prefix = Token::Assign;
234 }
235
236 component.levelsPresent = 0;
237 while (component.levelsPresent < 3)
238 {
239 component.version.numbers[component.levelsPresent] = parseVersionPart();
240 component.levelsPresent++;
241 if (currentChar() == '.')
242 nextChar();
243 else
244 break;
245 }
246 // TODO we do not support pre and build version qualifiers for now in match expressions
247 // (but we do support them in the actual versions)
248 return component;
249 }
250
parseVersionPart()251 unsigned SemVerMatchExpressionParser::parseVersionPart()
252 {
253 auto startPos = m_pos;
254 char c = currentChar();
255 nextChar();
256 if (c == 'x' || c == 'X' || c == '*')
257 return unsigned(-1);
258 else if (c == '0')
259 return 0;
260 else if ('1' <= c && c <= '9')
261 {
262 auto v = static_cast<unsigned>(c - '0');
263 // If we skip to the next token, the current number is terminated.
264 while (m_pos == startPos && '0' <= currentChar() && currentChar() <= '9')
265 {
266 c = currentChar();
267 if (v * 10 < v || v * 10 + static_cast<unsigned>(c - '0') < v * 10)
268 BOOST_THROW_EXCEPTION(SemVerError());
269 v = v * 10 + static_cast<unsigned>(c - '0');
270 nextChar();
271 }
272 return v;
273 }
274 else
275 BOOST_THROW_EXCEPTION(SemVerError());
276 }
277
currentChar() const278 char SemVerMatchExpressionParser::currentChar() const
279 {
280 if (m_pos >= m_literals.size())
281 return char(-1);
282 if (m_posInside >= m_literals[m_pos].size())
283 return char(-1);
284 return m_literals[m_pos][m_posInside];
285 }
286
nextChar()287 char SemVerMatchExpressionParser::nextChar()
288 {
289 if (m_pos < m_literals.size())
290 {
291 if (m_posInside + 1 >= m_literals[m_pos].size())
292 nextToken();
293 else
294 ++m_posInside;
295 }
296 return currentChar();
297 }
298
currentToken() const299 Token SemVerMatchExpressionParser::currentToken() const
300 {
301 if (m_pos < m_tokens.size())
302 return m_tokens[m_pos];
303 else
304 return Token::Illegal;
305 }
306
nextToken()307 void SemVerMatchExpressionParser::nextToken()
308 {
309 ++m_pos;
310 m_posInside = 0;
311 }
312