/* This file is part of solidity. solidity 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. solidity 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 solidity. If not, see . */ /** * @author Christian * @date 2016 * Solidity inline assembly parser. */ #include #include #include #include #include #include #include using namespace std; using namespace dev; using namespace langutil; using namespace yul; shared_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; _scanner->supportPeriodInIdentifier(true); ScopeGuard resetScanner([&]{ _scanner->supportPeriodInIdentifier(false); }); try { m_scanner = _scanner; auto block = make_shared(parseBlock()); if (!_reuseScanner) expectToken(Token::EOS); return block; } catch (FatalError const&) { solAssert(!m_errorReporter.errors().empty(), "Fatal error detected, but no error is reported."); } return nullptr; } std::map const& Parser::instructions() { // Allowed instructions, lowercase names. static map s_instructions; if (s_instructions.empty()) { for (auto const& instruction: dev::eth::c_instructions) { if ( instruction.second == dev::eth::Instruction::JUMPDEST || dev::eth::isPushInstruction(instruction.second) ) continue; string name = instruction.first; transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); s_instructions[name] = instruction.second; } } return s_instructions; } Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); Block block = createWithLocation(); expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); block.location.end = endPosition(); advance(); return block; } Statement Parser::parseStatement() { RecursionGuard recursionGuard(*this); switch (currentToken()) { case Token::Let: return parseVariableDeclaration(); case Token::Function: return parseFunctionDefinition(); case Token::LBrace: return parseBlock(); case Token::If: { If _if = createWithLocation(); advance(); _if.condition = make_unique(parseExpression()); _if.body = parseBlock(); return Statement{move(_if)}; } case Token::Switch: { Switch _switch = createWithLocation(); advance(); _switch.expression = make_unique(parseExpression()); while (currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); if (currentToken() == Token::Default) _switch.cases.emplace_back(parseCase()); if (currentToken() == Token::Default) fatalParserError("Only one default case allowed."); else if (currentToken() == Token::Case) fatalParserError("Case not allowed after default case."); if (_switch.cases.empty()) fatalParserError("Switch statement without any cases."); _switch.location.end = _switch.cases.back().body.location.end; return Statement{move(_switch)}; } case Token::For: return parseForLoop(); case Token::Break: { Statement stmt{createWithLocation()}; checkBreakContinuePosition("break"); m_scanner->next(); return stmt; } case Token::Continue: { Statement stmt{createWithLocation()}; checkBreakContinuePosition("continue"); m_scanner->next(); return stmt; } case Token::Assign: { if (m_dialect.flavour != AsmFlavour::Loose) break; StackAssignment assignment = createWithLocation(); advance(); expectToken(Token::Colon); assignment.variableName.location = location(); assignment.variableName.name = YulString(currentLiteral()); if (m_dialect.builtin(assignment.variableName.name)) fatalParserError("Identifier expected, got builtin symbol."); else if (instructions().count(assignment.variableName.name.str())) fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); return Statement{move(assignment)}; } default: break; } // Options left: // Simple instruction (might turn into functional), // literal, // identifier (might turn into label or functional assignment) ElementaryOperation elementary(parseElementaryOperation()); switch (currentToken()) { case Token::LParen: { Expression expr = parseCall(std::move(elementary)); return ExpressionStatement{locationOf(expr), expr}; } case Token::Comma: case Token::AssemblyAssign: { std::vector variableNames; while (true) { if (elementary.type() != typeid(Identifier)) { auto const token = currentToken() == Token::Comma ? "," : ":="; fatalParserError( std::string("Variable name must precede \"") + token + "\"" + (currentToken() == Token::Comma ? " in multiple assignment." : " in assignment.") ); } auto const& identifier = boost::get(elementary); if (m_dialect.builtin(identifier.name)) fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\"."); variableNames.emplace_back(identifier); if (currentToken() != Token::Comma) break; expectToken(Token::Comma); elementary = parseElementaryOperation(); } Assignment assignment = createWithLocation(boost::get(elementary).location); assignment.variableNames = std::move(variableNames); expectToken(Token::AssemblyAssign); assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return Statement{std::move(assignment)}; } case Token::Colon: { if (elementary.type() != typeid(Identifier)) fatalParserError("Label name must precede \":\"."); Identifier const& identifier = boost::get(elementary); advance(); // label if (m_dialect.flavour != AsmFlavour::Loose) fatalParserError("Labels are not supported."); Label label = createWithLocation