/* 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 using namespace std; using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; shared_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; try { m_scanner = _scanner; auto block = make_shared(parseBlock()); if (!_reuseScanner) expectToken(Token::EOS); return block; } catch (FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. } return nullptr; } assembly::Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); assembly::Block block = createWithLocation(); expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); block.location.end = endPosition(); advance(); return block; } assembly::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: { assembly::If _if = createWithLocation(); m_scanner->next(); _if.condition = make_shared(parseExpression()); _if.body = parseBlock(); return _if; } case Token::Switch: { assembly::Switch _switch = createWithLocation(); m_scanner->next(); _switch.expression = make_shared(parseExpression()); while (m_scanner->currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); if (m_scanner->currentToken() == Token::Default) _switch.cases.emplace_back(parseCase()); if (m_scanner->currentToken() == Token::Default) fatalParserError("Only one default case allowed."); else if (m_scanner->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 _switch; } case Token::For: return parseForLoop(); case Token::Assign: { if (m_flavour != AsmFlavour::Loose) break; assembly::StackAssignment assignment = createWithLocation(); advance(); expectToken(Token::Colon); assignment.variableName.location = location(); assignment.variableName.name = currentLiteral(); if (instructions().count(assignment.variableName.name)) fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); return 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: { // if a comma follows, a multiple assignment is assumed if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); assembly::Identifier const& identifier = boost::get(elementary); Assignment assignment = createWithLocation(identifier.location); assignment.variableNames.emplace_back(identifier); do { expectToken(Token::Comma); elementary = parseElementaryOperation(); if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Variable name expected in multiple assignment."); assignment.variableNames.emplace_back(boost::get(elementary)); } while (currentToken() == Token::Comma); expectToken(Token::Colon); expectToken(Token::Assign); assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } case Token::Colon: { if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \":\"."); assembly::Identifier const& identifier = boost::get(elementary); advance(); // identifier:=: should be parsed as identifier: =: (i.e. a label), // while identifier:= (being followed by a non-colon) as identifier := (assignment). if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { assembly::Assignment assignment = createWithLocation(identifier.location); if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name)) fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } else { // label if (m_flavour != AsmFlavour::Loose) fatalParserError("Labels are not supported."); Label label = createWithLocation