/src/solidity/libyul/AsmParser.cpp
Line | Count | Source (jump to first uncovered line) |
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 <c@ethdev.com> |
20 | | * @date 2016 |
21 | | * Solidity inline assembly parser. |
22 | | */ |
23 | | |
24 | | #include <libyul/AST.h> |
25 | | #include <libyul/AsmParser.h> |
26 | | #include <libyul/Exceptions.h> |
27 | | #include <liblangutil/ErrorReporter.h> |
28 | | #include <liblangutil/Exceptions.h> |
29 | | #include <liblangutil/Scanner.h> |
30 | | #include <libsolutil/Common.h> |
31 | | #include <libsolutil/Visitor.h> |
32 | | |
33 | | #include <range/v3/view/subrange.hpp> |
34 | | |
35 | | #include <boost/algorithm/string.hpp> |
36 | | |
37 | | #include <algorithm> |
38 | | #include <regex> |
39 | | |
40 | | using namespace std; |
41 | | using namespace solidity; |
42 | | using namespace solidity::util; |
43 | | using namespace solidity::langutil; |
44 | | using namespace solidity::yul; |
45 | | |
46 | | namespace |
47 | | { |
48 | | |
49 | | optional<int> toInt(string const& _value) |
50 | 0 | { |
51 | 0 | try |
52 | 0 | { |
53 | 0 | return stoi(_value); |
54 | 0 | } |
55 | 0 | catch (...) |
56 | 0 | { |
57 | 0 | return nullopt; |
58 | 0 | } |
59 | 0 | } |
60 | | |
61 | | } |
62 | | |
63 | | std::shared_ptr<DebugData const> Parser::createDebugData() const |
64 | 1.35M | { |
65 | 1.35M | switch (m_useSourceLocationFrom) |
66 | 1.35M | { |
67 | 1.35M | case UseSourceLocationFrom::Scanner: |
68 | 1.35M | return DebugData::create(ParserBase::currentLocation(), ParserBase::currentLocation()); |
69 | 0 | case UseSourceLocationFrom::LocationOverride: |
70 | 0 | return DebugData::create(m_locationOverride, m_locationOverride); |
71 | 0 | case UseSourceLocationFrom::Comments: |
72 | 0 | return DebugData::create(ParserBase::currentLocation(), m_locationFromComment, m_astIDFromComment); |
73 | 1.35M | } |
74 | 0 | solAssert(false, ""); |
75 | 0 | } |
76 | | |
77 | | void Parser::updateLocationEndFrom( |
78 | | shared_ptr<DebugData const>& _debugData, |
79 | | SourceLocation const& _location |
80 | | ) const |
81 | 561k | { |
82 | 561k | solAssert(_debugData, ""); |
83 | | |
84 | 561k | switch (m_useSourceLocationFrom) |
85 | 561k | { |
86 | 561k | case UseSourceLocationFrom::Scanner: |
87 | 561k | { |
88 | 561k | DebugData updatedDebugData = *_debugData; |
89 | 561k | updatedDebugData.nativeLocation.end = _location.end; |
90 | 561k | updatedDebugData.originLocation.end = _location.end; |
91 | 561k | _debugData = make_shared<DebugData const>(move(updatedDebugData)); |
92 | 561k | break; |
93 | 0 | } |
94 | 0 | case UseSourceLocationFrom::LocationOverride: |
95 | | // Ignore the update. The location we're overriding with is not supposed to change |
96 | 0 | break; |
97 | 0 | case UseSourceLocationFrom::Comments: |
98 | 0 | { |
99 | 0 | DebugData updatedDebugData = *_debugData; |
100 | 0 | updatedDebugData.nativeLocation.end = _location.end; |
101 | 0 | _debugData = make_shared<DebugData const>(move(updatedDebugData)); |
102 | 0 | break; |
103 | 0 | } |
104 | 561k | } |
105 | 561k | } |
106 | | |
107 | | unique_ptr<Block> Parser::parse(CharStream& _charStream) |
108 | 0 | { |
109 | 0 | m_scanner = make_shared<Scanner>(_charStream); |
110 | 0 | unique_ptr<Block> block = parseInline(m_scanner); |
111 | 0 | expectToken(Token::EOS); |
112 | 0 | return block; |
113 | 0 | } |
114 | | |
115 | | unique_ptr<Block> Parser::parseInline(std::shared_ptr<Scanner> const& _scanner) |
116 | 19.5k | { |
117 | 19.5k | m_recursionDepth = 0; |
118 | | |
119 | 19.5k | _scanner->setScannerMode(ScannerKind::Yul); |
120 | 19.5k | ScopeGuard resetScanner([&]{ _scanner->setScannerMode(ScannerKind::Solidity); }); |
121 | | |
122 | 19.5k | try |
123 | 19.5k | { |
124 | 19.5k | m_scanner = _scanner; |
125 | 19.5k | if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments) |
126 | 0 | fetchDebugDataFromComment(); |
127 | 19.5k | return make_unique<Block>(parseBlock()); |
128 | 19.5k | } |
129 | 19.5k | catch (FatalError const&) |
130 | 19.5k | { |
131 | 0 | yulAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); |
132 | 0 | } |
133 | | |
134 | 0 | return nullptr; |
135 | 19.5k | } |
136 | | |
137 | | langutil::Token Parser::advance() |
138 | 2.76M | { |
139 | 2.76M | auto const token = ParserBase::advance(); |
140 | 2.76M | if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments) |
141 | 0 | fetchDebugDataFromComment(); |
142 | 2.76M | return token; |
143 | 2.76M | } |
144 | | |
145 | | void Parser::fetchDebugDataFromComment() |
146 | 0 | { |
147 | 0 | solAssert(m_sourceNames.has_value(), ""); |
148 | | |
149 | 0 | static regex const tagRegex = regex( |
150 | 0 | R"~~((?:^|\s+)(@[a-zA-Z0-9\-_]+)(?:\s+|$))~~", // tag, e.g: @src |
151 | 0 | regex_constants::ECMAScript | regex_constants::optimize |
152 | 0 | ); |
153 | |
|
154 | 0 | string_view commentLiteral = m_scanner->currentCommentLiteral(); |
155 | 0 | match_results<string_view::const_iterator> match; |
156 | |
|
157 | 0 | langutil::SourceLocation originLocation = m_locationFromComment; |
158 | | // Empty for each new node. |
159 | 0 | optional<int> astID; |
160 | |
|
161 | 0 | while (regex_search(commentLiteral.cbegin(), commentLiteral.cend(), match, tagRegex)) |
162 | 0 | { |
163 | 0 | solAssert(match.size() == 2, ""); |
164 | 0 | commentLiteral = commentLiteral.substr(static_cast<size_t>(match.position() + match.length())); |
165 | |
|
166 | 0 | if (match[1] == "@src") |
167 | 0 | { |
168 | 0 | if (auto parseResult = parseSrcComment(commentLiteral, m_scanner->currentCommentLocation())) |
169 | 0 | tie(commentLiteral, originLocation) = *parseResult; |
170 | 0 | else |
171 | 0 | break; |
172 | 0 | } |
173 | 0 | else if (match[1] == "@ast-id") |
174 | 0 | { |
175 | 0 | if (auto parseResult = parseASTIDComment(commentLiteral, m_scanner->currentCommentLocation())) |
176 | 0 | tie(commentLiteral, astID) = *parseResult; |
177 | 0 | else |
178 | 0 | break; |
179 | 0 | } |
180 | 0 | else |
181 | | // Ignore unrecognized tags. |
182 | 0 | continue; |
183 | 0 | } |
184 | | |
185 | 0 | m_locationFromComment = originLocation; |
186 | 0 | m_astIDFromComment = astID; |
187 | 0 | } |
188 | | |
189 | | optional<pair<string_view, SourceLocation>> Parser::parseSrcComment( |
190 | | string_view const _arguments, |
191 | | langutil::SourceLocation const& _commentLocation |
192 | | ) |
193 | 0 | { |
194 | 0 | static regex const argsRegex = regex( |
195 | 0 | R"~~(^(-1|\d+):(-1|\d+):(-1|\d+)(?:\s+|$))~~" // index and location, e.g.: 1:234:-1 |
196 | 0 | R"~~(("(?:[^"\\]|\\.)*"?)?)~~", // optional code snippet, e.g.: "string memory s = \"abc\";..." |
197 | 0 | regex_constants::ECMAScript | regex_constants::optimize |
198 | 0 | ); |
199 | 0 | match_results<string_view::const_iterator> match; |
200 | 0 | if (!regex_search(_arguments.cbegin(), _arguments.cend(), match, argsRegex)) |
201 | 0 | { |
202 | 0 | m_errorReporter.syntaxError( |
203 | 0 | 8387_error, |
204 | 0 | _commentLocation, |
205 | 0 | "Invalid values in source location mapping. Could not parse location specification." |
206 | 0 | ); |
207 | 0 | return nullopt; |
208 | 0 | } |
209 | | |
210 | 0 | solAssert(match.size() == 5, ""); |
211 | 0 | string_view tail = _arguments.substr(static_cast<size_t>(match.position() + match.length())); |
212 | |
|
213 | 0 | if (match[4].matched && ( |
214 | 0 | !boost::algorithm::ends_with(match[4].str(), "\"") || |
215 | 0 | boost::algorithm::ends_with(match[4].str(), "\\\"") |
216 | 0 | )) |
217 | 0 | { |
218 | 0 | m_errorReporter.syntaxError( |
219 | 0 | 1544_error, |
220 | 0 | _commentLocation, |
221 | 0 | "Invalid code snippet in source location mapping. Quote is not terminated." |
222 | 0 | ); |
223 | 0 | return {{tail, SourceLocation{}}}; |
224 | 0 | } |
225 | | |
226 | 0 | optional<int> const sourceIndex = toInt(match[1].str()); |
227 | 0 | optional<int> const start = toInt(match[2].str()); |
228 | 0 | optional<int> const end = toInt(match[3].str()); |
229 | |
|
230 | 0 | if (!sourceIndex.has_value() || !start.has_value() || !end.has_value()) |
231 | 0 | m_errorReporter.syntaxError( |
232 | 0 | 6367_error, |
233 | 0 | _commentLocation, |
234 | 0 | "Invalid value in source location mapping. " |
235 | 0 | "Expected non-negative integer values or -1 for source index and location." |
236 | 0 | ); |
237 | 0 | else if (sourceIndex == -1) |
238 | 0 | return {{tail, SourceLocation{start.value(), end.value(), nullptr}}}; |
239 | 0 | else if (!(sourceIndex >= 0 && m_sourceNames->count(static_cast<unsigned>(sourceIndex.value())))) |
240 | 0 | m_errorReporter.syntaxError( |
241 | 0 | 2674_error, |
242 | 0 | _commentLocation, |
243 | 0 | "Invalid source mapping. Source index not defined via @use-src." |
244 | 0 | ); |
245 | 0 | else |
246 | 0 | { |
247 | 0 | shared_ptr<string const> sourceName = m_sourceNames->at(static_cast<unsigned>(sourceIndex.value())); |
248 | 0 | solAssert(sourceName, ""); |
249 | 0 | return {{tail, SourceLocation{start.value(), end.value(), move(sourceName)}}}; |
250 | 0 | } |
251 | 0 | return {{tail, SourceLocation{}}}; |
252 | 0 | } |
253 | | |
254 | | optional<pair<string_view, optional<int>>> Parser::parseASTIDComment( |
255 | | string_view _arguments, |
256 | | langutil::SourceLocation const& _commentLocation |
257 | | ) |
258 | 0 | { |
259 | 0 | static regex const argRegex = regex( |
260 | 0 | R"~~(^(\d+)(?:\s|$))~~", |
261 | 0 | regex_constants::ECMAScript | regex_constants::optimize |
262 | 0 | ); |
263 | 0 | match_results<string_view::const_iterator> match; |
264 | 0 | optional<int> astID; |
265 | 0 | bool matched = regex_search(_arguments.cbegin(), _arguments.cend(), match, argRegex); |
266 | 0 | string_view tail = _arguments; |
267 | 0 | if (matched) |
268 | 0 | { |
269 | 0 | solAssert(match.size() == 2, ""); |
270 | 0 | tail = _arguments.substr(static_cast<size_t>(match.position() + match.length())); |
271 | |
|
272 | 0 | astID = toInt(match[1].str()); |
273 | 0 | } |
274 | | |
275 | 0 | if (!matched || !astID || *astID < 0 || static_cast<int64_t>(*astID) != *astID) |
276 | 0 | { |
277 | 0 | m_errorReporter.syntaxError(1749_error, _commentLocation, "Invalid argument for @ast-id."); |
278 | 0 | astID = nullopt; |
279 | 0 | } |
280 | 0 | if (matched) |
281 | 0 | return {{_arguments, astID}}; |
282 | 0 | else |
283 | 0 | return nullopt; |
284 | 0 | } |
285 | | |
286 | | Block Parser::parseBlock() |
287 | 119k | { |
288 | 119k | RecursionGuard recursionGuard(*this); |
289 | 119k | Block block = createWithLocation<Block>(); |
290 | 119k | expectToken(Token::LBrace); |
291 | 370k | while (currentToken() != Token::RBrace) |
292 | 251k | block.statements.emplace_back(parseStatement()); |
293 | 119k | updateLocationEndFrom(block.debugData, currentLocation()); |
294 | 119k | advance(); |
295 | 119k | return block; |
296 | 119k | } |
297 | | |
298 | | Statement Parser::parseStatement() |
299 | 251k | { |
300 | 251k | RecursionGuard recursionGuard(*this); |
301 | 251k | switch (currentToken()) |
302 | 251k | { |
303 | 46.2k | case Token::Let: |
304 | 46.2k | return parseVariableDeclaration(); |
305 | 30.7k | case Token::Function: |
306 | 30.7k | return parseFunctionDefinition(); |
307 | 22.5k | case Token::LBrace: |
308 | 22.5k | return parseBlock(); |
309 | 2.26k | case Token::If: |
310 | 2.26k | { |
311 | 2.26k | If _if = createWithLocation<If>(); |
312 | 2.26k | advance(); |
313 | 2.26k | _if.condition = make_unique<Expression>(parseExpression()); |
314 | 2.26k | _if.body = parseBlock(); |
315 | 2.26k | updateLocationEndFrom(_if.debugData, nativeLocationOf(_if.body)); |
316 | 2.26k | return Statement{move(_if)}; |
317 | 0 | } |
318 | 7.56k | case Token::Switch: |
319 | 7.56k | { |
320 | 7.56k | Switch _switch = createWithLocation<Switch>(); |
321 | 7.56k | advance(); |
322 | 7.56k | _switch.expression = make_unique<Expression>(parseExpression()); |
323 | 25.1k | while (currentToken() == Token::Case) |
324 | 17.6k | _switch.cases.emplace_back(parseCase()); |
325 | 7.56k | if (currentToken() == Token::Default) |
326 | 5.85k | _switch.cases.emplace_back(parseCase()); |
327 | 7.56k | if (currentToken() == Token::Default) |
328 | 0 | fatalParserError(6931_error, "Only one default case allowed."); |
329 | 7.56k | else if (currentToken() == Token::Case) |
330 | 0 | fatalParserError(4904_error, "Case not allowed after default case."); |
331 | 7.56k | if (_switch.cases.empty()) |
332 | 0 | fatalParserError(2418_error, "Switch statement without any cases."); |
333 | 7.56k | updateLocationEndFrom(_switch.debugData, nativeLocationOf(_switch.cases.back().body)); |
334 | 7.56k | return Statement{move(_switch)}; |
335 | 0 | } |
336 | 6.97k | case Token::For: |
337 | 6.97k | return parseForLoop(); |
338 | 503 | case Token::Break: |
339 | 503 | { |
340 | 503 | Statement stmt{createWithLocation<Break>()}; |
341 | 503 | checkBreakContinuePosition("break"); |
342 | 503 | advance(); |
343 | 503 | return stmt; |
344 | 0 | } |
345 | 729 | case Token::Continue: |
346 | 729 | { |
347 | 729 | Statement stmt{createWithLocation<Continue>()}; |
348 | 729 | checkBreakContinuePosition("continue"); |
349 | 729 | advance(); |
350 | 729 | return stmt; |
351 | 0 | } |
352 | 1.28k | case Token::Leave: |
353 | 1.28k | { |
354 | 1.28k | Statement stmt{createWithLocation<Leave>()}; |
355 | 1.28k | if (!m_insideFunction) |
356 | 0 | m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); |
357 | 1.28k | advance(); |
358 | 1.28k | return stmt; |
359 | 0 | } |
360 | 132k | default: |
361 | 132k | break; |
362 | 251k | } |
363 | | |
364 | | // Options left: |
365 | | // Expression/FunctionCall |
366 | | // Assignment |
367 | 132k | variant<Literal, Identifier> elementary(parseLiteralOrIdentifier()); |
368 | | |
369 | 132k | switch (currentToken()) |
370 | 132k | { |
371 | 119k | case Token::LParen: |
372 | 119k | { |
373 | 119k | Expression expr = parseCall(std::move(elementary)); |
374 | 119k | return ExpressionStatement{debugDataOf(expr), move(expr)}; |
375 | 0 | } |
376 | 0 | case Token::Comma: |
377 | 12.2k | case Token::AssemblyAssign: |
378 | 12.2k | { |
379 | 12.2k | Assignment assignment; |
380 | 12.2k | assignment.debugData = debugDataOf(elementary); |
381 | | |
382 | 12.2k | while (true) |
383 | 12.2k | { |
384 | 12.2k | if (!holds_alternative<Identifier>(elementary)) |
385 | 0 | { |
386 | 0 | auto const token = currentToken() == Token::Comma ? "," : ":="; |
387 | |
|
388 | 0 | fatalParserError( |
389 | 0 | 2856_error, |
390 | 0 | std::string("Variable name must precede \"") + |
391 | 0 | token + |
392 | 0 | "\"" + |
393 | 0 | (currentToken() == Token::Comma ? " in multiple assignment." : " in assignment.") |
394 | 0 | ); |
395 | 0 | } |
396 | | |
397 | 12.2k | auto const& identifier = std::get<Identifier>(elementary); |
398 | | |
399 | 12.2k | if (m_dialect.builtin(identifier.name)) |
400 | 0 | fatalParserError(6272_error, "Cannot assign to builtin function \"" + identifier.name.str() + "\"."); |
401 | | |
402 | 12.2k | assignment.variableNames.emplace_back(identifier); |
403 | | |
404 | 12.2k | if (currentToken() != Token::Comma) |
405 | 12.2k | break; |
406 | | |
407 | 0 | expectToken(Token::Comma); |
408 | |
|
409 | 0 | elementary = parseLiteralOrIdentifier(); |
410 | 0 | } |
411 | | |
412 | 12.2k | expectToken(Token::AssemblyAssign); |
413 | | |
414 | 12.2k | assignment.value = make_unique<Expression>(parseExpression()); |
415 | 12.2k | updateLocationEndFrom(assignment.debugData, nativeLocationOf(*assignment.value)); |
416 | | |
417 | 12.2k | return Statement{move(assignment)}; |
418 | 0 | } |
419 | 0 | default: |
420 | 0 | fatalParserError(6913_error, "Call or assignment expected."); |
421 | 0 | break; |
422 | 132k | } |
423 | | |
424 | 0 | yulAssert(false, ""); |
425 | 0 | return {}; |
426 | 0 | } |
427 | | |
428 | | Case Parser::parseCase() |
429 | 23.4k | { |
430 | 23.4k | RecursionGuard recursionGuard(*this); |
431 | 23.4k | Case _case = createWithLocation<Case>(); |
432 | 23.4k | if (currentToken() == Token::Default) |
433 | 5.85k | advance(); |
434 | 17.6k | else if (currentToken() == Token::Case) |
435 | 17.6k | { |
436 | 17.6k | advance(); |
437 | 17.6k | variant<Literal, Identifier> literal = parseLiteralOrIdentifier(); |
438 | 17.6k | if (!holds_alternative<Literal>(literal)) |
439 | 0 | fatalParserError(4805_error, "Literal expected."); |
440 | 17.6k | _case.value = make_unique<Literal>(std::get<Literal>(std::move(literal))); |
441 | 17.6k | } |
442 | 0 | else |
443 | 17.6k | yulAssert(false, "Case or default case expected."); |
444 | 23.4k | _case.body = parseBlock(); |
445 | 23.4k | updateLocationEndFrom(_case.debugData, nativeLocationOf(_case.body)); |
446 | 23.4k | return _case; |
447 | 23.4k | } |
448 | | |
449 | | ForLoop Parser::parseForLoop() |
450 | 6.97k | { |
451 | 6.97k | RecursionGuard recursionGuard(*this); |
452 | | |
453 | 6.97k | ForLoopComponent outerForLoopComponent = m_currentForLoopComponent; |
454 | | |
455 | 6.97k | ForLoop forLoop = createWithLocation<ForLoop>(); |
456 | 6.97k | expectToken(Token::For); |
457 | 6.97k | m_currentForLoopComponent = ForLoopComponent::ForLoopPre; |
458 | 6.97k | forLoop.pre = parseBlock(); |
459 | 6.97k | m_currentForLoopComponent = ForLoopComponent::None; |
460 | 6.97k | forLoop.condition = make_unique<Expression>(parseExpression()); |
461 | 6.97k | m_currentForLoopComponent = ForLoopComponent::ForLoopPost; |
462 | 6.97k | forLoop.post = parseBlock(); |
463 | 6.97k | m_currentForLoopComponent = ForLoopComponent::ForLoopBody; |
464 | 6.97k | forLoop.body = parseBlock(); |
465 | 6.97k | updateLocationEndFrom(forLoop.debugData, nativeLocationOf(forLoop.body)); |
466 | | |
467 | 6.97k | m_currentForLoopComponent = outerForLoopComponent; |
468 | | |
469 | 6.97k | return forLoop; |
470 | 6.97k | } |
471 | | |
472 | | Expression Parser::parseExpression() |
473 | 681k | { |
474 | 681k | RecursionGuard recursionGuard(*this); |
475 | | |
476 | 681k | variant<Literal, Identifier> operation = parseLiteralOrIdentifier(); |
477 | 681k | return visit(GenericVisitor{ |
478 | 681k | [&](Identifier& _identifier) -> Expression |
479 | 681k | { |
480 | 298k | if (currentToken() == Token::LParen) |
481 | 192k | return parseCall(std::move(operation)); |
482 | 105k | if (m_dialect.builtin(_identifier.name)) |
483 | 0 | fatalParserError( |
484 | 0 | 7104_error, |
485 | 0 | nativeLocationOf(_identifier), |
486 | 0 | "Builtin function \"" + _identifier.name.str() + "\" must be called." |
487 | 0 | ); |
488 | 105k | return move(_identifier); |
489 | 298k | }, |
490 | 681k | [&](Literal& _literal) -> Expression |
491 | 681k | { |
492 | 383k | return move(_literal); |
493 | 383k | } |
494 | 681k | }, operation); |
495 | 681k | } |
496 | | |
497 | | variant<Literal, Identifier> Parser::parseLiteralOrIdentifier() |
498 | 831k | { |
499 | 831k | RecursionGuard recursionGuard(*this); |
500 | 831k | switch (currentToken()) |
501 | 831k | { |
502 | 430k | case Token::Identifier: |
503 | 430k | { |
504 | 430k | Identifier identifier{createDebugData(), YulString{currentLiteral()}}; |
505 | 430k | advance(); |
506 | 430k | return identifier; |
507 | 0 | } |
508 | 4.84k | case Token::StringLiteral: |
509 | 4.84k | case Token::HexStringLiteral: |
510 | 398k | case Token::Number: |
511 | 400k | case Token::TrueLiteral: |
512 | 400k | case Token::FalseLiteral: |
513 | 400k | { |
514 | 400k | LiteralKind kind = LiteralKind::Number; |
515 | 400k | switch (currentToken()) |
516 | 400k | { |
517 | 4.84k | case Token::StringLiteral: |
518 | 4.84k | case Token::HexStringLiteral: |
519 | 4.84k | kind = LiteralKind::String; |
520 | 4.84k | break; |
521 | 393k | case Token::Number: |
522 | 393k | if (!isValidNumberLiteral(currentLiteral())) |
523 | 0 | fatalParserError(4828_error, "Invalid number literal."); |
524 | 393k | kind = LiteralKind::Number; |
525 | 393k | break; |
526 | 2.22k | case Token::TrueLiteral: |
527 | 2.45k | case Token::FalseLiteral: |
528 | 2.45k | kind = LiteralKind::Boolean; |
529 | 2.45k | break; |
530 | 0 | default: |
531 | 0 | break; |
532 | 400k | } |
533 | | |
534 | 400k | Literal literal{ |
535 | 400k | createDebugData(), |
536 | 400k | kind, |
537 | 400k | YulString{currentLiteral()}, |
538 | 400k | kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType |
539 | 400k | }; |
540 | 400k | advance(); |
541 | 400k | if (currentToken() == Token::Colon) |
542 | 0 | { |
543 | 0 | expectToken(Token::Colon); |
544 | 0 | updateLocationEndFrom(literal.debugData, currentLocation()); |
545 | 0 | literal.type = expectAsmIdentifier(); |
546 | 0 | } |
547 | | |
548 | 400k | return literal; |
549 | 400k | } |
550 | 0 | case Token::Illegal: |
551 | 0 | fatalParserError(1465_error, "Illegal token: " + to_string(m_scanner->currentError())); |
552 | 0 | break; |
553 | 0 | default: |
554 | 0 | fatalParserError(1856_error, "Literal or identifier expected."); |
555 | 831k | } |
556 | 0 | return {}; |
557 | 831k | } |
558 | | |
559 | | VariableDeclaration Parser::parseVariableDeclaration() |
560 | 46.2k | { |
561 | 46.2k | RecursionGuard recursionGuard(*this); |
562 | 46.2k | VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); |
563 | 46.2k | expectToken(Token::Let); |
564 | 121k | while (true) |
565 | 121k | { |
566 | 121k | varDecl.variables.emplace_back(parseTypedName()); |
567 | 121k | if (currentToken() == Token::Comma) |
568 | 75.0k | expectToken(Token::Comma); |
569 | 46.2k | else |
570 | 46.2k | break; |
571 | 121k | } |
572 | 46.2k | if (currentToken() == Token::AssemblyAssign) |
573 | 41.6k | { |
574 | 41.6k | expectToken(Token::AssemblyAssign); |
575 | 41.6k | varDecl.value = make_unique<Expression>(parseExpression()); |
576 | 41.6k | updateLocationEndFrom(varDecl.debugData, nativeLocationOf(*varDecl.value)); |
577 | 41.6k | } |
578 | 4.66k | else |
579 | 4.66k | updateLocationEndFrom(varDecl.debugData, nativeLocationOf(varDecl.variables.back())); |
580 | | |
581 | 46.2k | return varDecl; |
582 | 46.2k | } |
583 | | |
584 | | FunctionDefinition Parser::parseFunctionDefinition() |
585 | 30.7k | { |
586 | 30.7k | RecursionGuard recursionGuard(*this); |
587 | | |
588 | 30.7k | if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre) |
589 | 0 | m_errorReporter.syntaxError( |
590 | 0 | 3441_error, |
591 | 0 | currentLocation(), |
592 | 0 | "Functions cannot be defined inside a for-loop init block." |
593 | 0 | ); |
594 | | |
595 | 30.7k | ForLoopComponent outerForLoopComponent = m_currentForLoopComponent; |
596 | 30.7k | m_currentForLoopComponent = ForLoopComponent::None; |
597 | | |
598 | 30.7k | FunctionDefinition funDef = createWithLocation<FunctionDefinition>(); |
599 | 30.7k | expectToken(Token::Function); |
600 | 30.7k | funDef.name = expectAsmIdentifier(); |
601 | 30.7k | expectToken(Token::LParen); |
602 | 81.8k | while (currentToken() != Token::RParen) |
603 | 78.4k | { |
604 | 78.4k | funDef.parameters.emplace_back(parseTypedName()); |
605 | 78.4k | if (currentToken() == Token::RParen) |
606 | 27.2k | break; |
607 | 51.1k | expectToken(Token::Comma); |
608 | 51.1k | } |
609 | 30.7k | expectToken(Token::RParen); |
610 | 30.7k | if (currentToken() == Token::RightArrow) |
611 | 24.8k | { |
612 | 24.8k | expectToken(Token::RightArrow); |
613 | 81.0k | while (true) |
614 | 81.0k | { |
615 | 81.0k | funDef.returnVariables.emplace_back(parseTypedName()); |
616 | 81.0k | if (currentToken() == Token::LBrace) |
617 | 24.8k | break; |
618 | 56.2k | expectToken(Token::Comma); |
619 | 56.2k | } |
620 | 24.8k | } |
621 | 30.7k | bool preInsideFunction = m_insideFunction; |
622 | 30.7k | m_insideFunction = true; |
623 | 30.7k | funDef.body = parseBlock(); |
624 | 30.7k | m_insideFunction = preInsideFunction; |
625 | 30.7k | updateLocationEndFrom(funDef.debugData, nativeLocationOf(funDef.body)); |
626 | | |
627 | 30.7k | m_currentForLoopComponent = outerForLoopComponent; |
628 | 30.7k | return funDef; |
629 | 30.7k | } |
630 | | |
631 | | FunctionCall Parser::parseCall(variant<Literal, Identifier>&& _initialOp) |
632 | 312k | { |
633 | 312k | RecursionGuard recursionGuard(*this); |
634 | | |
635 | 312k | if (!holds_alternative<Identifier>(_initialOp)) |
636 | 0 | fatalParserError(9980_error, "Function name expected."); |
637 | | |
638 | 312k | FunctionCall ret; |
639 | 312k | ret.functionName = std::move(std::get<Identifier>(_initialOp)); |
640 | 312k | ret.debugData = ret.functionName.debugData; |
641 | | |
642 | 312k | expectToken(Token::LParen); |
643 | 312k | if (currentToken() != Token::RParen) |
644 | 297k | { |
645 | 297k | ret.arguments.emplace_back(parseExpression()); |
646 | 610k | while (currentToken() != Token::RParen) |
647 | 313k | { |
648 | 313k | expectToken(Token::Comma); |
649 | 313k | ret.arguments.emplace_back(parseExpression()); |
650 | 313k | } |
651 | 297k | } |
652 | 312k | updateLocationEndFrom(ret.debugData, currentLocation()); |
653 | 312k | expectToken(Token::RParen); |
654 | 312k | return ret; |
655 | 312k | } |
656 | | |
657 | | TypedName Parser::parseTypedName() |
658 | 280k | { |
659 | 280k | RecursionGuard recursionGuard(*this); |
660 | 280k | TypedName typedName = createWithLocation<TypedName>(); |
661 | 280k | typedName.name = expectAsmIdentifier(); |
662 | 280k | if (currentToken() == Token::Colon) |
663 | 0 | { |
664 | 0 | expectToken(Token::Colon); |
665 | 0 | updateLocationEndFrom(typedName.debugData, currentLocation()); |
666 | 0 | typedName.type = expectAsmIdentifier(); |
667 | 0 | } |
668 | 280k | else |
669 | 280k | typedName.type = m_dialect.defaultType; |
670 | | |
671 | 280k | return typedName; |
672 | 280k | } |
673 | | |
674 | | YulString Parser::expectAsmIdentifier() |
675 | 311k | { |
676 | 311k | YulString name{currentLiteral()}; |
677 | 311k | if (currentToken() == Token::Identifier && m_dialect.builtin(name)) |
678 | 0 | fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name."); |
679 | | // NOTE: We keep the expectation here to ensure the correct source location for the error above. |
680 | 311k | expectToken(Token::Identifier); |
681 | 311k | return name; |
682 | 311k | } |
683 | | |
684 | | void Parser::checkBreakContinuePosition(string const& _which) |
685 | 1.23k | { |
686 | 1.23k | switch (m_currentForLoopComponent) |
687 | 1.23k | { |
688 | 0 | case ForLoopComponent::None: |
689 | 0 | m_errorReporter.syntaxError(2592_error, currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body."); |
690 | 0 | break; |
691 | 0 | case ForLoopComponent::ForLoopPre: |
692 | 0 | m_errorReporter.syntaxError(9615_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed."); |
693 | 0 | break; |
694 | 0 | case ForLoopComponent::ForLoopPost: |
695 | 0 | m_errorReporter.syntaxError(2461_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed."); |
696 | 0 | break; |
697 | 1.23k | case ForLoopComponent::ForLoopBody: |
698 | 1.23k | break; |
699 | 1.23k | } |
700 | 1.23k | } |
701 | | |
702 | | bool Parser::isValidNumberLiteral(string const& _literal) |
703 | 393k | { |
704 | 393k | try |
705 | 393k | { |
706 | | // Try to convert _literal to u256. |
707 | 393k | [[maybe_unused]] auto tmp = u256(_literal); |
708 | 393k | } |
709 | 393k | catch (...) |
710 | 393k | { |
711 | 0 | return false; |
712 | 0 | } |
713 | 393k | if (boost::starts_with(_literal, "0x")) |
714 | 243k | return true; |
715 | 149k | else |
716 | 149k | return _literal.find_first_not_of("0123456789") == string::npos; |
717 | 393k | } |