Browse Source

Solidity work for documentation strings

- Still a work in progress

- Parser now properly gets each function's doc comment

- Small changes in the scanner

- Multiline comments are considered
cl-refactor
Lefteris Karapetsas 10 years ago
parent
commit
e5da1ba6c6
  1. 4
      libsolidity/Parser.cpp
  2. 34
      libsolidity/Scanner.cpp
  3. 41
      libsolidity/Scanner.h
  4. 52
      test/solidityParser.cpp

4
libsolidity/Parser.cpp

@ -118,6 +118,8 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::FUNCTION); expectToken(Token::FUNCTION);
std::string docstring = m_scanner->getCurrentCommentLiteral();
m_scanner->clearCurrentCommentLiteral();
ASTPointer<ASTString> name(expectIdentifierToken()); ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters(parseParameterList()); ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false; bool isDeclaredConst = false;
@ -142,7 +144,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
} }
ASTPointer<Block> block = parseBlock(); ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block); nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, m_scanner->getCurrentCommentLiteral(), return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, docstring,
parameters, parameters,
isDeclaredConst, returnParameters, block); isDeclaredConst, returnParameters, block);
} }

34
libsolidity/Scanner.cpp

@ -180,10 +180,26 @@ Token::Value Scanner::skipSingleLineComment()
/// For the moment this function simply consumes a single line triple slash doc comment /// For the moment this function simply consumes a single line triple slash doc comment
Token::Value Scanner::scanDocumentationComment() Token::Value Scanner::scanDocumentationComment()
{ {
LiteralScope literal(this); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
advance(); //consume the last '/' advance(); //consume the last '/'
while (!isSourcePastEndOfInput() && !isLineTerminator(m_char)) while (!isSourcePastEndOfInput())
{ {
if (isLineTerminator(m_char))
{
// check if next line is also a documentation comment
skipWhitespace();
if (m_source.get(0) == '/' &&
m_source.get(1) == '/' &&
m_source.get(2) == '/' &&
!m_source.isPastEndOfInput(3))
{
m_source.advanceBy(3);
addCommentLiteralChar('\n');
}
else
break; // next line is not a documentation comment, we are done
}
addCommentLiteralChar(m_char); addCommentLiteralChar(m_char);
advance(); advance();
} }
@ -474,7 +490,7 @@ Token::Value Scanner::scanString()
{ {
char const quote = m_char; char const quote = m_char;
advance(); // consume quote advance(); // consume quote
LiteralScope literal(this); LiteralScope literal(this, LITERAL_TYPE_STRING);
while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char))
{ {
char c = m_char; char c = m_char;
@ -505,7 +521,7 @@ void Scanner::scanDecimalDigits()
Token::Value Scanner::scanNumber(char _charSeen) Token::Value Scanner::scanNumber(char _charSeen)
{ {
enum { DECIMAL, HEX, BINARY } kind = DECIMAL; enum { DECIMAL, HEX, BINARY } kind = DECIMAL;
LiteralScope literal(this); LiteralScope literal(this, LITERAL_TYPE_NUMBER);
if (_charSeen == '.') if (_charSeen == '.')
{ {
// we have already seen a decimal point of the float // we have already seen a decimal point of the float
@ -758,7 +774,7 @@ Token::Value Scanner::scanIdentifierOrKeyword()
{ {
if (asserts(isIdentifierStart(m_char))) if (asserts(isIdentifierStart(m_char)))
BOOST_THROW_EXCEPTION(InternalCompilerError()); BOOST_THROW_EXCEPTION(InternalCompilerError());
LiteralScope literal(this); LiteralScope literal(this, LITERAL_TYPE_STRING);
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
// Scan the rest of the identifier characters. // Scan the rest of the identifier characters.
while (isIdentifierPart(m_char)) while (isIdentifierPart(m_char))
@ -777,6 +793,14 @@ char CharStream::advanceAndGet()
return get(); return get();
} }
void CharStream::advanceBy(size_t _chars)
{
if (asserts(!isPastEndOfInput(_chars)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
m_pos += _chars;
}
char CharStream::rollback(size_t _amount) char CharStream::rollback(size_t _amount)
{ {
if (asserts(m_pos >= _amount)) if (asserts(m_pos >= _amount))

41
libsolidity/Scanner.h

@ -74,9 +74,10 @@ public:
CharStream(): m_pos(0) {} CharStream(): m_pos(0) {}
explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {} explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {}
int getPos() const { return m_pos; } int getPos() const { return m_pos; }
bool isPastEndOfInput() const { return m_pos >= m_source.size(); } bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_pos + _charsForward) >= m_source.size(); }
char get() const { return m_source[m_pos]; } char get(size_t _charsForward = 0) const { return m_source[m_pos + _charsForward]; }
char advanceAndGet(); char advanceAndGet();
void advanceBy(size_t _chars);
char rollback(size_t _amount); char rollback(size_t _amount);
///@{ ///@{
@ -93,19 +94,45 @@ private:
}; };
class Scanner class Scanner
{ {
public: public:
enum LiteralType {
LITERAL_TYPE_STRING,
LITERAL_TYPE_NUMBER, // not really different from string type in behaviour
LITERAL_TYPE_COMMENT
};
/// Scoped helper for literal recording. Automatically drops the literal /// Scoped helper for literal recording. Automatically drops the literal
/// if aborting the scanning before it's complete. /// if aborting the scanning before it's complete.
class LiteralScope class LiteralScope
{ {
public: public:
explicit LiteralScope(Scanner* self): m_scanner(self), m_complete(false) { m_scanner->startNewLiteral(); } explicit LiteralScope(Scanner* _self, enum LiteralType _type)
~LiteralScope() { if (!m_complete) m_scanner->dropLiteral(); } : m_type(_type)
, m_scanner(_self)
, m_complete(false)
{
if (_type == LITERAL_TYPE_COMMENT)
m_scanner->startNewCommentLiteral();
else
m_scanner->startNewLiteral();
}
~LiteralScope()
{
if (!m_complete)
{
if (m_type == LITERAL_TYPE_COMMENT)
m_scanner->dropCommentLiteral();
else
m_scanner->dropLiteral();
}
}
void complete() { m_complete = true; } void complete() { m_complete = true; }
private: private:
enum LiteralType m_type;
Scanner* m_scanner; Scanner* m_scanner;
bool m_complete; bool m_complete;
}; };
@ -133,8 +160,12 @@ public:
///@{ ///@{
///@name Information about the current comment token ///@name Information about the current comment token
Location getCurrentCommentLocation() const { return m_skippedComment.location; } Location getCurrentCommentLocation() const { return m_skippedComment.location; }
std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; } std::string const& getCurrentCommentLiteral() const { return m_skippedComment.literal; }
/// Called by the parser during FunctionDefinition parsing to clear the current comment
void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); }
///@} ///@}
///@{ ///@{
@ -166,9 +197,11 @@ private:
///@{ ///@{
///@name Literal buffer support ///@name Literal buffer support
inline void startNewLiteral() { m_nextToken.literal.clear(); } inline void startNewLiteral() { m_nextToken.literal.clear(); }
inline void startNewCommentLiteral() { m_nextSkippedComment.literal.clear(); }
inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); } inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); }
inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); } inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); }
inline void dropLiteral() { m_nextToken.literal.clear(); } inline void dropLiteral() { m_nextToken.literal.clear(); }
inline void dropCommentLiteral() { m_nextSkippedComment.literal.clear(); }
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
///@} ///@}

