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 | |
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')
-rw-r--r-- | libsolidity/analysis/SemVerHandler.cpp | 290 | ||||
-rw-r--r-- | libsolidity/analysis/SemVerHandler.h | 102 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.cpp | 54 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.h | 7 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 28 | ||||
-rw-r--r-- | libsolidity/ast/ASTForward.h | 1 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 13 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.cpp | 12 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTVisitor.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 12 | ||||
-rw-r--r-- | libsolidity/grammar.txt | 11 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 33 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.h | 1 | ||||
-rw-r--r-- | libsolidity/parsing/Token.h | 1 |
16 files changed, 566 insertions, 7 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; +} diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h new file mode 100644 index 00000000..3c110b19 --- /dev/null +++ b/libsolidity/analysis/SemVerHandler.h @@ -0,0 +1,102 @@ +/* + 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. + */ + +#pragma once + +#include <vector> +#include <libsolidity/parsing/Token.h> + +namespace dev +{ +namespace solidity +{ + +class SemVerError: dev::Exception +{ +}; + +struct SemVerVersion +{ + unsigned numbers[3]; + std::string prerelease; + std::string build; + + explicit SemVerVersion(std::string const& _versionString = "0.0.0"); +}; + +struct SemVerMatchExpression +{ + bool matches(SemVerVersion const& _version) const; + + bool isValid() const { return !m_disjunction.empty(); } + + struct MatchComponent + { + /// Prefix from < > <= >= ~ ^ + Token::Value prefix = Token::Illegal; + /// Version, where unsigned(-1) in major, minor or patch denotes '*', 'x' or 'X' + SemVerVersion version; + /// Whether we have 1, 1.2 or 1.2.4 + unsigned levelsPresent = 1; + bool matches(SemVerVersion const& _version) const; + }; + + struct Conjunction + { + std::vector<MatchComponent> components; + bool matches(SemVerVersion const& _version) const; + }; + + std::vector<Conjunction> m_disjunction; +}; + +class SemVerMatchExpressionParser +{ +public: + SemVerMatchExpressionParser(std::vector<Token::Value> const& _tokens, std::vector<std::string> const& _literals): + m_tokens(_tokens), m_literals(_literals) + {} + SemVerMatchExpression parse(); + +private: + void reset(); + + void parseMatchExpression(); + SemVerMatchExpression::MatchComponent parseMatchComponent(); + unsigned parseVersionPart(); + + char currentChar() const; + char nextChar(); + Token::Value currentToken() const; + void nextToken(); + + std::vector<Token::Value> m_tokens; + std::vector<std::string> m_literals; + + unsigned m_pos = 0; + unsigned m_posInside = 0; + + SemVerMatchExpression m_expression; +}; + +} +} diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 593f2f69..a95b4879 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -15,9 +15,11 @@ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. */ +#include <libsolidity/analysis/SyntaxChecker.h> #include <memory> #include <libsolidity/ast/AST.h> -#include <libsolidity/analysis/SyntaxChecker.h> +#include <libsolidity/analysis/SemVerHandler.h> +#include <libsolidity/interface/Version.h> using namespace std; using namespace dev; @@ -27,7 +29,7 @@ using namespace dev::solidity; bool SyntaxChecker::checkSyntax(SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); - return m_errors.empty(); + return Error::containsOnlyWarnings(m_errors); } void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description) @@ -40,6 +42,52 @@ void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string con m_errors.push_back(err); } +bool SyntaxChecker::visit(SourceUnit const&) +{ + m_versionPragmaFound = false; + return true; +} + +void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) +{ + if (!m_versionPragmaFound) + { + auto err = make_shared<Error>(Error::Type::Warning); + *err << + errinfo_sourceLocation(_sourceUnit.location()) << + errinfo_comment( + string("Source file does not specify required compiler version! ") + + string("Consider adding \"pragma solidity ^") + VersionNumber + string(";\".") + ); + m_errors.push_back(err); + } +} + +bool SyntaxChecker::visit(PragmaDirective const& _pragma) +{ + solAssert(!_pragma.tokens().empty(), ""); + solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); + if (_pragma.tokens()[0] != Token::Identifier && _pragma.literals()[0] != "solidity") + syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); + else + { + vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); + vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); + SemVerMatchExpressionParser parser(tokens, literals); + auto matchExpression = parser.parse(); + SemVerVersion currentVersion{string(VersionString)}; + if (!matchExpression.matches(currentVersion)) + syntaxError( + _pragma.location(), + "Source file requires different compiler version (current compiler is " + + string(VersionString) + " - note that nightly builds are considered to be " + "strictly less than the released version" + ); + m_versionPragmaFound = true; + } + return true; +} + bool SyntaxChecker::visit(ModifierDefinition const&) { m_placeholderFound = false; @@ -91,7 +139,7 @@ bool SyntaxChecker::visit(Break const& _breakStatement) return true; } -bool SyntaxChecker::visit(const PlaceholderStatement&) +bool SyntaxChecker::visit(PlaceholderStatement const&) { m_placeholderFound = true; return true; diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 3198ffd0..ac8ed872 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -45,6 +45,10 @@ private: /// Adds a new error to the list of errors. void syntaxError(SourceLocation const& _location, std::string const& _description); + virtual bool visit(SourceUnit const& _sourceUnit) override; + virtual void endVisit(SourceUnit const& _sourceUnit) override; + virtual bool visit(PragmaDirective const& _pragma) override; + virtual bool visit(ModifierDefinition const& _modifier) override; virtual void endVisit(ModifierDefinition const& _modifier) override; @@ -63,6 +67,9 @@ private: /// Flag that indicates whether a function modifier actually contains '_'. bool m_placeholderFound = false; + /// Flag that indicates whether some version pragma was present. + bool m_versionPragmaFound = false; + int m_inLoopDepth = 0; }; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index bf275869..761d85fe 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -176,6 +176,34 @@ private: }; /** + * Pragma directive, only version requirements in the form `pragma solidity "^0.4.0";` are + * supported for now. + */ +class PragmaDirective: public ASTNode +{ +public: + PragmaDirective( + SourceLocation const& _location, + std::vector<Token::Value> const& _tokens, + std::vector<ASTString> const& _literals + ): ASTNode(_location), m_tokens(_tokens), m_literals(_literals) + {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<Token::Value> const& tokens() const { return m_tokens; } + std::vector<ASTString> const& literals() const { return m_literals; } + +private: + + /// Sequence of tokens following the "pragma" keyword. + std::vector<Token::Value> m_tokens; + /// Sequence of literals following the "pragma" keyword. + std::vector<ASTString> m_literals; +}; + +/** * Import directive for referencing other files / source objects. * Example: import "abc.sol" // imports all symbols of "abc.sol" into current scope * Source objects are identified by a string which can be a file name but does not have to be. diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index dad2b2e2..59fc1b57 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -35,6 +35,7 @@ namespace solidity class ASTNode; class SourceUnit; +class PragmaDirective; class ImportDirective; class Declaration; class ContractDefinition; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 35fd0b7d..49ee6d34 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -103,6 +103,15 @@ bool ASTJsonConverter::visit(SourceUnit const&) return true; } +bool ASTJsonConverter::visit(PragmaDirective const& _node) +{ + Json::Value literals(Json::arrayValue); + for (auto const& literal: _node.literals()) + literals.append(literal); + addJsonNode(_node, "PragmaDirective", { make_pair("literals", literals) }); + return true; +} + bool ASTJsonConverter::visit(ImportDirective const& _node) { addJsonNode(_node, "ImportDirective", { make_pair("file", _node.path())}); @@ -401,6 +410,10 @@ void ASTJsonConverter::endVisit(SourceUnit const&) goUp(); } +void ASTJsonConverter::endVisit(PragmaDirective const&) +{ +} + void ASTJsonConverter::endVisit(ImportDirective const&) { } diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 97aa8654..7c7b37f8 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -52,6 +52,7 @@ public: Json::Value const& json(); bool visit(SourceUnit const& _node) override; + bool visit(PragmaDirective const& _node) override; bool visit(ImportDirective const& _node) override; bool visit(ContractDefinition const& _node) override; bool visit(InheritanceSpecifier const& _node) override; @@ -96,6 +97,7 @@ public: bool visit(Literal const& _node) override; void endVisit(SourceUnit const&) override; + void endVisit(PragmaDirective const&) override; void endVisit(ImportDirective const&) override; void endVisit(ContractDefinition const&) override; void endVisit(InheritanceSpecifier const&) override; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 9ed9c6d5..a9de457a 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -47,6 +47,13 @@ void ASTPrinter::print(ostream& _stream) } +bool ASTPrinter::visit(PragmaDirective const& _node) +{ + writeLine("PragmaDirective"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(ImportDirective const& _node) { writeLine("ImportDirective \"" + _node.path() + "\""); @@ -355,6 +362,11 @@ bool ASTPrinter::visit(Literal const& _node) return goDeeper(); } +void ASTPrinter::endVisit(PragmaDirective const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(ImportDirective const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index a2546935..f0ab1098 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -47,6 +47,7 @@ public: /// Output the string representation of the AST to _stream. void print(std::ostream& _stream); + bool visit(PragmaDirective const& _node) override; bool visit(ImportDirective const& _node) override; bool visit(ContractDefinition const& _node) override; bool visit(InheritanceSpecifier const& _node) override; @@ -89,6 +90,7 @@ public: bool visit(ElementaryTypeNameExpression const& _node) override; bool visit(Literal const& _node) override; + void endVisit(PragmaDirective const&) override; void endVisit(ImportDirective const&) override; void endVisit(ContractDefinition const&) override; void endVisit(InheritanceSpecifier const&) override; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 5aac2066..3a1b55d3 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -44,6 +44,7 @@ class ASTVisitor { public: virtual bool visit(SourceUnit& _node) { return visitNode(_node); } + virtual bool visit(PragmaDirective& _node) { return visitNode(_node); } virtual bool visit(ImportDirective& _node) { return visitNode(_node); } virtual bool visit(ContractDefinition& _node) { return visitNode(_node); } virtual bool visit(InheritanceSpecifier& _node) { return visitNode(_node); } @@ -88,6 +89,7 @@ public: virtual bool visit(Literal& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } + virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } virtual void endVisit(ImportDirective& _node) { endVisitNode(_node); } virtual void endVisit(ContractDefinition& _node) { endVisitNode(_node); } virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); } @@ -144,6 +146,7 @@ class ASTConstVisitor { public: virtual bool visit(SourceUnit const& _node) { return visitNode(_node); } + virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); } virtual bool visit(ImportDirective const& _node) { return visitNode(_node); } virtual bool visit(ContractDefinition const& _node) { return visitNode(_node); } virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); } @@ -188,6 +191,7 @@ public: virtual bool visit(Literal const& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } + virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); } virtual void endVisit(ContractDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index dd2a7d60..b5a3806b 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -45,6 +45,18 @@ void SourceUnit::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void PragmaDirective::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void PragmaDirective::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void ImportDirective::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 0d1f68a6..0230729a 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -1,13 +1,16 @@ -SourceUnit = (ImportDirective | ContractDefinition)* +SourceUnit = (PragmaDirective | ImportDirective | ContractDefinition)* -ContractDefinition = ( 'contract' | 'library' ) Identifier - ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? - '{' ContractPart* '}' +// Pragma actually parses anything up to the trailing ';' to be fully forward-compatible. +PragmaDirective = 'pragma' Identifier Expression ';' ImportDirective = 'import' StringLiteral ('as' Identifier)? ';' | 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';' | 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';' +ContractDefinition = ( 'contract' | 'library' ) Identifier + ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? + '{' ContractPart* '}' + ContractPart = StateVariableDeclaration | UsingForDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index b8f72238..b2f4a156 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -76,6 +76,9 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) { switch (auto token = m_scanner->currentToken()) { + case Token::Pragma: + nodes.push_back(parsePragmaDirective()); + break; case Token::Import: nodes.push_back(parseImportDirective()); break; @@ -97,6 +100,36 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) } } +ASTPointer<PragmaDirective> Parser::parsePragmaDirective() +{ + // pragma anything* ; + // Currently supported: + // pragma solidity ^0.4.0 || ^0.3.0; + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Pragma); + vector<string> literals; + vector<Token::Value> tokens; + do + { + Token::Value token = m_scanner->currentToken(); + if (token == Token::Illegal) + parserError("Token incompatible with Solidity parser as part of pragma directive."); + else + { + string literal = m_scanner->currentLiteral(); + if (literal.empty() && Token::toString(token)) + literal = Token::toString(token); + literals.push_back(literal); + tokens.push_back(token); + } + m_scanner->next(); + } + while (m_scanner->currentToken() != Token::Semicolon && m_scanner->currentToken() != Token::EOS); + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode<PragmaDirective>(tokens, literals); +} + ASTPointer<ImportDirective> Parser::parseImportDirective() { // import "abc" [as x]; diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index d776c3fd..9c30cf60 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -55,6 +55,7 @@ private: ///@{ ///@name Parsing functions for the AST nodes + ASTPointer<PragmaDirective> parsePragmaDirective(); ASTPointer<ImportDirective> parseImportDirective(); ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 007baef4..15d4860f 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -167,6 +167,7 @@ namespace solidity K(Modifier, "modifier", 0) \ K(New, "new", 0) \ K(Public, "public", 0) \ + K(Pragma, "pragma", 0) \ K(Private, "private", 0) \ K(Return, "return", 0) \ K(Returns, "returns", 0) \ |