docs with more pop
Replace a hunk of documentation in docs/*.bmh
with #Populate, which instructs bookmaker to
retrieve the documentation from include/core.
Check spelling for all documentation retrieved
from include/core against Skia declarations
and a list of words in spelling.txt.
TBR=caryclark@google.com
Docs-Preview: https://skia.org/?cl=163491
Bug: skia:
Change-Id: If057c3a1336e312ad59c084a3a130f0276802496
Reviewed-on: https://skia-review.googlesource.com/c/163491
Commit-Queue: Cary Clark <caryclark@skia.org>
Reviewed-by: Cary Clark <caryclark@skia.org>
Auto-Submit: Cary Clark <caryclark@skia.org>
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp
index 7f0b9f9..beb7dd6 100644
--- a/tools/bookmaker/bookmaker.cpp
+++ b/tools/bookmaker/bookmaker.cpp
@@ -12,6 +12,8 @@
#include <Windows.h>
#endif
+const string kSpellingFileName("spelling.txt");
+
DEFINE_string2(status, a, "", "File containing status of documentation. (Use in place of -b -i)");
DEFINE_string2(bmh, b, "", "Path to a *.bmh file or a directory.");
DEFINE_bool2(catalog, c, false, "Write example catalog.htm. (Requires -b -f -r)");
@@ -32,6 +34,8 @@
DEFINE_bool2(validate, V, false, "Validate that all anchor references have definitions. (Requires -r)");
DEFINE_bool2(skip, z, false, "Skip degenerate missed in legacy preprocessor.");
+// -b docs -i include/core/SkRect.h -f fiddleout.json -r site/user/api
+// -b docs/SkIRect_Reference.bmh -H
/* todos:
if #Subtopic contains #SeeAlso or #Example generate horizontal rule at end
@@ -217,6 +221,9 @@
if (nullptr == fRoot) {
fRoot = this->findBmhObject(markType, name);
fRoot->fFileName = fFileName;
+ fRoot->fName = name;
+ fRoot->fNames.fName = name;
+ fRoot->fNames.fParent = &fGlobalNames;
definition = fRoot;
} else {
if (nullptr == fParent) {
@@ -241,6 +248,10 @@
(fRoot->fBranches)[name] = childRoot;
childRoot->setRootParent(fRoot);
childRoot->fFileName = fFileName;
+ SkASSERT(MarkType::kSubtopic != fRoot->fMarkType
+ && MarkType::kTopic != fRoot->fMarkType);
+ childRoot->fNames.fName = name;
+ childRoot->fNames.fParent = &fRoot->fNames;
fRoot = childRoot;
definition = fRoot;
} else {
@@ -353,6 +364,7 @@
definition->fName += typeNameBuilder[0];
definition->fFiddle = parent->fFiddle + '_';
}
+ rootDefinition->fNames.fName = rootDefinition->fName;
definition->fFiddle += Definition::NormalizedName(typeNameBuilder[0]);
this->setAsParent(definition);
}
@@ -897,7 +909,7 @@
}
this->skipToAlpha();
const char* wordStart = fChar;
- this->skipToNonName();
+ this->skipToWhiteSpace();
if (fChar - wordStart > 0) {
fExternals.emplace_front(MarkType::kExternal, wordStart, fChar, fLineCount, fParent,
fMC);
@@ -1225,6 +1237,237 @@
return result;
}
+string BmhParser::loweredTopic(string name, Definition* def) {
+ string lowered;
+ SkASSERT('_' != name[0]);
+ char last = '_';
+ for (char c : name) {
+ SkASSERT(' ' != c);
+ if (isupper(last)) {
+ lowered += islower(c) ? tolower(last) : last;
+ last = '\0';
+ }
+ if ('_' == c) {
+ last = c;
+ c = ' ';
+ } else if ('_' == last && isupper(c)) {
+ last = c;
+ continue;
+ }
+ lowered += c;
+ if (' ' == c) {
+ this->setUpPartialSubstitute(lowered);
+ }
+ }
+ if (isupper(last)) {
+ lowered += tolower(last);
+ }
+ return lowered;
+}
+
+void BmhParser::setUpGlobalSubstitutes() {
+ for (auto& entry : fExternals) {
+ string externalName = entry.fName;
+ SkASSERT(fGlobalNames.fRefMap.end() == fGlobalNames.fRefMap.find(externalName));
+ fGlobalNames.fRefMap[externalName] = nullptr;
+ }
+ for (auto bMap : { &fClassMap, &fConstMap, &fDefineMap, &fEnumMap, &fMethodMap,
+ &fTypedefMap } ) {
+ for (auto& entry : *bMap) {
+ Definition* parent = (Definition*) &entry.second;
+ string name = parent->fName;
+ SkASSERT(fGlobalNames.fLinkMap.end() == fGlobalNames.fLinkMap.find(name));
+ string ref = ParserCommon::HtmlFileName(parent->fFileName) + '#' + parent->fFiddle;
+ fGlobalNames.fLinkMap[name] = ref;
+ SkASSERT(fGlobalNames.fRefMap.end() == fGlobalNames.fRefMap.find(name));
+ fGlobalNames.fRefMap[name] = const_cast<Definition*>(parent);
+ NameMap* names = MarkType::kClass == parent->fMarkType
+ || MarkType::kStruct == parent->fMarkType
+ || MarkType::kEnumClass == parent->fMarkType ? &parent->asRoot()->fNames :
+ &fGlobalNames;
+ this->setUpSubstitutes(parent, names);
+ if (names != &fGlobalNames) {
+ names->copyToParent(&fGlobalNames);
+ }
+ }
+ }
+ for (auto& topic : fTopicMap) {
+ bool hasSubstitute = false;
+ for (auto& child : topic.second->fChildren) {
+ bool isAlias = MarkType::kAlias == child->fMarkType;
+ bool isSubstitute = MarkType::kSubstitute == child->fMarkType;
+ if (!isAlias && !isSubstitute) {
+ continue;
+ }
+ hasSubstitute |= isSubstitute;
+ string name(child->fContentStart, child->length());
+ if (isAlias) {
+ name = ParserCommon::ConvertRef(name, false);
+ for (auto aliasChild : child->fChildren) {
+ if (MarkType::kSubstitute == aliasChild->fMarkType) {
+ string sub(aliasChild->fContentStart, aliasChild->length());
+ this->setUpSubstitute(sub, topic.second);
+ }
+ }
+ }
+ this->setUpSubstitute(name, topic.second);
+ }
+ if (hasSubstitute) {
+ continue;
+ }
+ string lowered = this->loweredTopic(topic.first, topic.second);
+ SkDEBUGCODE(auto globalIter = fGlobalNames.fLinkMap.find(lowered));
+ SkASSERT(fGlobalNames.fLinkMap.end() == globalIter);
+ fGlobalNames.fLinkMap[lowered] =
+ ParserCommon::HtmlFileName(topic.second->fFileName) + '#' + topic.first;
+ SkASSERT(fGlobalNames.fRefMap.end() == fGlobalNames.fRefMap.find(lowered));
+ fGlobalNames.fRefMap[lowered] = topic.second;
+ }
+ size_t slash = fRawFilePathDir.rfind('/');
+ size_t bslash = fRawFilePathDir.rfind('\\');
+ string spellFile;
+ if (string::npos == slash && string::npos == bslash) {
+ spellFile = fRawFilePathDir;
+ } else {
+ if (string::npos != bslash && bslash > slash) {
+ slash = bslash;
+ }
+ spellFile = fRawFilePathDir.substr(0, slash);
+ }
+ spellFile += '/';
+ spellFile += kSpellingFileName;
+ FILE* file = fopen(spellFile.c_str(), "r");
+ if (!file) {
+ SkDebugf("missing %s\n", spellFile.c_str());
+ return;
+ }
+ fseek(file, 0L, SEEK_END);
+ int sz = (int) ftell(file);
+ rewind(file);
+ char* buffer = new char[sz];
+ memset(buffer, ' ', sz);
+ int read = (int)fread(buffer, 1, sz, file);
+ SkAssertResult(read > 0);
+ sz = read; // FIXME: ? why are sz and read different?
+ fclose(file);
+ int i = 0;
+ int start = i;
+ string word;
+ do {
+ if (' ' < buffer[i]) {
+ ++i;
+ continue;
+ }
+ word = string(&buffer[start], i - start);
+ if (fGlobalNames.fRefMap.end() == fGlobalNames.fRefMap.find(word)) {
+ fGlobalNames.fRefMap[word] = nullptr;
+ } else {
+ SkDebugf("%s ", word.c_str()); // debugging: word missing from spelling list
+ }
+ do {
+ ++i;
+ } while (i < sz && ' ' >= buffer[i]);
+ start = i;
+ } while (i < sz);
+ delete[] buffer;
+}
+
+void BmhParser::setUpSubstitutes(const Definition* parent, NameMap* names) {
+ for (const auto& child : parent->fChildren) {
+ MarkType markType = child->fMarkType;
+ if (MarkType::kAlias == markType) {
+ continue;
+ }
+ if (MarkType::kSubstitute == markType) {
+ continue;
+ }
+ string name(child->fName);
+ if (&fGlobalNames != names) {
+ size_t lastDC = name.rfind("::");
+ if (string::npos != lastDC) {
+ name = name.substr(lastDC + 2);
+ }
+ if ("" == name) {
+ continue;
+ }
+ }
+ size_t lastUnder = name.rfind('_');
+ if (string::npos != lastUnder && ++lastUnder < name.length()) {
+ bool numbers = true;
+ for (size_t index = lastUnder; index < name.length(); ++index) {
+ numbers &= (bool) isdigit(name[index]);
+ }
+ if (numbers) {
+ continue;
+ }
+ }
+ string ref;
+ if (&fGlobalNames == names) {
+ ref = ParserCommon::HtmlFileName(child->fFileName);
+ }
+ ref += '#' + child->fFiddle;
+ if (MarkType::kClass == markType || MarkType::kStruct == markType
+ || MarkType::kMethod == markType || MarkType::kEnum == markType
+ || MarkType::kEnumClass == markType || MarkType::kConst == markType
+ || MarkType::kMember == markType || MarkType::kDefine == markType
+ || MarkType::kTypedef == markType) {
+ SkASSERT(names->fLinkMap.end() == names->fLinkMap.find(name));
+ names->fLinkMap[name] = ref;
+ SkASSERT(names->fRefMap.end() == names->fRefMap.find(name));
+ names->fRefMap[name] = child;
+ }
+ if (MarkType::kClass == markType || MarkType::kStruct == markType
+ || MarkType::kEnumClass == markType) {
+ RootDefinition* rootDef = child->asRoot();
+ NameMap* nameMap = &rootDef->fNames;
+ this->setUpSubstitutes(child, nameMap);
+ nameMap->copyToParent(names);
+ }
+ if (MarkType::kEnum == markType) {
+ this->setUpSubstitutes(child, names);
+ }
+ if (MarkType::kSubtopic == markType) {
+ if (&fGlobalNames != names && string::npos != child->fName.find('_')) {
+ string lowered = this->loweredTopic(child->fName, child);
+ SkDEBUGCODE(auto refIter = names->fRefMap.find(lowered));
+ SkDEBUGCODE(auto iter = names->fLinkMap.find(lowered));
+ SkASSERT(names->fLinkMap.end() == iter);
+ names->fLinkMap[lowered] = '#' + child->fName;
+ SkASSERT(names->fRefMap.end() == refIter);
+ names->fRefMap[lowered] = child;
+ }
+ this->setUpSubstitutes(child, names);
+ }
+ }
+}
+
+void BmhParser::setUpPartialSubstitute(string name) {
+ auto iter = fGlobalNames.fRefMap.find(name);
+ if (fGlobalNames.fRefMap.end() != iter) {
+ SkASSERT(nullptr == iter->second);
+ return;
+ }
+ fGlobalNames.fRefMap[name] = nullptr;
+}
+
+void BmhParser::setUpSubstitute(string name, Definition* def) {
+ SkASSERT(fGlobalNames.fRefMap.end() == fGlobalNames.fRefMap.find(name));
+ fGlobalNames.fRefMap[name] = def;
+ SkASSERT(fGlobalNames.fLinkMap.end() == fGlobalNames.fLinkMap.find(name));
+ string str = ParserCommon::HtmlFileName(def->fFileName) + '#' + def->fName;
+ fGlobalNames.fLinkMap[name] = str;
+ size_t stop = name.length();
+ do {
+ size_t space = name.rfind(' ', stop);
+ if (string::npos == space) {
+ break;
+ }
+ string partial = name.substr(0, space + 1);
+ stop = space - 1;
+ this->setUpPartialSubstitute(partial);
+ } while (true);
+}
+
void BmhParser::setWrapper(Definition* def) const {
const char drawWrapper[] = "void draw(SkCanvas* canvas) {";
const char drawNoCanvas[] = "void draw(SkCanvas* ) {";
@@ -1489,9 +1732,8 @@
return MarkType::kNone;
}
- // write #In to show containing #Topic
- // write #Line with one liner from Member_Functions, Constructors, Operators if method,
- // from Constants if enum, otherwise from #Subtopic containing match
+ // replace #Method description, #Param, #Return with #Populate
+ // if description, params, return are free of phrase refs
bool HackParser::hackFiles() {
string filename(fFileName);
size_t len = filename.length() - 1;
@@ -1511,7 +1753,10 @@
return false;
}
auto mapEntry = fBmhParser.fClassMap.find(className);
- SkASSERT(fBmhParser.fClassMap.end() != mapEntry);
+ if (fBmhParser.fClassMap.end() == mapEntry) {
+ remove(filename.c_str());
+ return true;
+ }
const Definition* classMarkup = &mapEntry->second;
const Definition* root = classMarkup->fParent;
SkASSERT(root);
@@ -1520,16 +1765,12 @@
SkASSERT(!root->fParent);
fStart = root->fStart;
fChar = fStart;
- fClasses = nullptr;
- fConstants = nullptr;
- fConstructors = nullptr;
- fMemberFunctions = nullptr;
- fMembers = nullptr;
- fOperators = nullptr;
- fRelatedFunctions = nullptr;
- fStructs = nullptr;
- this->topicIter(root);
- fprintf(fOut, "%.*s", (int) (fEnd - fChar), fChar);
+ fEnd = root->fTerminator;
+ this->replaceWithPop(root);
+ FPRINTF("%.*s", (int) (fEnd - fChar), fChar);
+ if ('\n' != fEnd[-1]) {
+ FPRINTF("\n");
+ }
fclose(fOut);
if (ParserCommon::WrittenFileDiffers(filename, root->fFileName)) {
SkDebugf("wrote %s\n", filename.c_str());
@@ -1539,186 +1780,59 @@
return true;
}
-string HackParser::searchTable(const Definition* tableHolder, const Definition* match) {
- if (!tableHolder) {
- return "";
- }
- string bestMatch;
- string result;
- for (auto table : tableHolder->fChildren) {
- if (MarkType::kTable == table->fMarkType) {
- for (auto row : table->fChildren) {
- if (MarkType::kRow == row->fMarkType) {
- const Definition* col0 = row->fChildren[0];
- size_t len = col0->fContentEnd - col0->fContentStart;
- string method = string(col0->fContentStart, len);
- if (len - 2 == method.find("()") && islower(method[0])
- && Definition::MethodType::kOperator != match->fMethodType) {
- method = method.substr(0, len - 2);
- }
- if (string::npos == match->fName.find(method)) {
- continue;
- }
- if (bestMatch.length() < method.length()) {
- bestMatch = method;
- const Definition * col1 = row->fChildren[1];
- if (col1->fContentEnd <= col1->fContentStart) {
- SkASSERT(string::npos != col1->fFileName.find("SkImageInfo"));
- result = "incomplete";
- } else {
- result = string(col1->fContentStart, col1->fContentEnd -
- col1->fContentStart);
- }
- }
- }
- }
- }
- }
- return result;
-}
-
// returns true if topic has method
-void HackParser::topicIter(const Definition* topic) {
- if (string::npos != topic->fName.find(SubtopicKeys::kClasses)) {
- SkASSERT(!fClasses);
- fClasses = topic;
- }
- if (string::npos != topic->fName.find(SubtopicKeys::kStructs)) {
- SkASSERT(!fStructs);
- fStructs = topic;
- }
- if (string::npos != topic->fName.find(SubtopicKeys::kConstants)) {
- SkASSERT(!fConstants);
- fConstants = topic;
- }
- if (string::npos != topic->fName.find(SubtopicKeys::kConstructors)) {
- SkASSERT(!fConstructors);
- fConstructors = topic;
- }
- if (string::npos != topic->fName.find(SubtopicKeys::kMemberFunctions)) {
- SkASSERT(!fMemberFunctions);
- fMemberFunctions = topic;
- }
- if (string::npos != topic->fName.find(SubtopicKeys::kMembers)) {
- SkASSERT(!fMembers);
- fMembers = topic;
- }
- if (string::npos != topic->fName.find(SubtopicKeys::kOperators)) {
- SkASSERT(!fOperators);
- fOperators = topic;
- }
- if (string::npos != topic->fName.find(SubtopicKeys::kRelatedFunctions)) {
- SkASSERT(!fRelatedFunctions);
- fRelatedFunctions = topic;
- }
- for (auto child : topic->fChildren) {
- string oneLiner;
- bool hasIn = false;
- bool hasLine = false;
- for (auto part : child->fChildren) {
- hasIn |= MarkType::kIn == part->fMarkType;
- hasLine |= MarkType::kLine == part->fMarkType;
+void HackParser::replaceWithPop(const Definition* root) {
+ for (auto child : root->fChildren) {
+ if (MarkType::kClass == child->fMarkType || MarkType::kStruct == child->fMarkType
+ || MarkType::kSubtopic == child->fMarkType) {
+ this->replaceWithPop(child);
}
- switch (child->fMarkType) {
- case MarkType::kMethod: {
- hasIn |= MarkType::kTopic != topic->fMarkType &&
- MarkType::kSubtopic != topic->fMarkType; // don't write #In if parent is class
- hasLine |= child->fClone;
- if (!hasLine) {
- // find member_functions, add entry 2nd column text to #Line
- for (auto tableHolder : { fMemberFunctions, fConstructors, fOperators }) {
- if (!tableHolder) {
- continue;
- }
- if (Definition::MethodType::kConstructor == child->fMethodType
- && fConstructors != tableHolder) {
- continue;
- }
- if (Definition::MethodType::kOperator == child->fMethodType
- && fOperators != tableHolder) {
- continue;
- }
- string temp = this->searchTable(tableHolder, child);
- if ("" != temp) {
- SkASSERT("" == oneLiner || temp == oneLiner);
- oneLiner = temp;
- }
- }
- if ("" == oneLiner) {
- #ifdef SK_DEBUG
- const Definition* rootParent = topic;
- while (rootParent->fParent && MarkType::kClass != rootParent->fMarkType
- && MarkType::kStruct != rootParent->fMarkType) {
- rootParent = rootParent->fParent;
- }
- #endif
- SkASSERT(rootParent);
- SkASSERT(MarkType::kClass == rootParent->fMarkType
- || MarkType::kStruct == rootParent->fMarkType);
- hasLine = true;
- }
- }
-
- if (hasIn && hasLine) {
- continue;
- }
- const char* start = fChar;
- const char* end = child->fContentStart;
- fprintf(fOut, "%.*s", (int) (end - start), start);
- fChar = end;
- // write to method markup header end
- if (!hasIn) {
- fprintf(fOut, "\n#In %s", topic->fName.c_str());
- }
- if (!hasLine) {
- fprintf(fOut, "\n#Line # %s ##", oneLiner.c_str());
- }
- } break;
- case MarkType::kTopic:
- case MarkType::kSubtopic:
- this->addOneLiner(fRelatedFunctions, child, hasLine, true);
- this->topicIter(child);
- break;
- case MarkType::kStruct:
- this->addOneLiner(fStructs, child, hasLine, false);
- this->topicIter(child);
- break;
- case MarkType::kClass:
- this->addOneLiner(fClasses, child, hasLine, false);
- this->topicIter(child);
- break;
- case MarkType::kEnum:
- case MarkType::kEnumClass:
- this->addOneLiner(fConstants, child, hasLine, true);
- break;
- case MarkType::kMember:
- this->addOneLiner(fMembers, child, hasLine, false);
- break;
- default:
- ;
+ if (MarkType::kMethod != child->fMarkType) {
+ continue;
}
- }
-}
-
-void HackParser::addOneLiner(const Definition* defTable, const Definition* child, bool hasLine,
- bool lfAfter) {
- if (hasLine) {
- return;
- }
- string oneLiner = this->searchTable(defTable, child);
- if ("" == oneLiner) {
- return;
- }
- const char* start = fChar;
- const char* end = child->fContentStart;
- fprintf(fOut, "%.*s", (int) (end - start), start);
- fChar = end;
- if (!lfAfter) {
- fprintf(fOut, "\n");
- }
- fprintf(fOut, "#Line # %s ##", oneLiner.c_str());
- if (lfAfter) {
- fprintf(fOut, "\n");
+ auto& grans = child->fChildren;
+ if (grans.end() != std::find_if(grans.begin(), grans.end(),
+ [](const Definition* def) {
+ return MarkType::kPopulate == def->fMarkType
+ || MarkType::kPhraseRef == def->fMarkType
+ || MarkType::kFormula == def->fMarkType
+ || MarkType::kAnchor == def->fMarkType
+ || MarkType::kList == def->fMarkType
+ || MarkType::kTable == def->fMarkType
+ || MarkType::kDeprecated == def->fMarkType
+ || MarkType::kExperimental == def->fMarkType
+ || MarkType::kPrivate == def->fMarkType;
+ } )) {
+ continue;
+ }
+ // write #Populate in place of description, #Param(s), #Return (if present)
+ const char* keep = child->fContentStart;
+ const char* next = nullptr;
+ for (auto gran : grans) {
+ if (MarkType::kIn == gran->fMarkType || MarkType::kLine == gran->fMarkType) {
+ keep = gran->fTerminator;
+ continue;
+ }
+ if (MarkType::kExample == gran->fMarkType
+ || MarkType::kNoExample == gran->fMarkType) {
+ next = gran->fStart;
+ break;
+ }
+ if (MarkType::kParam == gran->fMarkType
+ || MarkType::kReturn == gran->fMarkType
+ || MarkType::kToDo == gran->fMarkType
+ || MarkType::kComment == gran->fMarkType) {
+ continue;
+ }
+ SkDebugf(""); // convenient place to set a breakpoint
+ }
+ SkASSERT(next);
+ FPRINTF("%.*s", (int) (keep - fChar), fChar);
+ if ('\n' != keep[-1]) {
+ FPRINTF("\n");
+ }
+ FPRINTF("#Populate\n\n");
+ fChar = next;
}
}
@@ -2674,13 +2788,20 @@
}
}
if (FLAGS_hack) {
- if (FLAGS_bmh.isEmpty()) {
- SkDebugf("-k or --hack requires -b\n");
+ if (FLAGS_bmh.isEmpty() && FLAGS_status.isEmpty()) {
+ SkDebugf("-H or --hack requires -a or -b\n");
SkCommandLineFlags::PrintUsage();
return 1;
}
HackParser hacker(bmhParser);
- if (!hacker.parseFile(FLAGS_bmh[0], ".bmh", ParserCommon::OneFile::kNo)) {
+ hacker.fDebugOut = FLAGS_stdout;
+ if (!FLAGS_status.isEmpty() && !hacker.parseStatus(FLAGS_status[0], ".bmh",
+ StatusFilter::kInProgress)) {
+ SkDebugf("hack failed\n");
+ return -1;
+ }
+ if (!FLAGS_bmh.isEmpty() && !hacker.parseFile(FLAGS_bmh[0], ".bmh",
+ ParserCommon::OneFile::kNo)) {
SkDebugf("hack failed\n");
return -1;
}
@@ -2827,3 +2948,18 @@
}
return 0;
}
+
+void NameMap::copyToParent(NameMap* parent) const {
+ size_t colons = fName.rfind("::");
+ string topName = string::npos == colons ? fName : fName.substr(colons + 2);
+ for (auto& entry : fRefMap) {
+ string scoped = topName + "::" + entry.first;
+ SkASSERT(parent->fRefMap.end() == parent->fRefMap.find(scoped));
+ parent->fRefMap[scoped] = entry.second;
+ auto scopedLinkIter = fLinkMap.find(entry.first);
+ if (fLinkMap.end() != scopedLinkIter) {
+ SkASSERT(parent->fLinkMap.end() == parent->fLinkMap.find(scoped));
+ parent->fLinkMap[scoped] = scopedLinkIter->second;
+ }
+ }
+}
diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h
index 950bad7..5a72315 100644
--- a/tools/bookmaker/bookmaker.h
+++ b/tools/bookmaker/bookmaker.h
@@ -21,6 +21,13 @@
#include <unordered_map>
#include <vector>
+#define FPRINTF(...) \
+ if (fDebugOut) { \
+ SkDebugf(__VA_ARGS__); \
+ } \
+ fprintf(fOut, __VA_ARGS__)
+
+
// std::to_string isn't implemented on android
#include <sstream>
@@ -290,6 +297,19 @@
return nullptr;
}
+ bool back(const char* pattern) {
+ size_t len = strlen(pattern);
+ const char* start = fChar - len;
+ if (start <= fStart) {
+ return false;
+ }
+ if (strncmp(start, pattern, len)) {
+ return false;
+ }
+ fChar = start;
+ return true;
+ }
+
char backup(const char* pattern) const {
size_t len = strlen(pattern);
const char* start = fChar - len;
@@ -302,6 +322,12 @@
return start[-1];
}
+ void backupWord() {
+ while (fChar > fStart && isalpha(fChar[-1])) {
+ --fChar;
+ }
+ }
+
bool contains(const char* match, const char* lineEnd, const char** loc) const {
const char* result = this->strnstr(match, lineEnd);
if (loc) {
@@ -310,6 +336,25 @@
return result;
}
+ bool containsWord(const char* match, const char* lineEnd, const char** loc) {
+ size_t len = strlen(match);
+ do {
+ const char* result = this->strnstr(match, lineEnd);
+ if (!result) {
+ return false;
+ }
+ if ((result > fStart && isalnum(result[-1])) || (result + len < fEnd
+ && isalnum(result[len]))) {
+ fChar = result + len;
+ continue;
+ }
+ if (loc) {
+ *loc = result;
+ }
+ return true;
+ } while (true);
+ }
+
// either /n/n or /n# will stop parsing a typedef
const char* doubleLF() const {
const char* ptr = fChar - 1;
@@ -484,14 +529,22 @@
// differs from skipToNonAlphaNum in that a.b isn't considered a full name,
// since a.b can't be found as a named definition
void skipFullName() {
- while (fChar < fEnd && (isalnum(fChar[0])
- || '_' == fChar[0] /* || '-' == fChar[0] */
- || (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]))) {
- if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
+ do {
+ char last = '\0';
+ while (fChar < fEnd && (isalnum(fChar[0])
+ || '_' == fChar[0] /* || '-' == fChar[0] */
+ || (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1]))) {
+ if (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1]) {
+ fChar++;
+ }
+ last = fChar[0];
fChar++;
}
- fChar++;
- }
+ if (fChar + 1 >= fEnd || '/' != fChar[0] || !isalpha(last) || !isalpha(fChar[1])) {
+ break; // stop unless pattern is xxx/xxx as in I/O
+ }
+ fChar++; // skip slash
+ } while (true);
}
int skipToLineBalance(char open, char close) {
@@ -1091,6 +1144,16 @@
static const char* kGeneratedSubtopics[];
};
+struct NameMap {
+ void copyToParent(NameMap* parent) const;
+
+ string fName;
+ NameMap* fParent = nullptr;
+ unordered_map<string, string> fLinkMap; // from SkRect to #Rect
+ // ref map includes "xxx", "xxx ", "xxx yyy", "xxx zzz", etc.
+ unordered_map<string, Definition*> fRefMap; // e.g., from #Substitute entry to #Topic entry
+};
+
class RootDefinition : public Definition {
public:
enum class AllowParens {
@@ -1112,10 +1175,16 @@
RootDefinition(MarkType markType, const char* start, int line, Definition* parent, char mc)
: Definition(markType, start, line, parent, mc) {
+ if (MarkType::kSubtopic != markType && MarkType::kTopic != markType) {
+ if (parent) {
+ fNames.fName = parent->fName;
+ fNames.fParent = &parent->asRoot()->fNames;
+ }
+ }
}
RootDefinition(MarkType markType, const char* start, const char* end, int line,
- Definition* parent, char mc) : Definition(markType, start, end, line, parent, mc) {
+ Definition* parent, char mc) : Definition(markType, start, end, line, parent, mc) {
}
~RootDefinition() override {
@@ -1141,6 +1210,7 @@
unordered_map<string, RootDefinition*> fBranches;
unordered_map<string, Definition> fLeaves;
unordered_map<string, SubtopicContents> fPopulators;
+ NameMap fNames;
private:
RootDefinition* fRootParent = nullptr;
};
@@ -1219,9 +1289,10 @@
}
void checkLineLength(size_t len, const char* str);
+ static string ConvertRef(const string str, bool first);
static void CopyToFile(string oldFile, string newFile);
-
static char* FindDateTime(char* buffer, int size);
+ static string HtmlFileName(string bmhFileName);
void indentIn(IndentKind kind) {
fIndent += 4;
@@ -1239,10 +1310,7 @@
SkASSERT(column >= fColumn);
SkASSERT(!fReturnOnWrite);
SkASSERT(column < 80);
- if (fDebugOut) {
- SkDebugf("%*s", column - fColumn, "");
- }
- fprintf(fOut, "%*s", column - fColumn, "");
+ FPRINTF("%*s", column - fColumn, "");
fColumn = column;
fSpaces += column - fColumn;
}
@@ -1384,6 +1452,7 @@
vector<IndentState> fIndentStack;
Definition* fParent;
FILE* fOut;
+ string fRawFilePathDir;
int fLinefeeds; // number of linefeeds last written, zeroed on non-space
int fMaxLF; // number of linefeeds allowed
int fPendingLF; // number of linefeeds to write (can be suppressed)
@@ -1455,6 +1524,7 @@
kLiteral, // output untouched
kClone, // resolved, output, with references to clones as well
kSimple, // resolve simple words (used to resolve method declarations)
+ kInclude, // like simple, plus reverse resolve SkXXX to XXX
};
enum class ExampleOptions {
@@ -1482,7 +1552,7 @@
enum class TrimExtract {
kNo,
- kYes
+ kYes,
};
BmhParser(bool skip) : ParserCommon()
@@ -1524,6 +1594,7 @@
MarkType getMarkType(MarkLookup lookup) const;
bool hasEndToken() const;
static bool IsExemplary(const Definition* );
+ string loweredTopic(string name, Definition* def);
string memberName();
string methodName();
const Definition* parentSpace() const;
@@ -1558,6 +1629,10 @@
fCheckMethods = false;
}
+ void setUpGlobalSubstitutes();
+ void setUpPartialSubstitute(string name);
+ void setUpSubstitute(string name, Definition* def);
+ void setUpSubstitutes(const Definition* parent, NameMap* );
void setWrapper(Definition* def) const;
bool skipNoName();
bool skipToDefinitionEnd(MarkType markType);
@@ -1602,6 +1677,7 @@
unordered_map<string, Definition*> fTopicMap;
unordered_map<string, Definition*> fAliasMap;
unordered_map<string, Definition*> fPhraseMap;
+ NameMap fGlobalNames;
RootDefinition* fRoot;
Definition* fWorkingColumn;
Definition* fRow;
@@ -2363,9 +2439,6 @@
this->reset();
}
- void addOneLiner(const Definition* defTable, const Definition* child, bool hasLine,
- bool lfAfter);
-
bool parseFromFile(const char* path) override {
if (!INHERITED::parseSetup(path)) {
return false;
@@ -2377,20 +2450,10 @@
INHERITED::resetCommon();
}
- string searchTable(const Definition* tableHolder, const Definition* match);
-
- void topicIter(const Definition* );
+ void replaceWithPop(const Definition* );
private:
const BmhParser& fBmhParser;
- const Definition* fClasses;
- const Definition* fConstants;
- const Definition* fConstructors;
- const Definition* fMemberFunctions;
- const Definition* fMembers;
- const Definition* fOperators;
- const Definition* fRelatedFunctions;
- const Definition* fStructs;
bool hackFiles();
typedef ParserCommon INHERITED;
@@ -2410,6 +2473,7 @@
, fIncludeParser(inc) {
this->reset();
this->addPopulators();
+ fBmhParser.setUpGlobalSubstitutes();
}
bool buildReferences(const char* docDir, const char* mdOutDirOrFile);
@@ -2430,19 +2494,21 @@
void addCodeBlock(const Definition* def, string& str) const;
void addPopulators();
+ string addIncludeReferences(const char* refStart, const char* refEnd);
string addReferences(const char* start, const char* end, BmhParser::Resolvable );
string anchorDef(string def, string name);
string anchorLocalRef(string ref, string name);
string anchorRef(string def, string name);
-
bool buildRefFromFile(const char* fileName, const char* outDir);
bool checkParamReturnBody(const Definition* def);
Definition* checkParentsForMatch(Definition* test, string ref) const;
void childrenOut(Definition* def, const char* contentStart);
Definition* csParent();
+ bool findLink(string ref, string* link);
Definition* findParamType();
string getMemberTypeName(const Definition* def, string* memberType);
static bool HasDetails(const Definition* def);
+ bool hasWordSpace(string ) const;
void htmlOut(string );
Definition* isDefined(const TextParser& , string ref, BmhParser::Resolvable );
Definition* isDefinedByParent(RootDefinition* root, string ref);
@@ -2454,6 +2520,7 @@
void parameterHeaderOut(TextParser& paramParser, const Definition** prior, Definition* def);
void parameterTrailerOut();
bool parseFromFile(const char* path) override { return true; }
+ bool phraseContinues(string phrase, string* priorWord, string* priorLink) const;
void populateOne(Definition* def,
unordered_map<string, RootDefinition::SubtopicContents>& populator);
void populateTables(const Definition* def, RootDefinition* );
@@ -2522,7 +2589,7 @@
vector<const Definition*> fClassStack;
unordered_map<string, vector<AnchorDef> > fAllAnchorDefs;
unordered_map<string, vector<string> > fAllAnchorRefs;
-
+ NameMap* fNames;
BmhParser& fBmhParser;
IncludeParser& fIncludeParser;
const Definition* fEnumClass;
@@ -2604,6 +2671,7 @@
}
}
if (BmhParser::Resolvable::kSimple != resolvable
+ && BmhParser::Resolvable::kInclude != resolvable
&& (this->startsWith(name.c_str()) || this->startsWith("operator"))) {
const char* ptr = this->anyOf("\n (");
if (ptr && '(' == *ptr && strncmp(ptr, "(...", 4)) {
diff --git a/tools/bookmaker/definition.cpp b/tools/bookmaker/definition.cpp
index 52620f8..fb96ed1 100644
--- a/tools/bookmaker/definition.cpp
+++ b/tools/bookmaker/definition.cpp
@@ -912,7 +912,7 @@
if (!parser.skipExact("@param ")) {
continue;
}
- if (parser.skipExact(ref.c_str())) {
+ if (parser.skipExact(ref.c_str()) && ' ' == parser.peek()) {
return &iter;
}
}
diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp
index 0b4576c..2fdd7ae 100644
--- a/tools/bookmaker/includeParser.cpp
+++ b/tools/bookmaker/includeParser.cpp
@@ -1852,8 +1852,12 @@
}
Definition* IncludeParser::findMethod(const Definition& bmhDef) {
- auto doubleColon = bmhDef.fName.find("::");
- SkASSERT(string::npos != doubleColon); // more work to do to support global refs
+ auto doubleColon = bmhDef.fName.rfind("::");
+ if (string::npos == doubleColon) {
+ const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
+ SkASSERT(fIFunctionMap.end() != iGlobalMethod);
+ return iGlobalMethod->second;
+ }
string className = bmhDef.fName.substr(0, doubleColon);
const auto& iClass = fIClassMap.find(className);
SkASSERT(fIClassMap.end() != iClass);
@@ -1861,9 +1865,66 @@
auto& iTokens = iClass->second.fTokens;
const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
[methodName](Definition& token) {
- return MarkType::kMethod == token.fMarkType && methodName == token.fName; } );
- SkASSERT(iTokens.end() != iMethod);
- return &*iMethod;
+ return MarkType::kMethod == token.fMarkType
+ && (methodName == token.fName
+ || methodName == token.fName + "()"); } );
+ if (iTokens.end() != iMethod) {
+ return &*iMethod;
+ }
+ size_t subClassPos = className.rfind("::");
+ if (string::npos != subClassPos) {
+ className = className.substr(subClassPos + 2);
+ }
+ // match may be constructor; compare strings to see if this is so
+ SkASSERT(string::npos != methodName.find('('));
+ auto stripper = [](string s) -> string {
+ bool last = false;
+ string result;
+ for (char c : s) {
+ if (' ' >= c) {
+ if (!last) {
+ last = true;
+ result += ' ';
+ }
+ continue;
+ }
+ result += c;
+ last = false;
+ }
+ return result;
+ };
+ string strippedMethodName = stripper(methodName);
+ if (strippedMethodName == methodName) {
+ strippedMethodName = "";
+ }
+ const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
+ [className, methodName, stripper, strippedMethodName](Definition& token) {
+ if (MarkType::kMethod != token.fMarkType) {
+ return false;
+ }
+ TextParser parser(&token);
+ const char* match = parser.strnstr(className.c_str(), parser.fEnd);
+ if (!match) {
+ return false;
+ }
+ parser.skipTo(match);
+ parser.skipExact(className.c_str());
+ if ('(' != parser.peek()) {
+ return false;
+ }
+ parser.skipToBalancedEndBracket('(', ')');
+ string iMethodName(match, parser.fChar - match);
+ if (methodName == iMethodName) {
+ return true;
+ }
+ if ("" == strippedMethodName) {
+ return false;
+ }
+ string strippedIName = stripper(iMethodName);
+ return strippedIName == strippedMethodName;
+ } );
+ SkAssertResult(iTokens.end() != cMethod);
+ return &*cMethod;
}
Definition* IncludeParser::parentBracket(Definition* parent) const {
@@ -2570,7 +2631,6 @@
markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
markupDef, '\0');
Definition* markupChild = &markupDef->fTokens.back();
- // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
{
auto mapIter = fIClassMap.find(markupDef->fName);
SkASSERT(fIClassMap.end() != mapIter);
diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp
index 768ccf6..ef854a7 100644
--- a/tools/bookmaker/includeWriter.cpp
+++ b/tools/bookmaker/includeWriter.cpp
@@ -895,7 +895,7 @@
int commentIndex = child.fParentIndex;
auto iter = child.fParent->fTokens.begin();
std::advance(iter, commentIndex);
- SkDEBUGCODE(bool sawMethod = false);
+ SkDEBUGCODE(bool sawMethod = MarkType::kMethod == iter->fMarkType);
while (--commentIndex >= 0) {
std::advance(iter, -1);
if (Bracket::kSlashStar == iter->fBracket) {
@@ -2157,21 +2157,6 @@
return allPassed;
}
-// change Xxx_Xxx to xxx xxx
-static string ConvertRef(const string str, bool first) {
- string substitute;
- for (char c : str) {
- if ('_' == c) {
- c = ' '; // change Xxx_Xxx to xxx xxx
- } else if (isupper(c) && !first) {
- c = tolower(c);
- }
- substitute += c;
- first = false;
- }
- return substitute;
-}
-
string IncludeWriter::resolveMethod(const char* start, const char* end, bool first) {
string methodname(start, end - start);
if (string::npos != methodname.find("()")) {
@@ -2322,6 +2307,7 @@
// prefer the one mostly closely matching in text
if ((MarkType::kClass == child->fMarkType ||
MarkType::kStruct == child->fMarkType ||
+ MarkType::kTypedef == child->fMarkType ||
(MarkType::kEnum == child->fMarkType && !child->fAnonymous) ||
MarkType::kEnumClass == child->fMarkType) && (match == child->fName ||
skmatch == child->fName)) {
@@ -2372,7 +2358,7 @@
if (parent->fParent != fRootTopic) {
substitute = parent->fName;
substitute += ' ';
- substitute += ConvertRef(rootDef->fName, false);
+ substitute += ParserCommon::ConvertRef(rootDef->fName, false);
} else {
size_t underpos = undername.find('_');
if (string::npos != underpos) {
@@ -2390,7 +2376,7 @@
return this->reportError<string>("remove underline");
}
}
- substitute += ConvertRef(undername, first);
+ substitute += ParserCommon::ConvertRef(undername, first);
}
}
}
@@ -2454,7 +2440,7 @@
string temp = this->resolveRef(&data[start], &data[end], Word::kFirst == word, &refType);
if (!temp.length()) {
if (Word::kFirst != word && '_' != last) {
- temp = ConvertRef(resolved, false);
+ temp = ParserCommon::ConvertRef(resolved, false);
}
}
if (temp.length()) {
diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp
index 7cca263..ced107d 100644
--- a/tools/bookmaker/mdOut.cpp
+++ b/tools/bookmaker/mdOut.cpp
@@ -10,12 +10,6 @@
#include "SkOSFile.h"
#include "SkOSPath.h"
-#define FPRINTF(...) \
- if (fDebugOut) { \
- SkDebugf(__VA_ARGS__); \
- } \
- fprintf(fOut, __VA_ARGS__)
-
const char* SubtopicKeys::kGeneratedSubtopics[] = {
kConstants, kDefines, kTypedefs, kMembers, kClasses, kStructs, kConstructors,
kOperators, kMemberFunctions, kRelatedFunctions
@@ -55,17 +49,9 @@
const char* kTopicsTableHeader = " <tr>" kTH_Left "Topic</th>" "\n"
kTH_Left "Description</th>" "</tr>";
-static string html_file_name(string bmhFileName) {
- SkASSERT("docs" == bmhFileName.substr(0, 4));
- SkASSERT('\\' == bmhFileName[4] || '/' == bmhFileName[4]);
- SkASSERT(".bmh" == bmhFileName.substr(bmhFileName.length() - 4));
- string result = bmhFileName.substr(5, bmhFileName.length() - 4 - 5);
- return result;
-}
-
string MdOut::anchorDef(string str, string name) {
if (fValidate) {
- string htmlName = html_file_name(fFileName);
+ string htmlName = ParserCommon::HtmlFileName(fFileName);
vector<AnchorDef>& allDefs = fAllAnchorDefs[htmlName];
if (!std::any_of(allDefs.begin(), allDefs.end(),
[str](AnchorDef compare) { return compare.fDef == str; } )) {
@@ -91,7 +77,7 @@
size_t hashIndex = ref.find('#');
if (string::npos != hashIndex && "https://" != ref.substr(0, 8)) {
if (0 == hashIndex) {
- htmlName = html_file_name(fFileName);
+ htmlName = ParserCommon::HtmlFileName(fFileName);
} else {
htmlName = ref.substr(0, hashIndex);
}
@@ -303,9 +289,228 @@
int fBraceCount;
};
+bool MdOut::hasWordSpace(string wordSpace) const {
+ for (NameMap* names = fNames; names; names = names->fParent) {
+ if (names->fRefMap.end() != names->fRefMap.find(wordSpace)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MdOut::phraseContinues(string phrase, string* priorWord, string* priorLink) const {
+ for (NameMap* names = fNames; names; names = names->fParent) {
+ if (names->fRefMap.end() != names->fRefMap.find(phrase + ' ')) {
+ *priorWord = phrase;
+ return true;
+ }
+ if (names->fRefMap.end() != names->fRefMap.find(phrase)) {
+ *priorWord = phrase;
+ *priorLink = names->fLinkMap.end() == names->fLinkMap.find(phrase) ? "" :
+ names->fLinkMap[phrase];
+ return true;
+ }
+ }
+ return false;
+}
+
+// adds spaces to see if found word is part of registered phrase
+// adds parens to see if found word is all-lowercase function (parens must be in source as well)
+string MdOut::addIncludeReferences(const char* refStart, const char* refEnd) {
+ TextParser matrixParser(fMethod->fFileName, refStart, refEnd, fMethod->fLineCount);
+ const char* bracket = matrixParser.anyOf("|=\n");
+ bool inMatrix = bracket && ('|' == bracket[0] || '=' == bracket[0]);
+ auto& globals = fBmhParser.fGlobalNames;
+ string result;
+ const char* start = refStart;
+ string priorWord;
+ string priorLink;
+ string priorSeparator;
+ do {
+ const char* separatorStart = start;
+ bool whiteSpace = start < refEnd && ' ' >= start[0];
+ while (start < refEnd && !isalpha(start[0])) {
+ whiteSpace &= ' ' >= start[0];
+ ++start;
+ }
+ bool priorSpace = false;
+ string separator(separatorStart, start - separatorStart);
+ if ("" != priorWord && whiteSpace) {
+ string wordSpace = priorWord + ' ';
+ if (this->hasWordSpace(wordSpace)) {
+ priorWord = wordSpace;
+ priorSpace = true;
+ }
+ }
+ if ("()" == separator.substr(0, 2)) {
+ string funcRef = priorWord + "()";
+ if (this->findLink(funcRef, &priorLink)) {
+ priorWord = funcRef;
+ separator = separator.substr(2);
+ }
+ }
+ result += priorSeparator;
+ priorSeparator = separator;
+ const char* end = start;
+ do {
+ while (end < refEnd && (isalnum(end[0]) || '-' == end[0] || '_' == end[0])) {
+ ++end;
+ }
+ if (end + 1 >= refEnd || '/' != end[0] || start == end || !isalpha(end[-1])
+ || !isalpha(end[1])) {
+ break; // stop unless pattern is xxx/xxx as in I/O
+ }
+ ++end; // skip slash
+ } while (true);
+ while (start != end && '-' == end[-1]) {
+ --end;
+ }
+ if (start != end && end + 2 < refEnd && "()" == string(end, 2)) {
+ string check = string(start, end - start);
+ if (std::all_of(check.begin(), check.end(), [](char c) { return islower(c); })) {
+ end += 2;
+ }
+ }
+ if (start == end) {
+ break;
+ }
+ string word(start, end - start);
+ if (priorSpace) {
+ string phrase = priorWord + word;
+ if (this->phraseContinues(phrase, &priorWord, &priorLink)) {
+ start = end;
+ continue;
+ }
+ priorWord = priorWord.substr(0, priorWord.length() - 1);
+ }
+ string link;
+ bool found;
+ // TODO: operators have complicated parsing possibilities; handle the easiest for now
+ if ("operator" == priorWord && '(' == separator.back()) {
+ TextParser parser(fMethod->fFileName, separatorStart, refEnd, fMethod->fLineCount);
+ parser.skipToEndBracket('(');
+ const char* parenStart = parser.fChar;
+ parser.skipToBalancedEndBracket('(', ')');
+ word = priorWord + separator + string(parenStart + 1, parser.fChar - parenStart - 1);
+ end = parser.fChar;
+ priorWord = "";
+ priorLink = "";
+ priorSeparator = "";
+ }
+ {
+ auto paramIter = fNames->fRefMap.find(word);
+ if ((found = fNames->fRefMap.end() != paramIter) && paramIter->second) {
+ SkAssertResult(fNames->fLinkMap.end() != fNames->fLinkMap.find(word));
+ link = fNames->fLinkMap[word];
+ }
+ }
+ if (!found && ("." == separator || "->" == separator || "()." == separator
+ || "()->" == separator)) {
+ if (('f' == word[0] && isupper(word[1])) || "()" == word.substr(word.length() - 2)
+ || (end + 2 <= refEnd && "()" == string(end, 2))) {
+ if (fNames->fRefMap.end() != fNames->fRefMap.find(priorWord)) {
+ // find prior word's type in fMethod
+ TextParser parser(fMethod);
+ SkAssertResult(parser.containsWord(priorWord.c_str(), parser.fEnd,
+ &parser.fChar));
+ // look up class or struct; trival lookup only class/struct [& * const]
+ while (parser.back(" ") || parser.back("&") || parser.back("*")
+ || parser.back("const"))
+ ;
+ const char* structEnd = parser.fChar;
+ parser.backupWord();
+ if (structEnd != parser.fChar) {
+ string structName(parser.fChar, structEnd - parser.fChar);
+ if ("SkVector" == structName) {
+ // TODO: populate global refmap with typedefs as well as structs
+ structName = "SkPoint";
+ }
+ structName += "::" + word;
+ // look for word as member of class or struct
+ auto defIter = globals.fRefMap.find(structName);
+ if (globals.fRefMap.end() == defIter) {
+ structName += "()";
+ defIter = globals.fRefMap.find(structName);
+ }
+ if (globals.fRefMap.end() != defIter) {
+ found = true;
+ SkASSERT(globals.fLinkMap.end() != globals.fLinkMap.find(structName));
+ link = globals.fLinkMap[structName];
+ } else {
+ SkDebugf("probably missing struct or class member in bmh: ");
+ SkDebugf("%s\n", structName.c_str());
+ }
+ }
+ }
+ if (!found) {
+ auto& parentRefMap = fNames->fParent->fRefMap;
+ auto priorIter = parentRefMap.find(priorWord);
+ if (parentRefMap.end() == priorIter) {
+ priorIter = parentRefMap.find(priorWord + "()");
+ }
+ if (parentRefMap.end() != priorIter) {
+ Definition* priorDef = priorIter->second;
+ TextParser parser(priorDef->fFileName, priorDef->fStart,
+ priorDef->fContentStart, priorDef->fLineCount);
+ parser.skipExact("#Method ");
+ parser.skipSpace();
+ parser.skipExact("const "); // optional
+ parser.skipSpace();
+ const char* start = parser.fChar;
+ parser.skipToNonAlphaNum();
+ string structName(start, parser.fChar - start);
+ structName += "::" + word;
+ auto defIter = globals.fRefMap.find(structName);
+ if (globals.fRefMap.end() != defIter) {
+ found = true;
+ SkASSERT(globals.fLinkMap.end() != globals.fLinkMap.find(structName));
+ link = globals.fLinkMap[structName];
+ }
+ }
+ }
+ } else {
+ SkDebugf("probably missing () after function:");
+ const char* debugStart = end - 20 < refStart ? refStart : end - 20;
+ const char* debugEnd = end + 10 > refEnd ? refEnd : end + 10;
+ SkDebugf("%.*s\n", debugEnd - debugStart, debugStart);
+ SkDebugf("");
+ }
+ }
+ if (!found && "::" == separator) {
+ string fullRef = priorWord + "::" + word;
+ found = this->findLink(fullRef, &link);
+ }
+ if (!found) {
+ found = this->findLink(word, &link);
+ }
+ if (!found) {
+ found = globals.fRefMap.end() != globals.fRefMap.find(word + ' ');
+ }
+ if (!found) {
+ found = globals.fRefMap.end() != globals.fRefMap.find(word + "()");
+ }
+ if (!found) {
+ if (!inMatrix) {
+ SkDebugf("word %s not found\n", word.c_str());
+ fBmhParser.fGlobalNames.fRefMap[word] = nullptr;
+ }
+ }
+ result += "" == priorLink ? priorWord : this->anchorRef(priorLink, priorWord);
+ priorWord = word;
+ priorLink = link;
+ start = end;
+ } while (true);
+ result += "" == priorLink ? priorWord : this->anchorRef(priorLink, priorWord);
+ result += priorSeparator;
+ return result;
+}
+
// FIXME: preserve inter-line spaces and don't add new ones
string MdOut::addReferences(const char* refStart, const char* refEnd,
BmhParser::Resolvable resolvable) {
+ if (BmhParser::Resolvable::kInclude == resolvable) { // test include resolving
+ return this->addIncludeReferences(refStart, refEnd);
+ }
string result;
MethodParser t(fRoot ? fRoot->fName : string(), fFileName, refStart, refEnd, fLineCount);
bool lineStart = true;
@@ -448,6 +653,7 @@
}
SkASSERT(def->fFiddle.length());
if (BmhParser::Resolvable::kSimple != resolvable
+ && BmhParser::Resolvable::kInclude != resolvable
&& !t.eof() && '(' == t.peek() && t.strnchr(')', t.fEnd)) {
TextParserSave tSave(&t);
if (!t.skipToBalancedEndBracket('(', ')')) {
@@ -511,6 +717,12 @@
}
result += linkRef(leadingSpaces, def, ref, resolvable);
if (!t.eof() && '(' == t.peek()) {
+ if (BmhParser::Resolvable::kInclude == resolvable
+ && std::any_of(ref.begin(), ref.end(), [](char c){ return !islower(c); } )) {
+ t.next(); // skip open paren
+ SkAssertResult(')' == t.next()); // skip close paren
+ continue;
+ }
result += t.next(); // skip open paren
}
continue;
@@ -577,6 +789,7 @@
}
}
if (BmhParser::Resolvable::kSimple != resolvable
+ && BmhParser::Resolvable::kInclude != resolvable
&& BmhParser::Resolvable::kOut != resolvable
&& !formula_or_code(resolvable)) {
t.reportError("missed camelCase");
@@ -970,6 +1183,33 @@
return csParent;
}
+bool MdOut::findLink(string word, string* linkPtr) {
+ NameMap* names = fNames;
+ while ((names = names->fParent)) {
+ auto localIter = names->fRefMap.find(word);
+ if (names->fRefMap.end() != localIter) {
+ if (localIter->second) {
+ SkAssertResult(names->fLinkMap.end() != names->fLinkMap.find(word));
+ *linkPtr = names->fLinkMap[word];
+ }
+ return true;
+ }
+ if (!names->fParent && isupper(word[0])) {
+ SkASSERT(names == &fBmhParser.fGlobalNames);
+ string lower = (char) tolower(word[0]) + word.substr(1);
+ auto globalIter = names->fRefMap.find(lower);
+ if (names->fRefMap.end() != globalIter) {
+ if (globalIter->second) {
+ SkAssertResult(names->fLinkMap.end() != names->fLinkMap.find(lower));
+ *linkPtr = names->fLinkMap[lower];
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
Definition* MdOut::findParamType() {
SkASSERT(fMethod);
TextParser parser(fMethod->fFileName, fMethod->fStart, fMethod->fContentStart,
@@ -1244,6 +1484,15 @@
// def should not include SkXXX_
string MdOut::linkRef(string leadingSpaces, Definition* def,
string ref, BmhParser::Resolvable resolvable) {
+ bool trimRef = BmhParser::Resolvable::kInclude == resolvable && "Sk" == ref.substr(0, 2);
+ if (trimRef) {
+#ifdef SK_DEBUG
+ for (auto c : ref) {
+ SkASSERT(isalpha(c) || isdigit(c));
+ }
+#endif
+ ref = ref.substr(2);
+ }
string buildup;
string refName;
const string* str = &def->fFiddle;
@@ -1253,7 +1502,17 @@
const Definition* parent = def->csParent();
SkASSERT(parent);
classPart = parent->fName;
- refName = classPart + '_' + def->fParent->fName + '_' + ref;
+ auto bmhMap = fBmhParser.fClassMap.find(classPart);
+ auto defName = def->fParent->fName;
+ SkASSERT(fBmhParser.fClassMap.end() != bmhMap);
+ string fullName = classPart + "::" + defName;
+ auto bmhDef = bmhMap->second.fLeaves.find(fullName);
+ if (bmhMap->second.fLeaves.end() == bmhDef) {
+ bmhDef = bmhMap->second.fLeaves.find(fullName + "()");
+ }
+ SkASSERT(bmhMap->second.fLeaves.end() != bmhDef);
+ refName = bmhDef->second.fFiddle;
+ refName += '_' + ref;
}
SkASSERT(classPart.length() > 0);
bool globalEnumMember = false;
@@ -1285,6 +1544,15 @@
}
if (!fromInclude) {
refName = def->fFiddle;
+ if (trimRef) {
+ SkASSERT("Sk" == refName.substr(0, 2));
+#ifdef SK_DEBUG
+ for (auto c : refName) {
+ SkASSERT(isalpha(c) || isdigit(c));
+ }
+#endif
+ refName = refName.substr(2);
+ }
}
}
bool classMatch = fRoot->fFileName == def->fFileName || fromInclude;
@@ -1893,9 +2161,41 @@
SkASSERT(MarkType::kMethod == parent->fMarkType);
// retrieve parameters, return, description from include
Definition* iMethod = fIncludeParser.findMethod(*parent);
- SkDebugf("");
bool wroteParam = false;
fMethod = iMethod;
+ NameMap paramMap;
+ Definition* pParent = def->csParent();
+ string parentName;
+ NameMap* parentMap;
+ if (pParent) {
+ parentName = pParent->fName + "::";
+ parentMap = &pParent->asRoot()->fNames;
+ } else {
+ parentMap = &fBmhParser.fGlobalNames;
+ }
+ paramMap.fName = parentName + iMethod->fName;
+ paramMap.fParent = parentMap;
+ fNames = ¶mMap;
+ for (auto& param : iMethod->fTokens) {
+ if (MarkType::kComment != param.fMarkType) {
+ continue;
+ }
+ TextParser paramParser(¶m);
+ if (!paramParser.skipExact("@param ")) { // write parameters, if any
+ continue;
+ }
+ paramParser.skipSpace();
+ const char* start = paramParser.fChar;
+ paramParser.skipToSpace();
+ string paramName(start, paramParser.fChar - start);
+ #ifdef SK_DEBUG
+ for (char c : paramName) {
+ SkASSERT(isalnum(c) || '_' == c);
+ }
+ #endif
+ paramMap.fRefMap[paramName] = ¶m;
+ paramMap.fLinkMap[paramName] = '#' + def->fFiddle + '_' + paramName;
+ }
for (auto& entry : iMethod->fTokens) {
if (MarkType::kComment != entry.fMarkType) {
continue;
@@ -1903,7 +2203,8 @@
TextParser parser(&entry);
if (parser.skipExact("@param ")) { // write parameters, if any
this->parameterHeaderOut(parser, prior, def);
- this->resolveOut(parser.fChar, parser.fEnd, BmhParser::Resolvable::kYes);
+ this->resolveOut(parser.fChar, parser.fEnd,
+ BmhParser::Resolvable::kInclude);
this->parameterTrailerOut();
wroteParam = true;
continue;
@@ -1917,18 +2218,29 @@
}
if (parser.skipExact("@return ")) { // write return, if any
this->returnHeaderOut(prior, def);
- this->resolveOut(parser.fChar, parser.fEnd, BmhParser::Resolvable::kYes);
+ this->resolveOut(parser.fChar, parser.fEnd,
+ BmhParser::Resolvable::kInclude);
this->lf(2);
continue;
}
if (1 == entry.length() && '/' == entry.fContentStart[0]) {
continue;
}
+ if ("/!< " == string(entry.fContentStart, entry.length()).substr(0, 4)) {
+ continue;
+ }
+ const char* backwards = entry.fContentStart;
+ while (' ' == *--backwards)
+ ;
+ if ('\n' == backwards[0] && '\n' == backwards[-1]) {
+ this->lf(2);
+ }
this->resolveOut(entry.fContentStart, entry.fContentEnd,
- BmhParser::Resolvable::kYes); // write description
+ BmhParser::Resolvable::kInclude); // write description
this->lf(1);
}
fMethod = nullptr;
+ fNames = fNames->fParent;
}
} break;
case MarkType::kPrivate:
diff --git a/tools/bookmaker/parserCommon.cpp b/tools/bookmaker/parserCommon.cpp
index 5500a1b..c3bab1a 100644
--- a/tools/bookmaker/parserCommon.cpp
+++ b/tools/bookmaker/parserCommon.cpp
@@ -9,11 +9,6 @@
#include "SkOSFile.h"
#include "SkOSPath.h"
-static void debug_out(int len, const char* data) {
- // convenient place to intercept arbitrary output
- SkDebugf("%.*s", len, data);
-}
-
void ParserCommon::checkLineLength(size_t len, const char* str) {
if (!fWritingIncludes) {
return;
@@ -36,6 +31,21 @@
}
}
+// change Xxx_Xxx to xxx xxx
+string ParserCommon::ConvertRef(const string str, bool first) {
+ string substitute;
+ for (char c : str) {
+ if ('_' == c) {
+ c = ' ';
+ } else if (isupper(c) && !first) {
+ c = tolower(c);
+ }
+ substitute += c;
+ first = false;
+ }
+ return substitute;
+}
+
void ParserCommon::CopyToFile(string oldFile, string newFile) {
int bufferSize;
char* buffer = ParserCommon::ReadToBuffer(newFile, &bufferSize);
@@ -49,7 +59,16 @@
remove(newFile.c_str());
}
+string ParserCommon::HtmlFileName(string bmhFileName) {
+ SkASSERT("docs" == bmhFileName.substr(0, 4));
+ SkASSERT('\\' == bmhFileName[4] || '/' == bmhFileName[4]);
+ SkASSERT(".bmh" == bmhFileName.substr(bmhFileName.length() - 4));
+ string result = bmhFileName.substr(5, bmhFileName.length() - 4 - 5);
+ return result;
+}
+
bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix, OneFile oneFile) {
+ fRawFilePathDir = string(fileOrPath);
if (!sk_isdir(fileOrPath)) {
if (!this->parseFromFile(fileOrPath)) {
SkDebugf("failed to parse %s\n", fileOrPath);
@@ -80,6 +99,7 @@
}
bool ParserCommon::parseStatus(const char* statusFile, const char* suffix, StatusFilter filter) {
+ fRawFilePathDir = string(statusFile);
StatusIter iter(statusFile, suffix, filter);
if (iter.empty()) {
return false;
@@ -198,10 +218,7 @@
int len = lineEnd ? (int) (lineEnd - data) : size;
this->writePending();
this->indentToColumn(indent);
- if (fDebugOut) {
- debug_out(len, data);
- }
- fprintf(fOut, "%.*s", len, data);
+ FPRINTF("%.*s", len, data);
checkLineLength(len, data);
size -= len;
data += len;
@@ -241,10 +258,7 @@
fPendingSpace = 0;
}
this->writePending();
- if (fDebugOut) {
- debug_out(size, data);
- }
- fprintf(fOut, "%.*s", size, data);
+ FPRINTF("%.*s", size, data);
checkLineLength(size, data);
fWroteSomething = true;
int added = 0;
@@ -311,10 +325,7 @@
fPendingSpace = 0;
}
this->writePending();
- if (fDebugOut) {
- debug_out((int) strlen(str), str);
- }
- fprintf(fOut, "%s", str);
+ FPRINTF("%s", str);
checkLineLength(strlen(str), str);
fColumn += len;
fSpaces = 0;