52
test/solidityParser.cpp

@ -122,6 +122,58 @@ BOOST_AUTO_TEST_CASE(function_normal_comments)
BOOST_CHECK_EQUAL(function->getDocumentation(), ""); BOOST_CHECK_EQUAL(function->getDocumentation(), "");
} }
BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is test function 1\n"
" function functionName1(hash hashin) returns (hash hashout) {}\n"
" /// This is test function 2\n"
" function functionName2(hash hashin) returns (hash hashout) {}\n"
" // nothing to see here\n"
" function functionName3(hash hashin) returns (hash hashout) {}\n"
" /// This is test function 4\n"
" function functionName4(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_CHECK_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_CHECK_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(function->getDocumentation(), " This is test function 1");
BOOST_CHECK_NO_THROW(function = functions.at(1));
BOOST_CHECK_EQUAL(function->getDocumentation(), " This is test function 2");
BOOST_CHECK_NO_THROW(function = functions.at(2));
BOOST_CHECK_EQUAL(function->getDocumentation(), "");
BOOST_CHECK_NO_THROW(function = functions.at(3));
BOOST_CHECK_EQUAL(function->getDocumentation(), " This is test function 4");
}
#if 0 /* Work in progress - currently fails*/
BOOST_AUTO_TEST_CASE(multiline_function_documentation)
{
ASTPointer<ContractDefinition> contract;
ASTPointer<FunctionDefinition> function;
char const* text = "contract test {\n"
" uint256 stateVar;\n"
" /// This is a test function\n"
" /// and it has 2 lines\n"
" function functionName1(hash hashin) returns (hash hashout) {}\n"
"}\n";
BOOST_CHECK_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions();
BOOST_CHECK_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(function->getDocumentation(),
" This is a test function\n"
" and it has 2 lines");
}
#endif
BOOST_AUTO_TEST_CASE(struct_definition) BOOST_AUTO_TEST_CASE(struct_definition)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"

Loading…
Cancel
Save