diff --git a/AST.h b/AST.h index 10616022b..ece609daf 100644 --- a/AST.h +++ b/AST.h @@ -146,13 +146,15 @@ class ContractDefinition: public Declaration public: ContractDefinition(Location const& _location, ASTPointer const& _name, + ASTPointer const& _documentation, std::vector> const& _definedStructs, std::vector> const& _stateVariables, std::vector> const& _definedFunctions): Declaration(_location, _name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), - m_definedFunctions(_definedFunctions) + m_definedFunctions(_definedFunctions), + m_documentation(_documentation) {} virtual void accept(ASTVisitor& _visitor) override; @@ -161,6 +163,10 @@ public: std::vector> const& getStateVariables() const { return m_stateVariables; } std::vector> const& getDefinedFunctions() const { return m_definedFunctions; } + /// @return A shared pointer of an ASTString. + /// Can contain a nullptr in which case indicates absence of documentation + ASTPointer const& getDocumentation() const { return m_documentation; } + /// Returns the functions that make up the calling interface in the intended order. std::vector getInterfaceFunctions() const; @@ -168,6 +174,7 @@ private: std::vector> m_definedStructs; std::vector> m_stateVariables; std::vector> m_definedFunctions; + ASTPointer m_documentation; }; class StructDefinition: public Declaration diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp index c3e62cad0..ac1fb29a1 100644 --- a/InterfaceHandler.cpp +++ b/InterfaceHandler.cpp @@ -75,7 +75,7 @@ std::unique_ptr InterfaceHandler::getUserDocumentation(ContractDefi if (strPtr) { resetUser(); - parseDocString(*strPtr); + parseDocString(*strPtr, CommentOwner::FUNCTION); if (!m_notice.empty()) {// since @notice is the only user tag if missing function should not appear user["notice"] = Json::Value(m_notice); @@ -95,6 +95,20 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin Json::Value doc; Json::Value methods(Json::objectValue); + auto contractDoc = _contractDef.getDocumentation(); + if (contractDoc) + { + m_contractAuthor.clear(); + m_title.clear(); + parseDocString(*contractDoc, CommentOwner::CONTRACT); + + if (!m_contractAuthor.empty()) + doc["author"] = m_contractAuthor; + + if (!m_title.empty()) + doc["title"] = m_title; + } + for (FunctionDefinition const* f: _contractDef.getInterfaceFunctions()) { Json::Value method; @@ -102,16 +116,21 @@ std::unique_ptr InterfaceHandler::getDevDocumentation(ContractDefin if (strPtr) { resetDev(); - parseDocString(*strPtr); + parseDocString(*strPtr, CommentOwner::FUNCTION); if (!m_dev.empty()) method["details"] = Json::Value(m_dev); + + if (!m_author.empty()) + method["author"] = m_author; + Json::Value params(Json::objectValue); for (auto const& pair: m_params) params[pair.first] = pair.second; if (!m_params.empty()) method["params"] = params; + if (!m_return.empty()) method["return"] = m_return; @@ -133,6 +152,7 @@ void InterfaceHandler::resetUser() void InterfaceHandler::resetDev() { m_dev.clear(); + m_author.clear(); m_return.clear(); m_params.clear(); } @@ -193,7 +213,8 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_iterator _pos, std::string::const_iterator _end, - std::string const& _tag) + std::string const& _tag, + CommentOwner _owner) { // LTODO: need to check for @(start of a tag) between here and the end of line // for all cases @@ -205,37 +226,77 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); else if (_tag == "return") return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); + else if (_tag == "author") + { + if (_owner == CommentOwner::CONTRACT) + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR); + else if(_owner == CommentOwner::FUNCTION) + return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR); + else + // LTODO: for now this else makes no sense but later comments will go to more language constructs + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts")); + } + else if (_tag == "title") + { + if (_owner == CommentOwner::CONTRACT) + return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE); + else + // LTODO: Unknown tag, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts")); + } else if (_tag == "param") return parseDocTagParam(_pos, _end); else - { // LTODO: Unknown tag, throw some form of warning and not just an exception BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("Unknown tag " + _tag + " encountered")); - } } else - return appendDocTag(_pos, _end); + return appendDocTag(_pos, _end, _owner); } std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end) + std::string::const_iterator _end, + CommentOwner _owner) { switch (m_lastTag) { - case DocTagType::DEV: - m_dev += " "; - return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); - case DocTagType::NOTICE: - m_notice += " "; - return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); - case DocTagType::RETURN: - m_return += " "; - return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); - case DocTagType::PARAM: - return appendDocTagParam(_pos, _end); - default: - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); - break; + case DocTagType::DEV: + m_dev += " "; + return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); + case DocTagType::NOTICE: + m_notice += " "; + return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); + case DocTagType::RETURN: + m_return += " "; + return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); + case DocTagType::AUTHOR: + if (_owner == CommentOwner::CONTRACT) + { + m_contractAuthor += " "; + return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR); + } + else if(_owner == CommentOwner::FUNCTION) + { + m_author += " "; + return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR); + } + else + // LTODO: Unknown tag, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment")); + case DocTagType::TITLE: + if (_owner == CommentOwner::CONTRACT) + { + m_title += " "; + return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE); + } + else + // LTODO: Unknown tag, throw some form of warning and not just an exception + BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment")); + case DocTagType::PARAM: + return appendDocTagParam(_pos, _end); + default: + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Illegal documentation tag type")); + break; } } @@ -247,7 +308,7 @@ static inline std::string::const_iterator getFirstSpaceOrNl(std::string::const_i return (spacePos < nlPos) ? spacePos : nlPos; } -void InterfaceHandler::parseDocString(std::string const& _string) +void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _owner) { auto currPos = _string.begin(); auto end = _string.end(); @@ -265,10 +326,10 @@ void InterfaceHandler::parseDocString(std::string const& _string) BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("End of tag " + std::string(tagPos, tagNameEndPos) + "not found")); - currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos)); + currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner); } else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag - currPos = appendDocTag(currPos + 1, end); + currPos = appendDocTag(currPos + 1, end, _owner); else if (currPos != end) // skip the line if a newline was found currPos = nlPos + 1; } diff --git a/InterfaceHandler.h b/InterfaceHandler.h index e6be9e6a7..d71345b96 100644 --- a/InterfaceHandler.h +++ b/InterfaceHandler.h @@ -45,7 +45,15 @@ enum class DocTagType: uint8_t DEV, NOTICE, PARAM, - RETURN + RETURN, + AUTHOR, + TITLE +}; + +enum class CommentOwner +{ + CONTRACT, + FUNCTION }; class InterfaceHandler @@ -89,12 +97,14 @@ private: std::string::const_iterator _end); std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, std::string::const_iterator _end); - void parseDocString(std::string const& _string); + void parseDocString(std::string const& _string, CommentOwner _owner); std::string::const_iterator appendDocTag(std::string::const_iterator _pos, - std::string::const_iterator _end); + std::string::const_iterator _end, + CommentOwner _owner); std::string::const_iterator parseDocTag(std::string::const_iterator _pos, std::string::const_iterator _end, - std::string const& _tag); + std::string const& _tag, + CommentOwner _owner); Json::StyledWriter m_writer; @@ -103,6 +113,9 @@ private: std::string m_notice; std::string m_dev; std::string m_return; + std::string m_contractAuthor; + std::string m_author; + std::string m_title; std::vector> m_params; }; diff --git a/Parser.cpp b/Parser.cpp index ddab489b6..b678b2fc0 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -112,6 +112,9 @@ ASTPointer Parser::parseImportDirective() ASTPointer Parser::parseContractDefinition() { ASTNodeFactory nodeFactory(*this); + ASTPointer docstring; + if (m_scanner->getCurrentCommentLiteral() != "") + docstring = make_shared(m_scanner->getCurrentCommentLiteral()); expectToken(Token::CONTRACT); ASTPointer name = expectIdentifierToken(); expectToken(Token::LBRACE); @@ -146,7 +149,7 @@ ASTPointer Parser::parseContractDefinition() } nodeFactory.markEndPosition(); expectToken(Token::RBRACE); - return nodeFactory.createNode(name, structs, stateVariables, functions); + return nodeFactory.createNode(name, docstring, structs, stateVariables, functions); } ASTPointer Parser::parseFunctionDefinition(bool _isPublic)