From 137b87485508e3882968a10559c2cb389dcc93c5 Mon Sep 17 00:00:00 2001 From: Cary Clark Date: Wed, 30 May 2018 09:21:49 -0400 Subject: generate include comments - formalize how aliases and substitutions work together - add constexpr, #define, typedef support - check for correct description order - write short enum, struct members R=caryclark@google.com Docs-Preview: https://skia.org/?cl=129455 Bug: skia:6898 Change-Id: Id60fc2ed02f38a7ba4e5cad5ef493d8c674e6183 Reviewed-on: https://skia-review.googlesource.com/129455 Commit-Queue: Cary Clark Reviewed-by: Cary Clark Auto-Submit: Cary Clark --- tools/bookmaker/bookmaker.cpp | 66 +++++--- tools/bookmaker/bookmaker.h | 37 +++- tools/bookmaker/definition.cpp | 23 ++- tools/bookmaker/includeParser.cpp | 17 +- tools/bookmaker/includeWriter.cpp | 346 +++++++++++++++++++++++++++++++------- tools/bookmaker/parserCommon.cpp | 7 +- 6 files changed, 375 insertions(+), 121 deletions(-) (limited to 'tools/bookmaker') diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp index aaf3781974..e773df6da8 100644 --- a/tools/bookmaker/bookmaker.cpp +++ b/tools/bookmaker/bookmaker.cpp @@ -176,7 +176,7 @@ BmhParser::MarkProps BmhParser::kMarkProps[] = { , { "Set", MarkType::kSet, R_N, E_N, M(Example) | M(NoExample) } , { "StdOut", MarkType::kStdOut, R_N, E_N, M(Example) | M(NoExample) } , { "Struct", MarkType::kStruct, R_Y, E_O, M(Class) | M_ST } -, { "Substitute", MarkType::kSubstitute, R_N, E_N, M_ST } +, { "Substitute", MarkType::kSubstitute, R_N, E_N, M(Alias) | M_ST } , { "Subtopic", MarkType::kSubtopic, R_Y, E_Y, M_CSST } , { "Table", MarkType::kTable, R_Y, E_N, M(Method) | M_CSST | M_E } , { "Template", MarkType::kTemplate, R_Y, E_N, M_CSST } @@ -426,6 +426,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy case MarkType::kDescription: case MarkType::kStdOut: // may be one-liner + case MarkType::kAlias: case MarkType::kNoExample: case MarkType::kParam: case MarkType::kPhraseDef: @@ -474,6 +475,19 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy } fParent->fChildren.push_back(definition); } + if (MarkType::kAlias == markType) { + const char* end = definition->fChildren.size() > 0 ? + definition->fChildren[0]->fStart : definition->fContentEnd; + TextParser parser(definition->fFileName, definition->fContentStart, end, + definition->fLineCount); + parser.trimEnd(); + string key = string(parser.fStart, parser.lineLength()); + if (fAliasMap.end() != fAliasMap.find(key)) { + return this->reportError("duplicate alias"); + } + fAliasMap[key] = definition; + definition->fFiddle = definition->fParent->fFiddle; + } break; } else if (MarkType::kPhraseDef == markType) { bool hasParams = '(' == this->next(); @@ -563,7 +577,6 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy } break; // always treated as one-liners (can't detect misuse easily) - case MarkType::kAlias: case MarkType::kAnchor: case MarkType::kBug: case MarkType::kDeprecated: @@ -614,18 +627,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy definition->fContentEnd = link->fContentEnd; definition->fTerminator = fChar; definition->fChildren.emplace_back(link); - } else if (MarkType::kAlias == markType) { - this->skipWhiteSpace(); - const char* start = fChar; - this->skipToNonName(); - string alias(start, fChar - start); - if (fAliasMap.end() != fAliasMap.find(alias)) { - return this->reportError("duplicate alias"); - } - fAliasMap[alias] = definition; - definition->fFiddle = definition->fParent->fFiddle; - } - else if (MarkType::kLine == markType) { + } else if (MarkType::kLine == markType) { const char* nextLF = this->strnchr('\n', this->fEnd); const char* start = fChar; const char* end = this->trimmedBracketEnd(fMC); @@ -663,7 +665,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy fParent->fDetails = this->skipExact("soon") ? Definition::Details::kSoonToBe_Deprecated : this->skipExact("testing") ? Definition::Details::kTestingOnly_Experiment : - this->skipExact("do not use") ? Definition::Details::kDoNotUse_Experiement : + this->skipExact("do not use") ? Definition::Details::kDoNotUse_Experiment : this->skipExact("not ready") ? Definition::Details::kNotReady_Experiment : Definition::Details::kNone; this->skipSpace(); @@ -1317,7 +1319,9 @@ bool BmhParser::findDefinitions() { return this->reportError("duplicate name"); } if (hasEnd && expectEnd) { - SkASSERT(fMC != this->peek()); + if (fMC == this->peek()) { + return this->reportError("missing body"); + } } if (!this->addDefinition(defStart, hasEnd, markType, typeNameBuilder, HasTag::kYes)) { @@ -1847,6 +1851,10 @@ const Definition* BmhParser::parentSpace() const { return parent; } +// A full terminal statement is in the form: +// \n optional-white-space #MarkType white-space #[# white-space] +// \n optional-white-space #MarkType white-space Name white-space #[# white-space] +// MarkType must match definition->fMarkType const char* BmhParser::checkForFullTerminal(const char* end, const Definition* definition) const { const char* start = end; while ('\n' != start[0] && start > fStart) { @@ -1864,25 +1872,27 @@ const char* BmhParser::checkForFullTerminal(const char* end, const Definition* d return end; } parser.skipWhiteSpace(); - const char* nameStart = parser.fChar; - if (isupper(nameStart[0])) { - parser.skipToWhiteSpace(); - if (parser.eof()) { - return end; - } - string defName = string(nameStart, parser.fChar - nameStart); - size_t defNamePos = definition->fName.rfind(defName); - if (definition->fName.length() != defNamePos + defName.length()) { - return end; + TextParser startName(fFileName, definition->fStart, definition->fContentStart, + definition->fLineCount); + if ('#' == startName.next()) { + startName.skipToSpace(); + if (!startName.eof() && startName.skipSpace()) { + const char* nameBegin = startName.fChar; + startName.skipToWhiteSpace(); + string name(nameBegin, (int) (startName.fChar - nameBegin)); + if (fMC != parser.peek() && !parser.skipExact(name.c_str())) { + return end; + } + parser.skipSpace(); } } - parser.skipWhiteSpace(); - if (fMC != parser.next()) { + if (parser.eof() || fMC != parser.next()) { return end; } if (!parser.eof() && fMC != parser.next()) { return end; } + SkASSERT(parser.eof()); return start; } diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h index 322aa4c446..1a85621d49 100644 --- a/tools/bookmaker/bookmaker.h +++ b/tools/bookmaker/bookmaker.h @@ -811,7 +811,7 @@ public: kNone, kSoonToBe_Deprecated, kTestingOnly_Experiment, - kDoNotUse_Experiement, + kDoNotUse_Experiment, kNotReady_Experiment, }; @@ -1107,6 +1107,11 @@ public: kYes, }; + enum class OneLine { + kNo, + kYes, + }; + ParserCommon() : TextParser() , fParent(nullptr) , fDebugOut(false) @@ -1209,7 +1214,7 @@ public: SkAssertResult(writeBlockTrim(size, data)); } - void writeBlockIndent(int size, const char* data); + bool writeBlockIndent(int size, const char* data); void writeBlockSeparator() { this->writeString( @@ -1225,7 +1230,12 @@ public: this->writeSpace(); } - void writeCommentTrailer() { + void writeCommentTrailer(OneLine oneLine) { + if (OneLine::kNo == oneLine) { + this->lf(1); + } else { + this->writeSpace(); + } this->writeString("*/"); this->lfcr(); } @@ -2012,6 +2022,11 @@ public: }; struct Item { + void reset() { + fName = ""; + fValue = ""; + } + string fName; string fValue; }; @@ -2047,9 +2062,9 @@ public: bool checkChildCommentLength(const Definition* parent, MarkType childType) const; void checkEnumLengths(const Definition& child, string enumName, ItemLength* length) const; - void constOut(const Definition* memberStart, const Definition& child, - const Definition* bmhConst); - void descriptionOut(const Definition* def, SkipFirstLine , Phrase ); + void constOut(const Definition* memberStart, const Definition* bmhConst); + void constSizeMembers(const RootDefinition* root); + bool descriptionOut(const Definition* def, SkipFirstLine , Phrase ); void enumHeaderOut(RootDefinition* root, const Definition& child); string enumMemberComment(const Definition* currentEnumItem, const Definition& child) const; const Definition* enumMemberForComment(const Definition* currentEnumItem) const; @@ -2080,6 +2095,8 @@ public: fBmhParser = nullptr; fEnumDef = nullptr; fMethodDef = nullptr; + fBmhConst = nullptr; + fConstDef = nullptr; fBmhStructDef = nullptr; fInStruct = false; fWroteMethod = false; @@ -2087,6 +2104,7 @@ public: fPendingMethod = false; } + string resolveAlias(const Definition* ); string resolveMethod(const char* start, const char* end, bool first); string resolveRef(const char* start, const char* end, bool first, RefType* refType); Wrote rewriteBlock(int size, const char* data, Phrase phrase); @@ -2101,6 +2119,9 @@ private: const Definition* fBmhMethod; const Definition* fEnumDef; const Definition* fMethodDef; + const Definition* fBmhConst; + const Definition* fConstDef; + const Definition* fLastDescription; Definition* fBmhStructDef; const char* fContinuation; // used to construct paren-qualified method name int fAnonymousEnumCount; @@ -2109,6 +2130,10 @@ private: int fStructMemberTab; int fStructValueTab; int fStructCommentTab; + int fStructMemberLength; + int fConstValueTab; + int fConstCommentTab; + int fConstLength; bool fInStruct; bool fWroteMethod; bool fIndentNext; diff --git a/tools/bookmaker/definition.cpp b/tools/bookmaker/definition.cpp index 1c6b1ebbe0..18ce808068 100644 --- a/tools/bookmaker/definition.cpp +++ b/tools/bookmaker/definition.cpp @@ -814,21 +814,20 @@ bool Definition::hasMatch(string name) const { } string Definition::incompleteMessage(DetailsType detailsType) const { - if (!IncompleteAllowed(fMarkType)) { - auto iter = std::find_if(fChildren.begin(), fChildren.end(), - [](const Definition* test) { return IncompleteAllowed(test->fMarkType); }); - SkASSERT(fChildren.end() != iter); - return (*iter)->incompleteMessage(detailsType); - } - string message = MarkType::kExperimental == fMarkType ? + SkASSERT(!IncompleteAllowed(fMarkType)); + auto iter = std::find_if(fChildren.begin(), fChildren.end(), + [](const Definition* test) { return IncompleteAllowed(test->fMarkType); }); + SkASSERT(fChildren.end() != iter); + SkASSERT(Details::kNone == (*iter)->fDetails); + string message = MarkType::kExperimental == (*iter)->fMarkType ? "Experimental." : "Deprecated."; - if (Definition::Details::kDoNotUse_Experiement == fDetails) { + if (Details::kDoNotUse_Experiment == fDetails) { message += " Do not use."; - } else if (Definition::Details::kNotReady_Experiment == fDetails) { + } else if (Details::kNotReady_Experiment == fDetails) { message += " Not ready for general use."; - } else if (Definition::Details::kSoonToBe_Deprecated == fDetails) { - message += " Soon to be deprecated."; - } else if (Definition::Details::kTestingOnly_Experiment == fDetails) { + } else if (Details::kSoonToBe_Deprecated == fDetails) { + message = "To be deprecated soon."; + } else if (Details::kTestingOnly_Experiment == fDetails) { message += " For testing only."; } if (DetailsType::kPhrase == detailsType) { diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp index 12bd787eb8..56de4f6f15 100644 --- a/tools/bookmaker/includeParser.cpp +++ b/tools/bookmaker/includeParser.cpp @@ -1276,7 +1276,7 @@ bool IncludeParser::findComments(const Definition& includeDef, Definition* marku return true; } -// caller calls reportError, so just return false here +// caller just returns, so report error here bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) { SkASSERT(includeDef->fTokens.size() > 0); // parse class header @@ -1323,15 +1323,11 @@ bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) { while (iter != includeDef->fTokens.end() && (publicLen != (size_t) (iter->fContentEnd - iter->fStart) || strncmp(iter->fStart, publicName, publicLen))) { + iter->fPrivate = true; iter = std::next(iter); ++publicIndex; } } - auto childIter = includeDef->fChildren.begin(); - while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) { - (*childIter)->fPrivate = true; - childIter = std::next(childIter); - } int keyIndex = publicIndex; KeyWord currentKey = KeyWord::kPublic; const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName; @@ -1340,9 +1336,12 @@ bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) { size_t protectedLen = strlen(protectedName); const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName; size_t privateLen = strlen(privateName); + auto childIter = includeDef->fChildren.begin(); + std::advance(childIter, publicIndex); while (childIter != includeDef->fChildren.end()) { Definition* child = *childIter; while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) { + iter->fPrivate = KeyWord::kPublic != currentKey; const char* testStart = iter->fStart; size_t testLen = (size_t) (iter->fContentEnd - testStart); iter = std::next(iter); @@ -1365,12 +1364,14 @@ bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) { if (!this->parseObject(child, markupDef)) { return false; } - } else { - child->fPrivate = true; } fLastObject = child; childIter = std::next(childIter); } + while (iter != includeDef->fTokens.end()) { + iter->fPrivate = KeyWord::kPublic != currentKey; + iter = std::next(iter); + } SkASSERT(fParent->fParent); fParent = fParent->fParent; return true; diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp index 62e8205e3d..f05ef0ece1 100644 --- a/tools/bookmaker/includeWriter.cpp +++ b/tools/bookmaker/includeWriter.cpp @@ -58,22 +58,24 @@ void IncludeWriter::checkEnumLengths(const Definition& child, string enumName, I } } -void IncludeWriter::constOut(const Definition* memberStart, const Definition& child, - const Definition* bmhConst) { +void IncludeWriter::constOut(const Definition* memberStart, const Definition* bmhConst) { const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : memberStart->fContentStart; this->writeBlockTrim((int) (bodyEnd - fStart), fStart); // may write nothing this->lf(2); this->writeCommentHeader(); fIndent += 4; - this->descriptionOut(bmhConst, SkipFirstLine::kYes, Phrase::kNo); + if (!this->descriptionOut(bmhConst, SkipFirstLine::kYes, Phrase::kNo)) { + return memberStart->reportError("expected description for const"); + } fIndent -= 4; - this->writeCommentTrailer(); + this->writeCommentTrailer(OneLine::kNo); fStart = memberStart->fContentStart; } -void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirstLine, +bool IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirstLine, Phrase phrase) { + bool wroteSomething = false; const char* commentStart = def->fContentStart; if (SkipFirstLine::kYes == skipFirstLine) { TextParser parser(def); @@ -87,8 +89,12 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs string message = def->incompleteMessage(Definition::DetailsType::kSentence); this->writeString(message); this->lfcr(); + wroteSomething = true; } + const Definition* lastDescription = def; for (auto prop : def->fChildren) { + fLastDescription = lastDescription; + lastDescription = prop; switch (prop->fMarkType) { case MarkType::kCode: { bool literal = false; @@ -98,6 +104,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs SkASSERT(commentLen < 1000); if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { this->lf(2); + wroteSomething = true; } } size_t childSize = prop->fChildren.size(); @@ -116,14 +123,14 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs if (!literalOutdent) { fIndent += 4; } - this->writeBlockIndent(commentLen, commentStart); + wroteSomething |= this->writeBlockIndent(commentLen, commentStart); this->lf(2); if (!literalOutdent) { fIndent -= 4; } - commentStart = prop->fTerminator; SkDEBUGCODE(wroteCode = true); } + commentStart = prop->fTerminator; } break; case MarkType::kDefinedBy: commentStart = prop->fTerminator; @@ -133,6 +140,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs prop->fContentEnd - prop->fContentStart) + ')'); this->writeString(bugstr); this->lfcr(); + wroteSomething = true; } case MarkType::kDeprecated: case MarkType::kPrivate: @@ -141,10 +149,11 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs SkASSERT(commentLen < 1000); if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { this->lfcr(); + wroteSomething = true; } } commentStart = prop->fContentStart; - if (' ' < commentStart[0]) { + if (MarkType::kPrivate != prop->fMarkType && ' ' < commentStart[0]) { commentStart = strchr(commentStart, '\n'); } if (MarkType::kBug == prop->fMarkType) { @@ -152,7 +161,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs } commentLen = (int) (prop->fContentEnd - commentStart); if (commentLen > 0) { - this->writeBlockIndent(commentLen, commentStart); + wroteSomething |= this->writeBlockIndent(commentLen, commentStart); const char* end = commentStart + commentLen; while (end > commentStart && ' ' == end[-1]) { --end; @@ -165,8 +174,6 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs commentLen = (int) (def->fContentEnd - commentStart); break; case MarkType::kExperimental: - this->writeString("EXPERIMENTAL:"); - this->writeSpace(); commentStart = prop->fContentStart; if (' ' < commentStart[0]) { commentStart = strchr(commentStart, '\n'); @@ -175,6 +182,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs if (commentLen > 0) { if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { this->lfcr(); + wroteSomething = true; } } commentStart = prop->fTerminator; @@ -190,13 +198,14 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs } else { this->writeSpace(); } + wroteSomething = true; } } int saveIndent = fIndent; if (fIndent < fColumn + 1) { fIndent = fColumn + 1; } - this->writeBlockIndent(prop->length(), prop->fContentStart); + wroteSomething |= this->writeBlockIndent(prop->length(), prop->fContentStart); fIndent = saveIndent; commentStart = prop->fTerminator; commentLen = (int) (def->fContentEnd - commentStart); @@ -217,6 +226,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs SkASSERT(commentLen < 1000); if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { this->lfcr(); + wroteSomething = true; } } commentStart = prop->fTerminator; @@ -228,6 +238,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { this->lfcr(); + wroteSomething = true; } } for (auto row : prop->fChildren) { @@ -236,7 +247,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs SkASSERT(MarkType::kColumn == column->fMarkType); this->writeString("-"); this->writeSpace(); - this->descriptionOut(column, SkipFirstLine::kNo, Phrase::kNo); + wroteSomething |= this->descriptionOut(column, SkipFirstLine::kNo, Phrase::kNo); this->lf(1); } } @@ -252,10 +263,11 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs this->rewriteBlock(commentLen, commentStart, Phrase::kNo); // ince we don't do line wrapping, always insert LF before phrase this->lfcr(); // TODO: remove this once rewriteBlock rewraps paragraphs + wroteSomething = true; } auto iter = fBmhParser->fPhraseMap.find(prop->fName); if (fBmhParser->fPhraseMap.end() == iter) { - return this->reportError("missing phrase definition"); + return this->reportError("missing phrase definition"); } Definition* phraseDef = iter->second; // TODO: given TextParser(commentStart, prop->fStart + up to #) return if @@ -289,12 +301,16 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs this->writeSpace(); } defIsPhrase = Phrase::kYes; + wroteSomething = true; } if (length > 0) { this->rewriteBlock(length, start, defIsPhrase); } commentStart = prop->fContentStart; commentLen = (int) (def->fContentEnd - commentStart); + if ('\n' == commentStart[0] && '\n' == commentStart[1]) { + this->lf(2); + } } break; default: commentLen = (int) (prop->fStart - commentStart); @@ -307,7 +323,9 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs SkASSERT(wroteCode || (commentLen > 0 && commentLen < 1500) || def->fDeprecated); if (commentLen > 0) { this->rewriteBlock(commentLen, commentStart, phrase); + wroteSomething = true; } + return wroteSomething; } void IncludeWriter::enumHeaderOut(RootDefinition* root, const Definition& child) { @@ -435,7 +453,7 @@ void IncludeWriter::enumHeaderOut(RootDefinition* root, const Definition& child) if (wroteHeader) { fIndent -= 4; this->lfcr(); - this->writeCommentTrailer(); + this->writeCommentTrailer(OneLine::kNo); } Definition* braceHolder = child.fChildren[0]; if (KeyWord::kClass == braceHolder->fKeyWord) { @@ -474,7 +492,7 @@ string IncludeWriter::enumMemberComment(const Definition* currentEnumItem, break; } if (IncompleteAllowed(constItem->fMarkType)) { - shortComment = constItem->incompleteMessage(Definition::DetailsType::kPhrase); + shortComment = constItem->fParent->incompleteMessage(Definition::DetailsType::kPhrase); } } if (!shortComment.length()) { @@ -617,6 +635,7 @@ void IncludeWriter::enumMembersOut(Definition& child) { } if (ItemState::kNone != state) { this->enumMemberOut(currentEnumItem, child, item, preprocessor); + item.reset(); fStart = token.fContentStart; state = ItemState::kNone; last.fStart = nullptr; @@ -840,7 +859,7 @@ void IncludeWriter::methodOut(Definition* method, const Definition& child) { } fIndent -= 4; this->lfcr(); - this->writeCommentTrailer(); + this->writeCommentTrailer(OneLine::kNo); fBmhMethod = nullptr; fMethodDef = nullptr; fEnumDef = nullptr; @@ -864,7 +883,7 @@ void IncludeWriter::structOut(const Definition* root, const Definition& child, } fIndent -= 4; this->lfcr(); - this->writeCommentTrailer(); + this->writeCommentTrailer(OneLine::kNo); } bool IncludeWriter::findEnumSubtopic(string undername, const Definition** rootDefPtr) const { @@ -916,36 +935,18 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const string name(child.fContentStart, (int) (child.fContentEnd - child.fContentStart)); Definition* commentBlock = this->findMemberCommentBlock(fBmhStructDef->fChildren, name); if (!commentBlock) { - return memberStart->reportError("member missing comment block"); + return memberStart->reportError("member missing comment block 2"); } -#if 0 - if (!commentBlock->fShort) { - const char* commentStart = commentBlock->fContentStart; - ptrdiff_t commentLen = commentBlock->fContentEnd - commentStart; + auto lineIter = std::find_if(commentBlock->fChildren.begin(), commentBlock->fChildren.end(), + [](const Definition* def){ return MarkType::kLine == def->fMarkType; } ); + SkASSERT(commentBlock->fChildren.end() != lineIter); + const Definition* lineDef = *lineIter; + if (fStructMemberLength > 100) { this->writeCommentHeader(); - bool wroteLineFeed = false; - fIndent += 4; - for (auto child : commentBlock->fChildren) { - commentLen = child->fStart - commentStart; - wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo); - if (MarkType::kFormula == child->fMarkType) { - this->writeSpace(); - this->writeBlock((int) (child->fContentEnd - child->fContentStart), - child->fContentStart); - } - commentStart = child->fTerminator; - } - commentLen = commentBlock->fContentEnd - commentStart; - wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo); - fIndent -= 4; - if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) { - this->lfcr(); - } else { - this->writeSpace(); - } - this->writeCommentTrailer(); + this->writeSpace(); + this->rewriteBlock(lineDef->length(), lineDef->fContentStart, Phrase::kYes); + this->writeCommentTrailer(OneLine::kYes); } -#endif this->lfcr(); this->writeBlock((int) (child.fStart - memberStart->fContentStart), memberStart->fContentStart); @@ -967,22 +968,80 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const valueStart->fContentStart); } this->writeString(";"); - /* if (commentBlock->fShort) */ { + if (fStructMemberLength <= 100) { this->indentToColumn(fStructCommentTab); this->writeString("//!<"); this->writeSpace(); - string extract = fBmhParser->extractText(commentBlock, BmhParser::TrimExtract::kYes); - this->rewriteBlock(extract.length(), &extract.front(), Phrase::kNo); + this->rewriteBlock(lineDef->length(), lineDef->fContentStart, Phrase::kYes); } - this->lf(2); + this->lf(1); return valueEnd; } +// const and constexpr and #define aren't contained in a braces like struct and enum. +// use a bmh subtopic to group like ones together, then measure them in the include as if +// they were formally linked together +void IncludeWriter::constSizeMembers(const RootDefinition* root) { + // fBmhConst->fParent is subtopic containing all grouped const expressions + // fConstDef is token of const include name, hopefully on same line as const start + string rootPrefix = root ? root->fName + "::" : ""; + const Definition* test = fConstDef; + int tokenIndex = test->fParentIndex; + int longestName = 0; + int longestValue = 0; + int longestComment = 0; + const Definition* subtopic = fBmhConst->fParent; + SkASSERT(subtopic); + SkASSERT(MarkType::kSubtopic == subtopic->fMarkType); + // back up to first token on line + size_t lineCount = test->fLineCount; + const Definition* last; + auto tokenIter = test->fParent->fTokens.begin(); + std::advance(tokenIter, tokenIndex); + do { + last = test; + std::advance(tokenIter, -1); + test = &*tokenIter; + SkASSERT(test->fParentIndex == --tokenIndex); + } while (lineCount == test->fLineCount); + test = last; + for (auto child : subtopic->fChildren) { + if (MarkType::kConst != child->fMarkType) { + continue; + } + // expect found name to be on the left of assign + // expect assign + // expect semicolon + // no parens, no braces + while (rootPrefix + test->fName != child->fName) { + std::advance(tokenIter, 1); + test = &*tokenIter; + SkASSERT(lineCount >= test->fLineCount); + } + ++lineCount; + TextParser constText(test); + const char* nameEnd = constText.trimmedBracketEnd('='); + SkAssertResult(constText.skipToEndBracket('=')); + const char* valueEnd = constText.trimmedBracketEnd(';'); + auto lineIter = std::find_if(child->fChildren.begin(), child->fChildren.end(), + [](const Definition* def){ return MarkType::kLine == def->fMarkType; }); + SkASSERT(child->fChildren.end() != lineIter); + longestName = SkTMax(longestName, (int) (nameEnd - constText.fStart)); + longestValue = SkTMax(longestValue, (int) (valueEnd - constText.fChar)); + longestComment = SkTMax(longestComment, (*lineIter)->length()); + } + // write fStructValueTab, fStructCommentTab + fConstValueTab = longestName + fIndent + 1; + fConstCommentTab = fConstValueTab + longestValue + 2; + fConstLength = fConstCommentTab + longestComment + (int) sizeof("//!<"); +} + void IncludeWriter::structSizeMembers(const Definition& child) { int longestType = 0; Definition* typeStart = nullptr; int longestName = 0; int longestValue = 0; + int longestComment = 0; SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2); bool inEnum = false; bool inMethod = false; @@ -1066,6 +1125,18 @@ void IncludeWriter::structSizeMembers(const Definition& child) { longestName = SkTMax(longestName, (int) (token.fContentEnd - token.fContentStart)); typeStart->fMemberStart = true; inMember = true; + string tokenName(token.fContentStart, (int) (token.fContentEnd - token.fContentStart)); + Definition* commentBlock = this->findMemberCommentBlock(fBmhStructDef->fChildren, + tokenName); + if (!commentBlock) { + return token.reportError("member missing comment block 1"); + } + auto lineIter = std::find_if(commentBlock->fChildren.begin(), + commentBlock->fChildren.end(), + [](const Definition* def){ return MarkType::kLine == def->fMarkType; } ); + SkASSERT(commentBlock->fChildren.end() != lineIter); + const Definition* lineDef = *lineIter; + longestComment = SkTMax(longestComment, lineDef->length()); continue; } if (MarkType::kMethod == token.fMarkType) { @@ -1089,6 +1160,7 @@ void IncludeWriter::structSizeMembers(const Definition& child) { fStructCommentTab += longestValue + 3 /* space = space */ ; fStructValueTab -= 1 /* ; */ ; } + fStructMemberLength = fStructCommentTab + longestComment; // iterate through struct to ensure that members' comments fit on line // struct or class may not have any members (void) this->checkChildCommentLength(fBmhStructDef, MarkType::kMember); @@ -1218,6 +1290,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti params.skipToEndBracket('('); if (params.startsWith(child.fContentStart, childLen)) { this->methodOut(clonedMethod, child); + sawConst = false; break; } ++alternate; @@ -1263,6 +1336,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti return child.reportError("method not found"); } this->methodOut(method, child); + sawConst = false; continue; } if (Definition::Type::kPunctuation == child.fType && @@ -1283,6 +1357,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti continue; } this->methodOut(method, child); + sawConst = false; continue; } else if (fBmhStructDef && fBmhStructDef->fDeprecated) { fContinuation = nullptr; @@ -1343,6 +1418,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti continue; } this->methodOut(method, child); + sawConst = false; if (fAttrDeprecated) { startDef = fAttrDeprecated; fStart = fAttrDeprecated->fContentStart; @@ -1387,6 +1463,8 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti name = child.fName; } else if (1 == trial) { name = root->fName + "::" + child.fName; + } else if (2 == trial) { + name = root->fName; } else { SkASSERT(parent); name = parent->fName + "::" + child.fName; @@ -1451,11 +1529,12 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti continue; } #endif + Definition* priorBlock = fBmhStructDef; Definition* codeBlock = nullptr; Definition* nextBlock = nullptr; for (auto test : fBmhStructDef->fChildren) { if (MarkType::kCode == test->fMarkType) { - SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier + SkASSERT(!codeBlock); // FIXME: check enum earlier codeBlock = test; continue; } @@ -1463,13 +1542,34 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti nextBlock = test; break; } + priorBlock = test; } - // FIXME: trigger error earlier if inner #Struct or #Class is missing #Code + // FIXME: trigger error earlier if inner #Struct or #Class is missing #Code if (!fBmhStructDef->fDeprecated) { SkASSERT(codeBlock); SkASSERT(nextBlock); // FIXME: check enum for correct order earlier const char* commentStart = codeBlock->fTerminator; const char* commentEnd = nextBlock->fStart; + // FIXME: trigger error if #Code is present but comment is before it earlier + SkASSERT(priorBlock); // code always preceded by #Line (I think) + TextParser priorComment(priorBlock->fFileName, + priorBlock->fTerminator, codeBlock->fStart, + priorBlock->fLineCount); + priorComment.trimEnd(); + if (!priorComment.eof()) { + return priorBlock->reportError( + "expect no comment before #Code"); + } + TextParser nextComment(codeBlock->fFileName, commentStart, + commentEnd, codeBlock->fLineCount); + nextComment.trimEnd(); + if (!priorComment.eof()) { + return priorBlock->reportError( + "expect comment after #Code"); + } + if (!nextComment.eof()) { + + } fIndentNext = true; this->structOut(root, *fBmhStructDef, commentStart, commentEnd); } @@ -1632,10 +1732,33 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } } if (bmhConst) { - this->constOut(memberStart, child, bmhConst); + this->constOut(memberStart, bmhConst); fDeferComment = nullptr; sawConst = false; } + } else if (MarkType::kNone == child.fMarkType && sawConst && !fEnumDef) { + string match; + if (root) { + match = root->fName + "::"; + match += string(child.fContentStart, child.fContentEnd - child.fContentStart); + auto bmhClassIter = fBmhParser->fClassMap.find(root->fName); + if (fBmhParser->fClassMap.end() != bmhClassIter) { + RootDefinition& bmhClass = bmhClassIter->second; + auto constIter = std::find_if(bmhClass.fLeaves.begin(), bmhClass.fLeaves.end(), + [match](std::pair& leaf){ return match == leaf.second.fName; } ); + if (bmhClass.fLeaves.end() != constIter) { + const Definition& bmhConst = constIter->second; + if (MarkType::kConst == bmhConst.fMarkType + && MarkType::kSubtopic == bmhConst.fParent->fMarkType) { + fBmhConst = &bmhConst; + fConstDef = &child; + } + } + } + } else { + SkDebugf(""); // FIXME: support global constexpr + } + } if (child.fMemberStart) { memberStart = &child; @@ -1649,8 +1772,59 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } if (Definition::Type::kPunctuation == child.fType) { if (Punctuation::kSemicolon == child.fPunctuation) { + if (sawConst && fBmhConst) { // find bmh documentation. Parent must be subtopic. + const Definition* subtopic = fBmhConst->fParent; + SkASSERT(subtopic); + SkASSERT(MarkType::kSubtopic == subtopic->fMarkType); + auto firstConst = std::find_if(subtopic->fChildren.begin(), + subtopic->fChildren.end(), + [](const Definition* def){ return MarkType::kConst == def->fMarkType;}); + SkASSERT(firstConst != subtopic->fChildren.end()); + bool constIsFirst = *firstConst == fBmhConst; + if (constIsFirst) { // If first #Const child, output subtopic description. + this->constOut(memberStart, subtopic); + // find member / value / comment tabs + // look for a one-to-one correspondence between bmh and include + this->constSizeMembers(root); + } + const char* blockStart = fDeferComment ? fLastComment->fContentEnd : fStart; + const char* blockEnd = fDeferComment ? fDeferComment->fStart - 1 : + memberStart->fStart; + this->writeBlockTrim((int) (blockEnd - blockStart), blockStart); + // after const code, output #Line description as short comment + auto lineIter = std::find_if(fBmhConst->fChildren.begin(), + fBmhConst->fChildren.end(), + [](const Definition* def){ return MarkType::kLine == def->fMarkType; }); + SkASSERT(fBmhConst->fChildren.end() != lineIter); + const Definition* lineDef = *lineIter; + if (fConstLength > 100) { + this->writeCommentHeader(); + this->writeSpace(); + this->rewriteBlock(lineDef->length(), lineDef->fContentStart, Phrase::kYes); + this->writeCommentTrailer(OneLine::kYes); + } + this->lfcr(); + TextParser constText(memberStart); + const char* nameEnd = constText.trimmedBracketEnd('='); + SkAssertResult(constText.skipToEndBracket('=')); + const char* valueEnd = constText.trimmedBracketEnd(';'); + this->writeBlock((int) (nameEnd - memberStart->fContentStart), + memberStart->fContentStart); + this->indentToColumn(fConstValueTab); + this->writeBlock((int) (valueEnd - constText.fChar), constText.fChar); + this->writeString(";"); + if (fConstLength <= 100) { + this->indentToColumn(fConstCommentTab); + this->writeString("//!<"); + this->writeSpace(); + this->rewriteBlock(lineDef->length(), lineDef->fContentStart, Phrase::kYes); + } + fStart = child.fContentStart + 1; + fDeferComment = nullptr; + fBmhConst = nullptr; + sawConst = false; + } memberStart = nullptr; - sawConst = false; staticOnly = false; if (inStruct) { fInStruct = false; @@ -1763,6 +1937,25 @@ string IncludeWriter::resolveMethod(const char* start, const char* end, bool fir RootDefinition::AllowParens::kNo); if (defRef && MarkType::kMethod == defRef->fMarkType) { substitute = methodname + "()"; + } else { + auto defineIter = fBmhParser->fDefineMap.find(methodname); + if (fBmhParser->fDefineMap.end() != defineIter) { + const RootDefinition& defineDef = defineIter->second; + auto codeIter = std::find_if(defineDef.fChildren.begin(), + defineDef.fChildren.end(), + [](Definition* child){ return MarkType::kCode == child->fMarkType; } ); + if (defineDef.fChildren.end() != codeIter) { + const Definition* codeDef = *codeIter; + string codeContents(codeDef->fContentStart, codeDef->length()); + size_t namePos = codeContents.find(methodname); + if (string::npos != namePos) { + size_t parenPos = namePos + methodname.length(); + if (parenPos < codeContents.length() && '(' == codeContents[parenPos]) { + substitute = methodname + "()"; + } + } + } + } } } if (fMethodDef && methodname == fMethodDef->fName) { @@ -1783,6 +1976,18 @@ string IncludeWriter::resolveMethod(const char* start, const char* end, bool fir return substitute; } +string IncludeWriter::resolveAlias(const Definition* def) { + for (auto child : def->fChildren) { + if (MarkType::kSubstitute == child->fMarkType) { + return string(child->fContentStart, (int) (child->fContentEnd - child->fContentStart)); + } + if (MarkType::kAlias == child->fMarkType && def->fName == child->fName) { + return this->resolveAlias(child); + } + } + return ""; +} + string IncludeWriter::resolveRef(const char* start, const char* end, bool first, RefType* refType) { // look up Xxx_Xxx @@ -1820,6 +2025,9 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, } } } + if (!rootDef && fEnumDef && "Sk" + prefixedName == fEnumDef->fFiddle) { + rootDef = fEnumDef; + } if (!rootDef && !substitute.length()) { auto aliasIter = fBmhParser->fAliasMap.find(undername); if (fBmhParser->fAliasMap.end() != aliasIter) { @@ -1837,17 +2045,9 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, } if (rootDef) { MarkType rootType = rootDef->fMarkType; - bool isTopic = MarkType::kSubtopic == rootType || MarkType::kTopic == rootType; - auto substituteParent = MarkType::kAlias == rootType ? rootDef->fParent : - isTopic ? rootDef : nullptr; - if (substituteParent) { - for (auto child : substituteParent->fChildren) { - if (MarkType::kSubstitute == child->fMarkType) { - substitute = string(child->fContentStart, - (int) (child->fContentEnd - child->fContentStart)); - break; - } - } + if (MarkType::kSubtopic == rootType || MarkType::kTopic == rootType + || MarkType::kAlias == rootType) { + substitute = this->resolveAlias(rootDef); } if (!substitute.length()) { string match = rootDef->fName; @@ -1856,7 +2056,7 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, match.erase(index, 1); } string skmatch = "Sk" + match; - auto parent = substituteParent ? substituteParent : rootDef; + auto parent = MarkType::kAlias == rootType ? rootDef->fParent : rootDef; for (auto child : parent->fChildren) { // there may be more than one // prefer the one mostly closely matching in text @@ -1910,6 +2110,22 @@ string IncludeWriter::resolveRef(const char* start, const char* end, bool first, substitute += ' '; substitute += ConvertRef(rootDef->fName, false); } else { + size_t underpos = undername.find('_'); + if (string::npos != underpos) { + string parentName = undername.substr(0, underpos); + string skName = "Sk" + parentName; + if (skName == parent->fName) { + SkASSERT(start >= fLastDescription->fContentStart); + string lastDescription = string(fLastDescription->fContentStart, + (int) (start - fLastDescription->fContentStart)); + size_t lineStart = lastDescription.rfind('\n'); + SkASSERT(string::npos != lineStart); + fLine = fLastDescription->fContentStart + lineStart + 1; + fChar = start; + fEnd = end; + return this->reportError("remove underline"); + } + } substitute += ConvertRef(undername, first); } } diff --git a/tools/bookmaker/parserCommon.cpp b/tools/bookmaker/parserCommon.cpp index 3733aed366..652df53e51 100644 --- a/tools/bookmaker/parserCommon.cpp +++ b/tools/bookmaker/parserCommon.cpp @@ -102,7 +102,8 @@ bool ParserCommon::parseSetup(const char* path) { return true; } -void ParserCommon::writeBlockIndent(int size, const char* data) { +bool ParserCommon::writeBlockIndent(int size, const char* data) { + bool wroteSomething = false; while (size && ' ' >= data[size - 1]) { --size; } @@ -113,7 +114,7 @@ void ParserCommon::writeBlockIndent(int size, const char* data) { --size; } if (!size) { - return; + return wroteSomething; } if (newLine) { this->lf(1); @@ -130,7 +131,9 @@ void ParserCommon::writeBlockIndent(int size, const char* data) { size -= len; data += len; newLine = true; + wroteSomething = true; } + return wroteSomething; } bool ParserCommon::writeBlockTrim(int size, const char* data) { -- cgit v1.2.3