aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCary Clark <caryclark@skia.org>2018-11-01 09:29:36 -0400
committerSkia Commit-Bot <skia-commit-bot@chromium.org>2018-11-01 13:58:32 +0000
commit2da9fb836696a2ffc344688236cbad5b0d0386e4 (patch)
tree93552b012e77d3e9f9e270d70838e3f79dfd9b62
parent3bdaa46bff71b839c806c0955417b1abaae650b0 (diff)
downloadskqp-2da9fb836696a2ffc344688236cbad5b0d0386e4.tar.gz
refactor bookmaker
mostly create include files with minimum content TBR=caryclark@google.com Bug: skia: Change-Id: I3db0f913cc108bd5ef8ec6c3b229eaccc3f25198 Reviewed-on: https://skia-review.googlesource.com/c/167302 Commit-Queue: Cary Clark <caryclark@skia.org> Auto-Submit: Cary Clark <caryclark@skia.org> Reviewed-by: Cary Clark <caryclark@skia.org>
-rw-r--r--BUILD.gn3
-rw-r--r--tools/bookmaker/bmhParser.cpp2358
-rw-r--r--tools/bookmaker/bmhParser.h194
-rw-r--r--tools/bookmaker/bookmaker.cpp2601
-rw-r--r--tools/bookmaker/bookmaker.h2543
-rw-r--r--tools/bookmaker/cataloger.cpp3
-rw-r--r--tools/bookmaker/definition.cpp4
-rw-r--r--tools/bookmaker/definition.h326
-rw-r--r--tools/bookmaker/fiddleParser.cpp18
-rw-r--r--tools/bookmaker/fiddleParser.h84
-rw-r--r--tools/bookmaker/hackParser.cpp112
-rw-r--r--tools/bookmaker/includeParser.cpp4
-rw-r--r--tools/bookmaker/includeParser.h452
-rw-r--r--tools/bookmaker/includeWriter.cpp5
-rw-r--r--tools/bookmaker/includeWriter.h243
-rw-r--r--tools/bookmaker/mdOut.cpp115
-rw-r--r--tools/bookmaker/mdOut.h171
-rw-r--r--tools/bookmaker/parserCommon.cpp3
-rw-r--r--tools/bookmaker/parserCommon.h319
-rw-r--r--tools/bookmaker/selfCheck.cpp3
-rw-r--r--tools/bookmaker/selfCheck.h15
-rw-r--r--tools/bookmaker/spellCheck.cpp5
-rw-r--r--tools/bookmaker/textParser.cpp190
-rw-r--r--tools/bookmaker/textParser.h733
24 files changed, 5338 insertions, 5166 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 3bbc95eb2f..88be476fb9 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1649,16 +1649,19 @@ if (skia_enable_tools) {
test_app("bookmaker") {
sources = [
+ "tools/bookmaker/bmhParser.cpp",
"tools/bookmaker/bookmaker.cpp",
"tools/bookmaker/cataloger.cpp",
"tools/bookmaker/definition.cpp",
"tools/bookmaker/fiddleParser.cpp",
+ "tools/bookmaker/hackParser.cpp",
"tools/bookmaker/includeParser.cpp",
"tools/bookmaker/includeWriter.cpp",
"tools/bookmaker/mdOut.cpp",
"tools/bookmaker/parserCommon.cpp",
"tools/bookmaker/selfCheck.cpp",
"tools/bookmaker/spellCheck.cpp",
+ "tools/bookmaker/textParser.cpp",
]
deps = [
":flags",
diff --git a/tools/bookmaker/bmhParser.cpp b/tools/bookmaker/bmhParser.cpp
new file mode 100644
index 0000000000..bfdffc704a
--- /dev/null
+++ b/tools/bookmaker/bmhParser.cpp
@@ -0,0 +1,2358 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "bmhParser.h"
+
+const string kSpellingFileName("spelling.txt");
+
+#define M(mt) (1LL << (int) MarkType::k##mt)
+#define M_D M(Description)
+#define M_CS M(Class) | M(Struct)
+#define M_MD M(Method) | M(Define)
+#define M_MDCM M_MD | M(Const) | M(Member)
+#define M_ST M(Subtopic) | M(Topic)
+#define M_CSST M_CS | M_ST
+#ifdef M_E
+#undef M_E
+#endif
+#define M_E M(Enum) | M(EnumClass)
+
+#define R_Y Resolvable::kYes
+#define R_N Resolvable::kNo
+#define R_O Resolvable::kOut
+#define R_K Resolvable::kCode
+#define R_F Resolvable::kFormula
+#define R_C Resolvable::kClone
+
+#define E_Y Exemplary::kYes
+#define E_N Exemplary::kNo
+#define E_O Exemplary::kOptional
+
+// ToDo: add column to denote which marks are one-liners
+BmhParser::MarkProps BmhParser::kMarkProps[] = {
+// names without formal definitions (e.g. Column) aren't included
+ { "", MarkType::kNone, R_Y, E_N, 0 }
+, { "A", MarkType::kAnchor, R_N, E_N, 0 }
+, { "Alias", MarkType::kAlias, R_N, E_N, M_ST | M(Const) }
+, { "Bug", MarkType::kBug, R_N, E_N, M_CSST | M_MDCM | M_E
+ | M(Example) | M(NoExample) }
+, { "Class", MarkType::kClass, R_Y, E_O, M_CSST }
+, { "Code", MarkType::kCode, R_K, E_N, M_CSST | M_E | M_MD | M(Typedef) }
+, { "", MarkType::kColumn, R_Y, E_N, M(Row) }
+, { "", MarkType::kComment, R_N, E_N, 0 }
+, { "Const", MarkType::kConst, R_Y, E_O, M_E | M_CSST }
+, { "Define", MarkType::kDefine, R_O, E_Y, M_ST }
+, { "Deprecated", MarkType::kDeprecated, R_Y, E_N, M_CS | M_MDCM | M_E }
+, { "Description", MarkType::kDescription, R_Y, E_N, M(Example) | M(NoExample) }
+, { "Details", MarkType::kDetails, R_N, E_N, M(Const) }
+, { "Duration", MarkType::kDuration, R_N, E_N, M(Example) | M(NoExample) }
+, { "Enum", MarkType::kEnum, R_Y, E_O, M_CSST }
+, { "EnumClass", MarkType::kEnumClass, R_Y, E_O, M_CSST }
+, { "Example", MarkType::kExample, R_O, E_N, M_CSST | M_E | M_MD | M(Const) }
+, { "Experimental", MarkType::kExperimental, R_Y, E_N, M_CS | M_MDCM | M_E }
+, { "External", MarkType::kExternal, R_Y, E_N, 0 }
+, { "File", MarkType::kFile, R_Y, E_N, M(Topic) }
+, { "Filter", MarkType::kFilter, R_N, E_N, M(Subtopic) | M(Code) }
+, { "Formula", MarkType::kFormula, R_F, E_N, M(Column) | M(Description)
+ | M_E | M_ST | M_MDCM }
+, { "Function", MarkType::kFunction, R_O, E_N, M(Example) | M(NoExample) }
+, { "Height", MarkType::kHeight, R_N, E_N, M(Example) | M(NoExample) }
+, { "Illustration", MarkType::kIllustration, R_N, E_N, M_CSST | M_MD }
+, { "Image", MarkType::kImage, R_N, E_N, M(Example) | M(NoExample) }
+, { "In", MarkType::kIn, R_N, E_N, M_CSST | M_E | M(Method) | M(Typedef) | M(Code) }
+, { "Legend", MarkType::kLegend, R_Y, E_N, M(Table) }
+, { "Line", MarkType::kLine, R_N, E_N, M_CSST | M_E | M(Method) | M(Typedef) }
+, { "", MarkType::kLink, R_N, E_N, M(Anchor) }
+, { "List", MarkType::kList, R_Y, E_N, M(Method) | M_CSST | M_E | M_D }
+, { "Literal", MarkType::kLiteral, R_N, E_N, M(Code) }
+, { "", MarkType::kMarkChar, R_N, E_N, 0 }
+, { "Member", MarkType::kMember, R_Y, E_O, M_CSST }
+, { "Method", MarkType::kMethod, R_Y, E_Y, M_CSST }
+, { "NoExample", MarkType::kNoExample, R_N, E_N, M_CSST | M_E | M_MD }
+, { "NoJustify", MarkType::kNoJustify, R_N, E_N, M(Const) | M(Member) }
+, { "Outdent", MarkType::kOutdent, R_N, E_N, M(Code) }
+, { "Param", MarkType::kParam, R_Y, E_N, M(Method) | M(Define) }
+, { "PhraseDef", MarkType::kPhraseDef, R_Y, E_N, M_ST }
+, { "", MarkType::kPhraseParam, R_Y, E_N, 0 }
+, { "", MarkType::kPhraseRef, R_N, E_N, 0 }
+, { "Platform", MarkType::kPlatform, R_N, E_N, M(Example) | M(NoExample) }
+, { "Populate", MarkType::kPopulate, R_N, E_N, M(Code) | M(Method) }
+, { "Private", MarkType::kPrivate, R_N, E_N, M_CSST | M_MDCM | M_E }
+, { "Return", MarkType::kReturn, R_Y, E_N, M(Method) }
+, { "", MarkType::kRow, R_Y, E_N, M(Table) | M(List) }
+, { "SeeAlso", MarkType::kSeeAlso, R_C, E_N, M_CSST | M_E | M_MD | M(Typedef) }
+, { "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(Alias) | M_ST }
+, { "Subtopic", MarkType::kSubtopic, R_Y, E_Y, M_CSST | M_E }
+, { "Table", MarkType::kTable, R_Y, E_N, M(Method) | M_CSST | M_E }
+, { "Template", MarkType::kTemplate, R_Y, E_N, M_CSST }
+, { "", MarkType::kText, R_N, E_N, 0 }
+, { "ToDo", MarkType::kToDo, R_N, E_N, 0 }
+, { "Topic", MarkType::kTopic, R_Y, E_Y, 0 }
+, { "Typedef", MarkType::kTypedef, R_Y, E_O, M_CSST | M_E }
+, { "Union", MarkType::kUnion, R_Y, E_N, M_CSST }
+, { "Using", MarkType::kUsing, R_Y, E_O, M_CSST }
+, { "Volatile", MarkType::kVolatile, R_N, E_N, M(StdOut) }
+, { "Width", MarkType::kWidth, R_N, E_N, M(Example) | M(NoExample) }
+};
+
+#undef R_O
+#undef R_N
+#undef R_Y
+#undef R_K
+#undef R_F
+#undef R_C
+
+#undef M_E
+#undef M_CSST
+#undef M_ST
+#undef M_CS
+#undef M_MCD
+#undef M_D
+#undef M
+
+#undef E_Y
+#undef E_N
+#undef E_O
+
+bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markType,
+ const vector<string>& typeNameBuilder, HasTag hasTag) {
+ Definition* definition = nullptr;
+ switch (markType) {
+ case MarkType::kComment:
+ if (!this->skipToDefinitionEnd(markType)) {
+ return false;
+ }
+ return true;
+ // these types may be referred to by name
+ case MarkType::kClass:
+ case MarkType::kStruct:
+ case MarkType::kConst:
+ case MarkType::kDefine:
+ case MarkType::kEnum:
+ case MarkType::kEnumClass:
+ case MarkType::kMember:
+ case MarkType::kMethod:
+ case MarkType::kTypedef: {
+ if (!typeNameBuilder.size()) {
+ return this->reportError<bool>("unnamed markup");
+ }
+ if (typeNameBuilder.size() > 1) {
+ return this->reportError<bool>("expected one name only");
+ }
+ string name = typeNameBuilder[0];
+ 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) {
+ return this->reportError<bool>("expected parent");
+ }
+ if (fParent == fRoot && hasEnd) {
+ RootDefinition* rootParent = fRoot->rootParent();
+ if (rootParent) {
+ fRoot = rootParent;
+ }
+ definition = fParent;
+ } else {
+ if (!hasEnd && fRoot->find(name, RootDefinition::AllowParens::kNo)) {
+ return this->reportError<bool>("duplicate symbol");
+ }
+ if (MarkType::kStruct == markType || MarkType::kClass == markType
+ || MarkType::kEnumClass == markType) {
+ // if class or struct, build fRoot hierarchy
+ // and change isDefined to search all parents of fRoot
+ SkASSERT(!hasEnd);
+ RootDefinition* childRoot = new RootDefinition;
+ (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 {
+ definition = &fRoot->fLeaves[name];
+ }
+ }
+ }
+ if (hasEnd) {
+ Exemplary hasExample = Exemplary::kNo;
+ bool hasExcluder = false;
+ for (auto child : definition->fChildren) {
+ if (MarkType::kExample == child->fMarkType) {
+ hasExample = Exemplary::kYes;
+ }
+ hasExcluder |= MarkType::kPrivate == child->fMarkType
+ || MarkType::kDeprecated == child->fMarkType
+ || MarkType::kExperimental == child->fMarkType
+ || MarkType::kNoExample == child->fMarkType;
+ }
+ if (kMarkProps[(int) markType].fExemplary != hasExample
+ && kMarkProps[(int) markType].fExemplary != Exemplary::kOptional) {
+ if (string::npos == fFileName.find("undocumented")
+ && !hasExcluder) {
+ hasExample == Exemplary::kNo ?
+ this->reportWarning("missing example") :
+ this->reportWarning("unexpected example");
+ }
+
+ }
+ if (MarkType::kMethod == markType) {
+ if (fCheckMethods && !definition->checkMethod()) {
+ return false;
+ }
+ }
+ if (HasTag::kYes == hasTag) {
+ if (!this->checkEndMarker(markType, definition->fName)) {
+ return false;
+ }
+ }
+ if (!this->popParentStack(definition)) {
+ return false;
+ }
+ if (fRoot == definition) {
+ fRoot = nullptr;
+ }
+ } else {
+ definition->fStart = defStart;
+ this->skipSpace();
+ definition->fFileName = fFileName;
+ definition->fContentStart = fChar;
+ definition->fLineCount = fLineCount;
+ definition->fClone = fCloned;
+ if (MarkType::kConst == markType) {
+ // todo: require that fChar points to def on same line as markup
+ // additionally add definition to class children if it is not already there
+ if (definition->fParent != fRoot) {
+// fRoot->fChildren.push_back(definition);
+ }
+ }
+ SkASSERT(string::npos == name.find('\n'));
+ definition->fName = name;
+ if (MarkType::kMethod == markType) {
+ if (string::npos != name.find(':', 0)) {
+ definition->setCanonicalFiddle();
+ } else {
+ definition->fFiddle = name;
+ }
+ } else {
+ definition->fFiddle = Definition::NormalizedName(name);
+ }
+ definition->fMarkType = markType;
+ definition->fAnonymous = fAnonymous;
+ this->setAsParent(definition);
+ }
+ } break;
+ case MarkType::kTopic:
+ case MarkType::kSubtopic:
+ SkASSERT(1 == typeNameBuilder.size());
+ if (!hasEnd) {
+ if (!typeNameBuilder.size()) {
+ return this->reportError<bool>("unnamed topic");
+ }
+ fTopics.emplace_front(markType, defStart, fLineCount, fParent, fMC);
+ RootDefinition* rootDefinition = &fTopics.front();
+ definition = rootDefinition;
+ definition->fFileName = fFileName;
+ definition->fContentStart = fChar;
+ if (MarkType::kTopic == markType) {
+ if (fParent) {
+ return this->reportError<bool>("#Topic must be root");
+ }
+ // topic name is unappended
+ definition->fName = typeNameBuilder[0];
+ } else {
+ if (!fParent) {
+ return this->reportError<bool>("#Subtopic may not be root");
+ }
+ Definition* parent = fParent;
+ while (MarkType::kTopic != parent->fMarkType && MarkType::kSubtopic != parent->fMarkType) {
+ parent = parent->fParent;
+ if (!parent) {
+ // subtopic must have subtopic or topic in parent chain
+ return this->reportError<bool>("#Subtopic missing parent");
+ }
+ }
+ if (MarkType::kSubtopic == parent->fMarkType) {
+ // subtopic prepends parent subtopic name, but not parent topic name
+ definition->fName = parent->fName + '_';
+ }
+ definition->fName += typeNameBuilder[0];
+ definition->fFiddle = parent->fFiddle + '_';
+ }
+ rootDefinition->fNames.fName = rootDefinition->fName;
+ definition->fFiddle += Definition::NormalizedName(typeNameBuilder[0]);
+ this->setAsParent(definition);
+ }
+ {
+ SkASSERT(hasEnd ? fParent : definition);
+ string fullTopic = hasEnd ? fParent->fFiddle : definition->fFiddle;
+ Definition* defPtr = fTopicMap[fullTopic];
+ if (hasEnd) {
+ if (HasTag::kYes == hasTag && !this->checkEndMarker(markType, fullTopic)) {
+ return false;
+ }
+ if (!definition) {
+ definition = defPtr;
+ } else if (definition != defPtr) {
+ return this->reportError<bool>("mismatched topic");
+ }
+ } else {
+ if (nullptr != defPtr) {
+ return this->reportError<bool>("already declared topic");
+ }
+ fTopicMap[fullTopic] = definition;
+ }
+ }
+ if (hasEnd) {
+ if (!this->popParentStack(definition)) {
+ return false;
+ }
+ }
+ break;
+ case MarkType::kFormula:
+ // hasEnd : single line / multiple line
+ if (!fParent || MarkType::kFormula != fParent->fMarkType) {
+ SkASSERT(!definition || MarkType::kFormula == definition->fMarkType);
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
+ definition = &fMarkup.front();
+ definition->fContentStart = fChar;
+ definition->fName = typeNameBuilder[0];
+ definition->fFiddle = fParent->fFiddle;
+ fParent = definition;
+ } else {
+ SkASSERT(fParent && MarkType::kFormula == fParent->fMarkType);
+ SkASSERT(fMC == defStart[0]);
+ SkASSERT(fMC == defStart[-1]);
+ definition = fParent;
+ definition->fTerminator = fChar;
+ if (!this->popParentStack(definition)) {
+ return false;
+ }
+ this->parseHashFormula(definition);
+ fParent->fChildren.push_back(definition);
+ }
+ break;
+ // these types are children of parents, but are not in named maps
+ case MarkType::kDescription:
+ case MarkType::kStdOut:
+ // may be one-liner
+ case MarkType::kAlias:
+ case MarkType::kNoExample:
+ case MarkType::kParam:
+ case MarkType::kPhraseDef:
+ case MarkType::kReturn:
+ case MarkType::kToDo:
+ if (hasEnd) {
+ if (markType == fParent->fMarkType) {
+ definition = fParent;
+ if (MarkType::kBug == markType || MarkType::kReturn == markType
+ || MarkType::kToDo == markType) {
+ this->skipNoName();
+ }
+ if (!this->popParentStack(fParent)) { // if not one liner, pop
+ return false;
+ }
+ if (MarkType::kParam == markType || MarkType::kReturn == markType
+ || MarkType::kPhraseDef == markType) {
+ if (!this->checkParamReturn(definition)) {
+ return false;
+ }
+ }
+ if (MarkType::kPhraseDef == markType) {
+ string key = definition->fName;
+ if (fPhraseMap.end() != fPhraseMap.find(key)) {
+ this->reportError<bool>("duplicate phrase key");
+ }
+ fPhraseMap[key] = definition;
+ }
+ } else {
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
+ definition = &fMarkup.front();
+ definition->fName = typeNameBuilder[0];
+ definition->fFiddle = fParent->fFiddle;
+ definition->fContentStart = fChar;
+ string endBracket;
+ endBracket += fMC;
+ endBracket += fMC;
+ definition->fContentEnd = this->trimmedBracketEnd(endBracket);
+ this->skipToEndBracket(endBracket.c_str());
+ SkAssertResult(fMC == this->next());
+ SkAssertResult(fMC == this->next());
+ definition->fTerminator = fChar;
+ TextParser checkForChildren(definition);
+ if (checkForChildren.strnchr(fMC, definition->fContentEnd)) {
+ this->reportError<bool>("put ## on separate line");
+ }
+ 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<bool>("duplicate alias");
+ }
+ fAliasMap[key] = definition;
+ definition->fFiddle = definition->fParent->fFiddle;
+ }
+ break;
+ } else if (MarkType::kPhraseDef == markType) {
+ bool hasParams = '(' == this->next();
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
+ definition = &fMarkup.front();
+ definition->fName = typeNameBuilder[0];
+ definition->fFiddle = fParent->fFiddle;
+ definition->fContentStart = fChar;
+ if (hasParams) {
+ char lastChar;
+ do {
+ const char* subEnd = this->anyOf(",)\n");
+ if (!subEnd || '\n' == *subEnd) {
+ return this->reportError<bool>("unexpected phrase list end");
+ }
+ fMarkup.emplace_front(MarkType::kPhraseParam, fChar, fLineCount, fParent,
+ fMC);
+ Definition* phraseParam = &fMarkup.front();
+ phraseParam->fContentStart = fChar;
+ phraseParam->fContentEnd = subEnd;
+ phraseParam->fName = string(fChar, subEnd - fChar);
+ definition->fChildren.push_back(phraseParam);
+ this->skipTo(subEnd);
+ lastChar = this->next();
+ phraseParam->fTerminator = fChar;
+ } while (')' != lastChar);
+ this->skipWhiteSpace();
+ definition->fContentStart = fChar;
+ }
+ this->setAsParent(definition);
+ break;
+ }
+ // not one-liners
+ case MarkType::kCode:
+ case MarkType::kExample:
+ case MarkType::kFile:
+ case MarkType::kFunction:
+ case MarkType::kLegend:
+ case MarkType::kList:
+ case MarkType::kPrivate:
+ case MarkType::kTable:
+ if (hasEnd) {
+ definition = fParent;
+ if (markType != fParent->fMarkType) {
+ return this->reportError<bool>("end element mismatch");
+ } else if (!this->popParentStack(fParent)) {
+ return false;
+ }
+ if (MarkType::kExample == markType) {
+ if (definition->fChildren.size() == 0) {
+ TextParser emptyCheck(definition);
+ if (emptyCheck.eof() || !emptyCheck.skipWhiteSpace()) {
+ return this->reportError<bool>("missing example body");
+ }
+ }
+// can't do this here; phrase refs may not have been defined yet
+// this->setWrapper(definition);
+ }
+ } else {
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
+ definition = &fMarkup.front();
+ definition->fContentStart = fChar;
+ definition->fName = typeNameBuilder[0];
+ definition->fFiddle = fParent->fFiddle;
+ char suffix = '\0';
+ bool tryAgain;
+ do {
+ tryAgain = false;
+ for (const auto& child : fParent->fChildren) {
+ if (child->fFiddle == definition->fFiddle) {
+ if (MarkType::kExample != child->fMarkType) {
+ continue;
+ }
+ if ('\0' == suffix) {
+ suffix = 'a';
+ } else if (++suffix > 'z') {
+ return reportError<bool>("too many examples");
+ }
+ definition->fFiddle = fParent->fFiddle + '_';
+ definition->fFiddle += suffix;
+ tryAgain = true;
+ break;
+ }
+ }
+ } while (tryAgain);
+ this->setAsParent(definition);
+ }
+ break;
+ // always treated as one-liners (can't detect misuse easily)
+ case MarkType::kAnchor:
+ case MarkType::kBug:
+ case MarkType::kDeprecated:
+ case MarkType::kDetails:
+ case MarkType::kDuration:
+ case MarkType::kExperimental:
+ case MarkType::kFilter:
+ case MarkType::kHeight:
+ case MarkType::kIllustration:
+ case MarkType::kImage:
+ case MarkType::kIn:
+ case MarkType::kLine:
+ case MarkType::kLiteral:
+ case MarkType::kNoJustify:
+ case MarkType::kOutdent:
+ case MarkType::kPlatform:
+ case MarkType::kPopulate:
+ case MarkType::kSeeAlso:
+ case MarkType::kSet:
+ case MarkType::kSubstitute:
+ case MarkType::kVolatile:
+ case MarkType::kWidth:
+ // todo : add check disallowing children?
+ if (hasEnd && MarkType::kAnchor != markType && MarkType::kLine != markType) {
+ return this->reportError<bool>("one liners omit end element");
+ } else if (!hasEnd && MarkType::kAnchor == markType) {
+ return this->reportError<bool>("anchor line must have end element last");
+ }
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
+ definition = &fMarkup.front();
+ definition->fName = typeNameBuilder[0];
+ definition->fFiddle = Definition::NormalizedName(typeNameBuilder[0]);
+ definition->fContentStart = fChar;
+ definition->fContentEnd = this->trimmedBracketEnd('\n');
+ definition->fTerminator = this->lineEnd() - 1;
+ fParent->fChildren.push_back(definition);
+ if (MarkType::kAnchor == markType) {
+ this->parseHashAnchor(definition);
+ } else if (MarkType::kLine == markType) {
+ this->parseHashLine(definition);
+ } else if (IncompleteAllowed(markType)) {
+ this->skipSpace();
+ fParent->fDeprecated = true;
+ fParent->fDetails =
+ this->skipExact("soon") ? Definition::Details::kSoonToBe_Deprecated :
+ this->skipExact("testing") ? Definition::Details::kTestingOnly_Experiment :
+ this->skipExact("do not use") ? Definition::Details::kDoNotUse_Experiment :
+ this->skipExact("not ready") ? Definition::Details::kNotReady_Experiment :
+ Definition::Details::kNone;
+ this->skipSpace();
+ if ('\n' != this->peek()) {
+ return this->reportError<bool>("unexpected text after #Deprecated");
+ }
+ }
+ break;
+ case MarkType::kExternal:
+ (void) this->collectExternals(); // FIXME: detect errors in external defs?
+ break;
+ default:
+ SkASSERT(0); // fixme : don't let any types be invisible
+ return true;
+ }
+ if (fParent) {
+ SkASSERT(definition);
+ SkASSERT(definition->fName.length() > 0);
+ }
+ return true;
+}
+
+void BmhParser::reportDuplicates(const Definition& def, string dup) const {
+ if (MarkType::kExample == def.fMarkType && dup == def.fFiddle) {
+ TextParser reporter(&def);
+ reporter.reportError("duplicate example name");
+ }
+ for (auto& child : def.fChildren ) {
+ reportDuplicates(*child, dup);
+ }
+}
+
+
+static Definition* find_fiddle(Definition* def, string name) {
+ if (MarkType::kExample == def->fMarkType && name == def->fFiddle) {
+ return def;
+ }
+ for (auto& child : def->fChildren) {
+ Definition* result = find_fiddle(child, name);
+ if (result) {
+ return result;
+ }
+ }
+ return nullptr;
+}
+
+Definition* BmhParser::findExample(string name) const {
+ for (const auto& topic : fTopicMap) {
+ if (topic.second->fParent) {
+ continue;
+ }
+ Definition* def = find_fiddle(topic.second, name);
+ if (def) {
+ return def;
+ }
+ }
+ return nullptr;
+}
+
+static bool check_example_hashes(Definition* def) {
+ if (MarkType::kExample == def->fMarkType) {
+ if (def->fHash.length()) {
+ return true;
+ }
+ for (auto child : def->fChildren) {
+ if (MarkType::kPlatform == child->fMarkType) {
+ if (string::npos != string(child->fContentStart, child->length()).find("!fiddle")) {
+ return true;
+ }
+ }
+ }
+ return def->reportError<bool>("missing hash");
+ }
+ for (auto& child : def->fChildren) {
+ if (!check_example_hashes(child)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool BmhParser::checkExampleHashes() const {
+ for (const auto& topic : fTopicMap) {
+ if (!topic.second->fParent && !check_example_hashes(topic.second)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void reset_example_hashes(Definition* def) {
+ if (MarkType::kExample == def->fMarkType) {
+ def->fHash.clear();
+ return;
+ }
+ for (auto& child : def->fChildren) {
+ reset_example_hashes(child);
+ }
+}
+
+void BmhParser::resetExampleHashes() {
+ for (const auto& topic : fTopicMap) {
+ if (!topic.second->fParent) {
+ reset_example_hashes(topic.second);
+ }
+ }
+}
+
+static void find_examples(const Definition& def, vector<string>* exampleNames) {
+ if (MarkType::kExample == def.fMarkType) {
+ exampleNames->push_back(def.fFiddle);
+ }
+ for (auto& child : def.fChildren ) {
+ find_examples(*child, exampleNames);
+ }
+}
+
+bool BmhParser::checkEndMarker(MarkType markType, string match) const {
+ TextParser tp(fFileName, fLine, fChar, fLineCount);
+ tp.skipSpace();
+ if (fMC != tp.next()) {
+ return this->reportError<bool>("mismatched end marker expect #");
+ }
+ const char* nameStart = tp.fChar;
+ tp.skipToNonName();
+ string markName(nameStart, tp.fChar - nameStart);
+ if (kMarkProps[(int) markType].fName != markName) {
+ return this->reportError<bool>("expected #XXX ## to match");
+ }
+ tp.skipSpace();
+ nameStart = tp.fChar;
+ tp.skipToNonName();
+ markName = string(nameStart, tp.fChar - nameStart);
+ if ("" == markName) {
+ if (fMC != tp.next() || fMC != tp.next()) {
+ return this->reportError<bool>("expected ##");
+ }
+ return true;
+ }
+ std::replace(markName.begin(), markName.end(), '-', '_');
+ auto defPos = match.rfind(markName);
+ if (string::npos == defPos) {
+ return this->reportError<bool>("mismatched end marker v1");
+ }
+ if (markName.size() != match.size() - defPos) {
+ return this->reportError<bool>("mismatched end marker v2");
+ }
+ return true;
+}
+
+bool BmhParser::checkExamples() const {
+ vector<string> exampleNames;
+ for (const auto& topic : fTopicMap) {
+ if (topic.second->fParent) {
+ continue;
+ }
+ find_examples(*topic.second, &exampleNames);
+ }
+ std::sort(exampleNames.begin(), exampleNames.end());
+ string* last = nullptr;
+ string reported;
+ bool checkOK = true;
+ for (auto& nameIter : exampleNames) {
+ if (last && *last == nameIter && reported != *last) {
+ reported = *last;
+ SkDebugf("%s\n", reported.c_str());
+ for (const auto& topic : fTopicMap) {
+ if (topic.second->fParent) {
+ continue;
+ }
+ this->reportDuplicates(*topic.second, reported);
+ }
+ checkOK = false;
+ }
+ last = &nameIter;
+ }
+ return checkOK;
+}
+
+bool BmhParser::checkParamReturn(const Definition* definition) const {
+ const char* parmEndCheck = definition->fContentEnd;
+ while (parmEndCheck < definition->fTerminator) {
+ if (fMC == parmEndCheck[0]) {
+ break;
+ }
+ if (' ' < parmEndCheck[0]) {
+ this->reportError<bool>(
+ "use full end marker on multiline #Param and #Return");
+ }
+ ++parmEndCheck;
+ }
+ return true;
+}
+
+bool BmhParser::childOf(MarkType markType) const {
+ auto childError = [this](MarkType markType) -> bool {
+ string errStr = "expected ";
+ errStr += kMarkProps[(int) markType].fName;
+ errStr += " parent";
+ return this->reportError<bool>(errStr.c_str());
+ };
+
+ if (markType == fParent->fMarkType) {
+ return true;
+ }
+ if (this->hasEndToken()) {
+ if (!fParent->fParent) {
+ return this->reportError<bool>("expected grandparent");
+ }
+ if (markType == fParent->fParent->fMarkType) {
+ return true;
+ }
+ }
+ return childError(markType);
+}
+
+string BmhParser::className(MarkType markType) {
+ const char* end = this->lineEnd();
+ const char* mc = this->strnchr(fMC, end);
+ string classID;
+ TextParserSave savePlace(this);
+ this->skipSpace();
+ const char* wordStart = fChar;
+ this->skipToNonName();
+ const char* wordEnd = fChar;
+ classID = string(wordStart, wordEnd - wordStart);
+ if (!mc) {
+ savePlace.restore();
+ }
+ string builder;
+ const Definition* parent = this->parentSpace();
+ if (parent && parent->fName != classID) {
+ builder += parent->fName;
+ }
+ if (mc) {
+ if (mc + 1 < fEnd && fMC == mc[1]) { // if ##
+ if (markType != fParent->fMarkType) {
+ return this->reportError<string>("unbalanced method");
+ }
+ if (builder.length() > 0 && classID.size() > 0) {
+ if (builder != fParent->fName) {
+ builder += "::";
+ builder += classID;
+ if (builder != fParent->fName) {
+ return this->reportError<string>("name mismatch");
+ }
+ }
+ }
+ this->skipLine();
+ return fParent->fName;
+ }
+ fChar = mc;
+ this->next();
+ }
+ this->skipWhiteSpace();
+ if (MarkType::kEnum == markType && fChar >= end) {
+ fAnonymous = true;
+ builder += "::_anonymous";
+ return uniqueRootName(builder, markType);
+ }
+ builder = this->word(builder, "::");
+ return builder;
+}
+
+bool BmhParser::collectExternals() {
+ do {
+ this->skipWhiteSpace();
+ if (this->eof()) {
+ break;
+ }
+ if (fMC == this->peek()) {
+ this->next();
+ if (this->eof()) {
+ break;
+ }
+ if (fMC == this->peek()) {
+ this->skipLine();
+ break;
+ }
+ if (' ' >= this->peek()) {
+ this->skipLine();
+ continue;
+ }
+ if (this->startsWith(kMarkProps[(int) MarkType::kExternal].fName)) {
+ this->skipToNonName();
+ continue;
+ }
+ }
+ this->skipToAlpha();
+ const char* wordStart = fChar;
+ this->skipToWhiteSpace();
+ if (fChar - wordStart > 0) {
+ fExternals.emplace_front(MarkType::kExternal, wordStart, fChar, fLineCount, fParent,
+ fMC);
+ RootDefinition* definition = &fExternals.front();
+ definition->fFileName = fFileName;
+ definition->fName = string(wordStart ,fChar - wordStart);
+ definition->fFiddle = Definition::NormalizedName(definition->fName);
+ }
+ } while (!this->eof());
+ return true;
+}
+
+bool BmhParser::dumpExamples(FILE* fiddleOut, Definition& def, bool* continuation) const {
+ if (MarkType::kExample == def.fMarkType) {
+ string result;
+ if (!this->exampleToScript(&def, BmhParser::ExampleOptions::kAll, &result)) {
+ return false;
+ }
+ if (result.length() > 0) {
+ result += "\n";
+ result += "}";
+ if (*continuation) {
+ fprintf(fiddleOut, ",\n");
+ } else {
+ *continuation = true;
+ }
+ fprintf(fiddleOut, "%s", result.c_str());
+ }
+ return true;
+ }
+ for (auto& child : def.fChildren ) {
+ if (!this->dumpExamples(fiddleOut, *child, continuation)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool BmhParser::dumpExamples(const char* fiddleJsonFileName) const {
+ string oldFiddle(fiddleJsonFileName);
+ string newFiddle(fiddleJsonFileName);
+ newFiddle += "_new";
+ FILE* fiddleOut = fopen(newFiddle.c_str(), "wb");
+ if (!fiddleOut) {
+ SkDebugf("could not open output file %s\n", newFiddle.c_str());
+ return false;
+ }
+ fprintf(fiddleOut, "{\n");
+ bool continuation = false;
+ for (const auto& topic : fTopicMap) {
+ if (topic.second->fParent) {
+ continue;
+ }
+ this->dumpExamples(fiddleOut, *topic.second, &continuation);
+ }
+ fprintf(fiddleOut, "\n}\n");
+ fclose(fiddleOut);
+ if (ParserCommon::WrittenFileDiffers(oldFiddle, newFiddle)) {
+ ParserCommon::CopyToFile(oldFiddle, newFiddle);
+ SkDebugf("wrote %s\n", fiddleJsonFileName);
+ } else {
+ remove(newFiddle.c_str());
+ }
+ return true;
+}
+
+int BmhParser::endHashCount() const {
+ const char* end = fLine + this->lineLength();
+ int count = 0;
+ while (fLine < end && fMC == *--end) {
+ count++;
+ }
+ return count;
+}
+
+bool BmhParser::endTableColumn(const char* end, const char* terminator) {
+ if (!this->popParentStack(fParent)) {
+ return false;
+ }
+ fWorkingColumn->fContentEnd = end;
+ fWorkingColumn->fTerminator = terminator;
+ fColStart = fChar - 1;
+ this->skipSpace();
+ fTableState = TableState::kColumnStart;
+ return true;
+}
+
+static size_t count_indent(string text, size_t test, size_t end) {
+ size_t result = test;
+ while (test < end) {
+ if (' ' != text[test]) {
+ break;
+ }
+ ++test;
+ }
+ return test - result;
+}
+
+static void add_code(string text, int pos, int end,
+ size_t outIndent, size_t textIndent, string& example) {
+ do {
+ // fix this to move whole paragraph in, out, but preserve doc indent
+ int nextIndent = count_indent(text, pos, end);
+ size_t len = text.find('\n', pos);
+ if (string::npos == len) {
+ len = end;
+ }
+ if ((size_t) (pos + nextIndent) < len) {
+ size_t indent = outIndent + nextIndent;
+ SkASSERT(indent >= textIndent);
+ indent -= textIndent;
+ for (size_t index = 0; index < indent; ++index) {
+ example += ' ';
+ }
+ pos += nextIndent;
+ while ((size_t) pos < len) {
+ example += '"' == text[pos] ? "\\\"" :
+ '\\' == text[pos] ? "\\\\" :
+ text.substr(pos, 1);
+ ++pos;
+ }
+ example += "\\n";
+ } else {
+ pos += nextIndent;
+ }
+ if ('\n' == text[pos]) {
+ ++pos;
+ }
+ } while (pos < end);
+}
+
+bool BmhParser::IsExemplary(const Definition* def) {
+ return kMarkProps[(int) def->fMarkType].fExemplary != Exemplary::kNo;
+}
+
+bool BmhParser::exampleToScript(Definition* def, ExampleOptions exampleOptions,
+ string* result) const {
+ bool hasFiddle = true;
+ const Definition* platform = def->hasChild(MarkType::kPlatform);
+ if (platform) {
+ TextParser platParse(platform);
+ hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd);
+ }
+ if (!hasFiddle) {
+ *result = "";
+ return true;
+ }
+ string text = this->extractText(def, TrimExtract::kNo);
+ bool textOut = string::npos != text.find("SkDebugf(")
+ || string::npos != text.find("dump(")
+ || string::npos != text.find("dumpHex(");
+ string heightStr = "256";
+ string widthStr = "256";
+ string normalizedName(def->fFiddle);
+ string code;
+ string imageStr = "0";
+ string srgbStr = "false";
+ string durationStr = "0";
+ for (auto iter : def->fChildren) {
+ switch (iter->fMarkType) {
+ case MarkType::kDuration:
+ durationStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
+ break;
+ case MarkType::kHeight:
+ heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
+ break;
+ case MarkType::kWidth:
+ widthStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
+ break;
+ case MarkType::kDescription:
+ // ignore for now
+ break;
+ case MarkType::kFunction: {
+ // emit this, but don't wrap this in draw()
+ string funcText = this->extractText(&*iter, TrimExtract::kNo);
+ size_t pos = 0;
+ while (pos < funcText.length() && ' ' > funcText[pos]) {
+ ++pos;
+ }
+ size_t indent = count_indent(funcText, pos, funcText.length());
+ add_code(funcText, pos, funcText.length(), 0, indent, code);
+ code += "\\n";
+ } break;
+ case MarkType::kComment:
+ break;
+ case MarkType::kImage:
+ imageStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
+ break;
+ case MarkType::kToDo:
+ break;
+ case MarkType::kBug:
+ case MarkType::kMarkChar:
+ case MarkType::kPlatform:
+ case MarkType::kPhraseRef:
+ // ignore for now
+ break;
+ case MarkType::kSet:
+ if ("sRGB" == string(iter->fContentStart,
+ iter->fContentEnd - iter->fContentStart)) {
+ srgbStr = "true";
+ } else {
+ SkASSERT(0); // more work to do
+ return false;
+ }
+ break;
+ case MarkType::kStdOut:
+ textOut = true;
+ break;
+ default:
+ SkASSERT(0); // more coding to do
+ }
+ }
+ string animatedStr = "0" != durationStr ? "true" : "false";
+ string textOutStr = textOut ? "true" : "false";
+ size_t pos = 0;
+ while (pos < text.length() && ' ' > text[pos]) {
+ ++pos;
+ }
+ size_t end = text.length();
+ size_t outIndent = 0;
+ size_t textIndent = count_indent(text, pos, end);
+ if ("" == def->fWrapper) {
+ this->setWrapper(def);
+ }
+ if (def->fWrapper.length() > 0) {
+ code += def->fWrapper;
+ code += "\\n";
+ outIndent = 4;
+ }
+ add_code(text, pos, end, outIndent, textIndent, code);
+ if (def->fWrapper.length() > 0) {
+ code += "}";
+ }
+ string example = "\"" + normalizedName + "\": {\n";
+ string filename = def->fileName();
+ string baseFile = filename.substr(0, filename.length() - 4);
+ if (ExampleOptions::kText == exampleOptions) {
+ example += " \"code\": \"" + code + "\",\n";
+ example += " \"hash\": \"" + def->fHash + "\",\n";
+ example += " \"file\": \"" + baseFile + "\",\n";
+ example += " \"name\": \"" + def->fName + "\",";
+ } else {
+ example += " \"code\": \"" + code + "\",\n";
+ if (ExampleOptions::kPng == exampleOptions) {
+ example += " \"width\": " + widthStr + ",\n";
+ example += " \"height\": " + heightStr + ",\n";
+ example += " \"hash\": \"" + def->fHash + "\",\n";
+ example += " \"file\": \"" + baseFile + "\",\n";
+ example += " \"name\": \"" + def->fName + "\"\n";
+ example += "}";
+ } else {
+ example += " \"options\": {\n";
+ example += " \"width\": " + widthStr + ",\n";
+ example += " \"height\": " + heightStr + ",\n";
+ example += " \"source\": " + imageStr + ",\n";
+ example += " \"srgb\": " + srgbStr + ",\n";
+ example += " \"f16\": false,\n";
+ example += " \"textOnly\": " + textOutStr + ",\n";
+ example += " \"animated\": " + animatedStr + ",\n";
+ example += " \"duration\": " + durationStr + "\n";
+ example += " },\n";
+ example += " \"fast\": true";
+ }
+ }
+ *result = example;
+ return true;
+}
+
+string BmhParser::extractText(const Definition* def, TrimExtract trimExtract) const {
+ string result;
+ TextParser parser(def);
+ auto childIter = def->fChildren.begin();
+ while (!parser.eof()) {
+ const char* end = def->fChildren.end() == childIter ? parser.fEnd : (*childIter)->fStart;
+ string fragment(parser.fChar, end - parser.fChar);
+ trim_end(fragment);
+ if (TrimExtract::kYes == trimExtract) {
+ trim_start(fragment);
+ if (result.length()) {
+ result += '\n';
+ result += '\n';
+ }
+ }
+ if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) {
+ result += fragment;
+ }
+ parser.skipTo(end);
+ if (def->fChildren.end() != childIter) {
+ Definition* child = *childIter;
+ if (MarkType::kPhraseRef == child->fMarkType) {
+ auto phraseIter = fPhraseMap.find(child->fName);
+ if (fPhraseMap.end() == phraseIter) {
+ return def->reportError<string>("missing phrase definition");
+ }
+ Definition* phrase = phraseIter->second;
+ // count indent of last line in result
+ size_t lastLF = result.rfind('\n');
+ size_t startPos = string::npos == lastLF ? 0 : lastLF;
+ size_t lastLen = result.length() - startPos;
+ size_t indent = count_indent(result, startPos, result.length()) + 4;
+ string phraseStr = this->extractText(phrase, TrimExtract::kNo);
+ startPos = 0;
+ bool firstTime = true;
+ size_t endPos;
+ do {
+ endPos = phraseStr.find('\n', startPos);
+ size_t len = (string::npos != endPos ? endPos : phraseStr.length()) - startPos;
+ if (firstTime && lastLen + len + 1 < 100) { // FIXME: make 100 global const or something
+ result += ' ';
+ } else {
+ result += '\n';
+ result += string(indent, ' ');
+ }
+ firstTime = false;
+ string tmp = phraseStr.substr(startPos, len);
+ result += tmp;
+ startPos = endPos + 1;
+ } while (string::npos != endPos);
+ result += '\n';
+ }
+ parser.skipTo(child->fTerminator);
+ std::advance(childIter, 1);
+ }
+ }
+ 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* ) {";
+ string text = this->extractText(def, TrimExtract::kNo);
+ size_t nonSpace = 0;
+ while (nonSpace < text.length() && ' ' >= text[nonSpace]) {
+ ++nonSpace;
+ }
+ bool hasFunc = !text.compare(nonSpace, sizeof(drawWrapper) - 1, drawWrapper);
+ bool noCanvas = !text.compare(nonSpace, sizeof(drawNoCanvas) - 1, drawNoCanvas);
+ bool hasCanvas = string::npos != text.find("SkCanvas canvas");
+ SkASSERT(!hasFunc || !noCanvas);
+ bool preprocessor = text[0] == '#';
+ bool wrapCode = !hasFunc && !noCanvas && !preprocessor;
+ if (wrapCode) {
+ def->fWrapper = hasCanvas ? string(drawNoCanvas) : string(drawWrapper);
+ }
+}
+
+RootDefinition* BmhParser::findBmhObject(MarkType markType, string typeName) {
+ const auto& mapIter = std::find_if(fMaps.begin(), fMaps.end(),
+ [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
+ if (mapIter == fMaps.end()) {
+ return nullptr;
+ }
+ return &(*mapIter->fMap)[typeName];
+}
+
+// FIXME: some examples may produce different output on different platforms
+// if the text output can be different, think of how to author that
+
+bool BmhParser::findDefinitions() {
+ bool lineStart = true;
+ const char* lastChar = nullptr;
+ const char* lastMC = nullptr;
+ fParent = nullptr;
+ while (!this->eof()) {
+ if (this->peek() == fMC) {
+ lastMC = fChar;
+ this->next();
+ if (this->peek() == fMC) {
+ this->next();
+ if (!lineStart && ' ' < this->peek()) {
+ if (!fParent || MarkType::kFormula != fParent->fMarkType) {
+ return this->reportError<bool>("expected definition");
+ }
+ }
+ if (this->peek() != fMC) {
+ if (MarkType::kColumn == fParent->fMarkType) {
+ SkASSERT(TableState::kColumnEnd == fTableState);
+ if (!this->endTableColumn(lastChar, lastMC)) {
+ return false;
+ }
+ SkASSERT(fRow);
+ if (!this->popParentStack(fParent)) {
+ return false;
+ }
+ fRow->fContentEnd = fWorkingColumn->fContentEnd;
+ fWorkingColumn = nullptr;
+ fRow = nullptr;
+ fTableState = TableState::kNone;
+ } else {
+ vector<string> parentName;
+ parentName.push_back(fParent->fName);
+ if (!this->addDefinition(fChar - 1, true, fParent->fMarkType, parentName,
+ HasTag::kNo)) {
+ return false;
+ }
+ }
+ } else {
+ SkAssertResult(this->next() == fMC);
+ fMC = this->next(); // change markup character
+ if (' ' >= fMC) {
+ return this->reportError<bool>("illegal markup character");
+ }
+ fMarkup.emplace_front(MarkType::kMarkChar, fChar - 4, fLineCount, fParent, fMC);
+ Definition* markChar = &fMarkup.front();
+ markChar->fContentStart = fChar - 1;
+ this->skipToEndBracket('\n');
+ markChar->fContentEnd = fChar;
+ markChar->fTerminator = fChar;
+ fParent->fChildren.push_back(markChar);
+ }
+ } else if (this->peek() >= 'A' && this->peek() <= 'Z') {
+ const char* defStart = fChar - 1;
+ MarkType markType = this->getMarkType(MarkLookup::kRequire);
+ bool hasEnd = this->hasEndToken();
+ if (!hasEnd && fParent) {
+ MarkType parentType = fParent->fMarkType;
+ uint64_t parentMask = kMarkProps[(int) markType].fParentMask;
+ if (parentMask && !(parentMask & (1LL << (int) parentType))) {
+ return this->reportError<bool>("invalid parent");
+ }
+ }
+ if (!this->skipName(kMarkProps[(int) markType].fName)) {
+ return this->reportError<bool>("illegal markup character");
+ }
+ if (!this->skipSpace()) {
+ return this->reportError<bool>("unexpected end");
+ }
+ lineStart = '\n' == this->peek();
+ bool expectEnd = true;
+ vector<string> typeNameBuilder = this->typeName(markType, &expectEnd);
+ if (fCloned && MarkType::kMethod != markType && MarkType::kExample != markType
+ && !fAnonymous) {
+ return this->reportError<bool>("duplicate name");
+ }
+ if (hasEnd && expectEnd) {
+ if (fMC == this->peek()) {
+ return this->reportError<bool>("missing body");
+ }
+ }
+ if (!this->addDefinition(defStart, hasEnd, markType, typeNameBuilder,
+ HasTag::kYes)) {
+ return false;
+ }
+ continue;
+ } else if (this->peek() == ' ') {
+ if (!fParent || (MarkType::kFormula != fParent->fMarkType
+ && MarkType::kLegend != fParent->fMarkType
+ && MarkType::kList != fParent->fMarkType
+ && MarkType::kLine != fParent->fMarkType
+ && MarkType::kTable != fParent->fMarkType)) {
+ int endHashes = this->endHashCount();
+ if (endHashes <= 1) {
+ if (fParent) {
+ if (TableState::kColumnEnd == fTableState) {
+ if (!this->endTableColumn(lastChar, lastMC)) {
+ return false;
+ }
+ } else { // one line comment
+ fMarkup.emplace_front(MarkType::kComment, fChar - 1, fLineCount,
+ fParent, fMC);
+ Definition* comment = &fMarkup.front();
+ comment->fContentStart = fChar - 1;
+ this->skipToEndBracket('\n');
+ comment->fContentEnd = fChar;
+ comment->fTerminator = fChar;
+ fParent->fChildren.push_back(comment);
+ }
+ } else {
+ fChar = fLine + this->lineLength() - 1;
+ }
+ } else { // table row
+ if (2 != endHashes) {
+ string errorStr = "expect ";
+ errorStr += fMC;
+ errorStr += fMC;
+ return this->reportError<bool>(errorStr.c_str());
+ }
+ if (!fParent || MarkType::kTable != fParent->fMarkType) {
+ return this->reportError<bool>("missing table");
+ }
+ }
+ } else if (TableState::kNone == fTableState) {
+ // fixme? no nested tables for now
+ fColStart = fChar - 1;
+ fMarkup.emplace_front(MarkType::kRow, fColStart, fLineCount, fParent, fMC);
+ fRow = &fMarkup.front();
+ fRow->fName = fParent->fName;
+ this->skipWhiteSpace();
+ fRow->fContentStart = fChar;
+ this->setAsParent(fRow);
+ fTableState = TableState::kColumnStart;
+ }
+ if (TableState::kColumnStart == fTableState) {
+ fMarkup.emplace_front(MarkType::kColumn, fColStart, fLineCount, fParent, fMC);
+ fWorkingColumn = &fMarkup.front();
+ fWorkingColumn->fName = fParent->fName;
+ fWorkingColumn->fContentStart = fChar;
+ this->setAsParent(fWorkingColumn);
+ fTableState = TableState::kColumnEnd;
+ continue;
+ }
+ } else if (this->peek() >= 'a' && this->peek() <= 'z') {
+ // expect zero or more letters and underscores (no spaces) then hash
+ const char* phraseNameStart = fChar;
+ this->skipPhraseName();
+ string phraseKey = string(phraseNameStart, fChar - phraseNameStart);
+ char delimiter = this->next();
+ vector<string> params;
+ vector<const char*> paramsLoc;
+ if (fMC != delimiter) {
+ if ('(' != delimiter) {
+ return this->reportError<bool>("expect # after phrase name");
+ }
+ // phrase may take comma delimited parameter list
+ do {
+ const char* subEnd = this->anyOf(",)\n");
+ if (!subEnd || '\n' == *subEnd) {
+ return this->reportError<bool>("unexpected phrase list end");
+ }
+ params.push_back(string(fChar, subEnd - fChar));
+ paramsLoc.push_back(fChar);
+ this->skipTo(subEnd);
+
+ } while (')' != this->next());
+ }
+ const char* start = phraseNameStart;
+ SkASSERT('#' == start[-1]);
+ --start;
+ if (start > fStart && ' ' >= start[-1]) {
+ --start; // preserve whether to add whitespace before substitution
+ }
+ fMarkup.emplace_front(MarkType::kPhraseRef, start, fLineCount, fParent, fMC);
+ Definition* markChar = &fMarkup.front();
+ this->skipExact("#");
+ markChar->fContentStart = fChar;
+ markChar->fContentEnd = fChar;
+ markChar->fTerminator = fChar;
+ markChar->fName = phraseKey;
+ fParent->fChildren.push_back(markChar);
+ int paramLocIndex = 0;
+ for (auto param : params) {
+ const char* paramLoc = paramsLoc[paramLocIndex++];
+ fMarkup.emplace_front(MarkType::kPhraseParam, paramLoc, fLineCount, fParent,
+ fMC);
+ Definition* phraseParam = &fMarkup.front();
+ phraseParam->fContentStart = paramLoc;
+ phraseParam->fContentEnd = paramLoc + param.length();
+ phraseParam->fTerminator = paramLoc + param.length();
+ phraseParam->fName = param;
+ markChar->fChildren.push_back(phraseParam);
+ }
+ }
+ }
+ char nextChar = this->next();
+ if (' ' < nextChar) {
+ lastChar = fChar;
+ lineStart = false;
+ } else if (nextChar == '\n') {
+ lineStart = true;
+ }
+ }
+ if (fParent) {
+ return fParent->reportError<bool>("mismatched end");
+ }
+ return true;
+}
+
+MarkType BmhParser::getMarkType(MarkLookup lookup) const {
+ for (int index = 0; index <= Last_MarkType; ++index) {
+ int typeLen = strlen(kMarkProps[index].fName);
+ if (typeLen == 0) {
+ continue;
+ }
+ if (fChar + typeLen >= fEnd || fChar[typeLen] > ' ') {
+ continue;
+ }
+ int chCompare = strncmp(fChar, kMarkProps[index].fName, typeLen);
+ if (chCompare < 0) {
+ goto fail;
+ }
+ if (chCompare == 0) {
+ return (MarkType) index;
+ }
+ }
+fail:
+ if (MarkLookup::kRequire == lookup) {
+ return this->reportError<MarkType>("unknown mark type");
+ }
+ return MarkType::kNone;
+}
+
+bool BmhParser::hasEndToken() const {
+ const char* ptr = fLine;
+ char test;
+ do {
+ if (ptr >= fEnd) {
+ return false;
+ }
+ test = *ptr++;
+ if ('\n' == test) {
+ return false;
+ }
+ } while (fMC != test || fMC != *ptr);
+ return true;
+}
+
+string BmhParser::memberName() {
+ const char* wordStart;
+ const char* prefixes[] = { "static", "const" };
+ do {
+ this->skipSpace();
+ wordStart = fChar;
+ this->skipToNonName();
+ } while (this->anyOf(wordStart, prefixes, SK_ARRAY_COUNT(prefixes)));
+ if ('*' == this->peek()) {
+ this->next();
+ }
+ return this->className(MarkType::kMember);
+}
+
+string BmhParser::methodName() {
+ if (this->hasEndToken()) {
+ if (!fParent || !fParent->fName.length()) {
+ return this->reportError<string>("missing parent method name");
+ }
+ SkASSERT(fMC == this->peek());
+ this->next();
+ SkASSERT(fMC == this->peek());
+ this->next();
+ SkASSERT(fMC != this->peek());
+ return fParent->fName;
+ }
+ string builder;
+ const char* end = this->lineEnd();
+ const char* paren = this->strnchr('(', end);
+ if (!paren) {
+ return this->reportError<string>("missing method name and reference");
+ }
+ {
+ TextParserSave endCheck(this);
+ while (end < fEnd && !this->strnchr(')', end)) {
+ fChar = end + 1;
+ end = this->lineEnd();
+ }
+ if (end >= fEnd) {
+ return this->reportError<string>("missing method end paren");
+ }
+ endCheck.restore();
+ }
+ const char* nameStart = paren;
+ char ch;
+ bool expectOperator = false;
+ bool isConstructor = false;
+ const char* nameEnd = nullptr;
+ while (nameStart > fChar && ' ' != (ch = *--nameStart)) {
+ if (!isalnum(ch) && '_' != ch) {
+ if (nameEnd) {
+ break;
+ }
+ expectOperator = true;
+ continue;
+ }
+ if (!nameEnd) {
+ nameEnd = nameStart + 1;
+ }
+ }
+ if (!nameEnd) {
+ return this->reportError<string>("unexpected method name char");
+ }
+ if (' ' == nameStart[0]) {
+ ++nameStart;
+ }
+ if (nameEnd <= nameStart) {
+ return this->reportError<string>("missing method name");
+ }
+ if (nameStart >= paren) {
+ return this->reportError<string>("missing method name length");
+ }
+ string name(nameStart, nameEnd - nameStart);
+ bool allLower = true;
+ for (int index = 0; index < (int) (nameEnd - nameStart); ++index) {
+ if (!islower(nameStart[index])) {
+ allLower = false;
+ break;
+ }
+ }
+ if (expectOperator && "operator" != name) {
+ return this->reportError<string>("expected operator");
+ }
+ const Definition* parent = this->parentSpace();
+ if (parent && parent->fName.length() > 0) {
+ size_t parentNameIndex = parent->fName.rfind(':');
+ parentNameIndex = string::npos == parentNameIndex ? 0 : parentNameIndex + 1;
+ string parentName = parent->fName.substr(parentNameIndex);
+ if (parentName == name) {
+ isConstructor = true;
+ } else if ('~' == name[0]) {
+ if (parentName != name.substr(1)) {
+ return this->reportError<string>("expected destructor");
+ }
+ isConstructor = true;
+ }
+ builder = parent->fName + "::";
+ }
+ bool addConst = false;
+ if (isConstructor || expectOperator) {
+ paren = this->strnchr(')', end) + 1;
+ TextParserSave saveState(this);
+ this->skipTo(paren);
+ if (this->skipExact("_const")) {
+ addConst = true;
+ }
+ saveState.restore();
+ }
+ builder.append(nameStart, paren - nameStart);
+ if (addConst) {
+ builder.append("_const");
+ }
+ if (!expectOperator && allLower) {
+ builder.append("()");
+ }
+ int parens = 0;
+ while (fChar < end || parens > 0) {
+ if ('(' == this->peek()) {
+ ++parens;
+ } else if (')' == this->peek()) {
+ --parens;
+ }
+ this->next();
+ }
+ TextParserSave saveState(this);
+ this->skipWhiteSpace();
+ if (this->startsWith("const")) {
+ this->skipName("const");
+ } else {
+ saveState.restore();
+ }
+// this->next();
+ if (string::npos != builder.find('\n')) {
+ builder.erase(std::remove(builder.begin(), builder.end(), '\n'), builder.end());
+ }
+ return uniqueRootName(builder, MarkType::kMethod);
+}
+
+const Definition* BmhParser::parentSpace() const {
+ Definition* parent = nullptr;
+ Definition* test = fParent;
+ while (test) {
+ if (MarkType::kClass == test->fMarkType ||
+ MarkType::kEnumClass == test->fMarkType ||
+ MarkType::kStruct == test->fMarkType) {
+ parent = test;
+ break;
+ }
+ test = test->fParent;
+ }
+ 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) {
+ --start;
+ }
+ SkASSERT (start < end);
+ // if end is preceeeded by \n#MarkType ## backup to there
+ TextParser parser(fFileName, start, fChar, fLineCount);
+ parser.skipWhiteSpace();
+ if (parser.eof() || fMC != parser.next()) {
+ return end;
+ }
+ const char* markName = kMarkProps[(int) definition->fMarkType].fName;
+ if (!parser.skipExact(markName)) {
+ return end;
+ }
+ parser.skipWhiteSpace();
+ 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();
+ }
+ }
+ if (parser.eof() || fMC != parser.next()) {
+ return end;
+ }
+ if (!parser.eof() && fMC != parser.next()) {
+ return end;
+ }
+ SkASSERT(parser.eof());
+ return start;
+}
+
+void BmhParser::parseHashAnchor(Definition* definition) {
+ this->skipToEndBracket(fMC);
+ fMarkup.emplace_front(MarkType::kLink, fChar, fLineCount, definition, fMC);
+ SkAssertResult(fMC == this->next());
+ this->skipWhiteSpace();
+ Definition* link = &fMarkup.front();
+ link->fContentStart = fChar;
+ link->fContentEnd = this->trimmedBracketEnd(fMC);
+ this->skipToEndBracket(fMC);
+ SkAssertResult(fMC == this->next());
+ SkAssertResult(fMC == this->next());
+ link->fTerminator = fChar;
+ definition->fContentEnd = link->fContentEnd;
+ definition->fTerminator = fChar;
+ definition->fChildren.emplace_back(link);
+}
+
+void BmhParser::parseHashFormula(Definition* definition) {
+ const char* start = definition->fContentStart;
+ definition->trimEnd();
+ const char* end = definition->fContentEnd;
+ fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition, fMC);
+ Definition* text = &fMarkup.front();
+ text->fContentStart = start;
+ text->fContentEnd = end;
+ text->fTerminator = definition->fTerminator;
+ definition->fChildren.emplace_back(text);
+}
+
+void BmhParser::parseHashLine(Definition* definition) {
+ const char* nextLF = this->strnchr('\n', this->fEnd);
+ const char* start = fChar;
+ const char* end = this->trimmedBracketEnd(fMC);
+ this->skipToEndBracket(fMC, nextLF);
+ if (fMC != this->next() || fMC != this->next()) {
+ return this->reportError<void>("expected ## to delineate line");
+ }
+ fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition, fMC);
+ Definition* text = &fMarkup.front();
+ if (!islower(start[0]) && (!isdigit(start[0])
+ || MarkType::kConst != definition->fParent->fMarkType)) {
+ return this->reportError<void>("expect lower case start");
+ }
+ string contents = string(start, end - start);
+ if (string::npos != contents.find('.')) {
+ return this->reportError<void>("expect phrase, not sentence");
+ }
+ size_t firstSpace = contents.find(' ');
+ if (string::npos == firstSpace || 0 == firstSpace || 's' != start[firstSpace - 1]) {
+ if (MarkType::kMethod == fParent->fMarkType && "experimental" != contents
+ && "incomplete" != contents) {
+ return this->reportError<void>( "expect phrase in third person present"
+ " tense (1st word should end in 's'");
+ }
+ }
+ text->fContentStart = start;
+ text->fContentEnd = end;
+ text->fTerminator = fChar;
+ definition->fContentEnd = text->fContentEnd;
+ definition->fTerminator = fChar;
+ definition->fChildren.emplace_back(text);
+}
+
+bool BmhParser::popParentStack(Definition* definition) {
+ if (!fParent) {
+ return this->reportError<bool>("missing parent");
+ }
+ if (definition != fParent) {
+ return this->reportError<bool>("definition end is not parent");
+ }
+ if (!definition->fStart) {
+ return this->reportError<bool>("definition missing start");
+ }
+ if (definition->fContentEnd) {
+ return this->reportError<bool>("definition already ended");
+ }
+ // more to figure out to handle table columns, at minimum
+ const char* end = fChar;
+ if (fMC != end[0]) {
+ while (end > definition->fContentStart && ' ' >= end[-1]) {
+ --end;
+ }
+ SkASSERT(&end[-1] >= definition->fContentStart && fMC == end[-1]
+ && (MarkType::kColumn == definition->fMarkType
+ || (&end[-2] >= definition->fContentStart && fMC == end[-2])));
+ end -= 2;
+ }
+ end = checkForFullTerminal(end, definition);
+ definition->fContentEnd = end;
+ definition->fTerminator = fChar;
+ fParent = definition->fParent;
+ if (!fParent || (MarkType::kTopic == fParent->fMarkType && !fParent->fParent)) {
+ fRoot = nullptr;
+ }
+ return true;
+}
+
+
+
+bool BmhParser::skipNoName() {
+ if ('\n' == this->peek()) {
+ this->next();
+ return true;
+ }
+ this->skipWhiteSpace();
+ if (fMC != this->peek()) {
+ return this->reportError<bool>("expected end mark 1");
+ }
+ this->next();
+ if (fMC != this->peek()) {
+ return this->reportError<bool>("expected end mark 2");
+ }
+ this->next();
+ return true;
+}
+
+bool BmhParser::skipToDefinitionEnd(MarkType markType) {
+ if (this->eof()) {
+ return this->reportError<bool>("missing end");
+ }
+ const char* start = fLine;
+ int startLineCount = fLineCount;
+ int stack = 1;
+ ptrdiff_t lineLen;
+ bool foundEnd = false;
+ do {
+ lineLen = this->lineLength();
+ if (fMC != *fChar++) {
+ continue;
+ }
+ if (fMC == *fChar) {
+ continue;
+ }
+ if (' ' == *fChar) {
+ continue;
+ }
+ MarkType nextType = this->getMarkType(MarkLookup::kAllowUnknown);
+ if (markType != nextType) {
+ continue;
+ }
+ bool hasEnd = this->hasEndToken();
+ if (hasEnd) {
+ if (!--stack) {
+ foundEnd = true;
+ continue;
+ }
+ } else {
+ ++stack;
+ }
+ } while ((void) ++fLineCount, (void) (fLine += lineLen), (void) (fChar = fLine),
+ !this->eof() && !foundEnd);
+ if (foundEnd) {
+ return true;
+ }
+ fLineCount = startLineCount;
+ fLine = start;
+ fChar = start;
+ return this->reportError<bool>("unbalanced stack");
+}
+
+bool BmhParser::skipToString() {
+ this->skipSpace();
+ if (fMC != this->peek()) {
+ return this->reportError<bool>("expected end mark 3");
+ }
+ this->next();
+ this->skipSpace();
+ // body is text from here to double fMC
+ // no single fMC allowed, no linefeed allowed
+ return true;
+}
+
+vector<string> BmhParser::topicName() {
+ vector<string> result;
+ this->skipWhiteSpace();
+ const char* lineEnd = fLine + this->lineLength();
+ const char* nameStart = fChar;
+ while (fChar < lineEnd) {
+ char ch = this->next();
+ SkASSERT(',' != ch);
+ if ('\n' == ch) {
+ break;
+ }
+ if (fMC == ch) {
+ break;
+ }
+ }
+ if (fChar - 1 > nameStart) {
+ string builder(nameStart, fChar - nameStart - 1);
+ trim_start_end(builder);
+ result.push_back(builder);
+ }
+ if (fChar < lineEnd && fMC == this->peek()) {
+ this->next();
+ }
+ return result;
+}
+
+// typeName parsing rules depend on mark type
+vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
+ fAnonymous = false;
+ fCloned = false;
+ vector<string> result;
+ string builder;
+ if (fParent) {
+ builder = fParent->fName;
+ }
+ switch (markType) {
+ case MarkType::kDefine:
+ case MarkType::kEnum:
+ // enums may be nameless
+ case MarkType::kConst:
+ case MarkType::kEnumClass:
+ case MarkType::kClass:
+ case MarkType::kStruct:
+ // expect name
+ builder = this->className(markType);
+ break;
+ case MarkType::kExample:
+ // check to see if one already exists -- if so, number this one
+ builder = this->uniqueName(string(), markType);
+ this->skipNoName();
+ break;
+ case MarkType::kCode:
+ case MarkType::kDescription:
+ case MarkType::kExternal:
+ case MarkType::kFunction:
+ case MarkType::kLegend:
+ case MarkType::kList:
+ case MarkType::kNoExample:
+ case MarkType::kPrivate:
+ this->skipNoName();
+ break;
+ case MarkType::kFormula:
+ case MarkType::kLine:
+ this->skipToString();
+ break;
+ case MarkType::kAlias:
+ case MarkType::kAnchor:
+ case MarkType::kBug: // fixme: expect number
+ case MarkType::kDeprecated:
+ case MarkType::kDetails:
+ case MarkType::kDuration:
+ case MarkType::kExperimental:
+ case MarkType::kFile:
+ case MarkType::kFilter:
+ case MarkType::kHeight:
+ case MarkType::kIllustration:
+ case MarkType::kImage:
+ case MarkType::kIn:
+ case MarkType::kLiteral:
+ case MarkType::kNoJustify:
+ case MarkType::kOutdent:
+ case MarkType::kPlatform:
+ case MarkType::kPopulate:
+ case MarkType::kReturn:
+ case MarkType::kSeeAlso:
+ case MarkType::kSet:
+ case MarkType::kSubstitute:
+ case MarkType::kToDo:
+ case MarkType::kVolatile:
+ case MarkType::kWidth:
+ *checkEnd = false; // no name, may have text body
+ break;
+ case MarkType::kStdOut:
+ this->skipNoName();
+ break; // unnamed
+ case MarkType::kMember:
+ builder = this->memberName();
+ break;
+ case MarkType::kMethod:
+ builder = this->methodName();
+ break;
+ case MarkType::kTypedef:
+ builder = this->typedefName();
+ break;
+ case MarkType::kParam:
+ // fixme: expect camelCase for param
+ builder = this->word("", "");
+ this->skipSpace();
+ *checkEnd = false;
+ break;
+ case MarkType::kPhraseDef: {
+ const char* nameEnd = this->anyOf("(\n");
+ builder = string(fChar, nameEnd - fChar);
+ this->skipLower();
+ if (fChar != nameEnd) {
+ this->reportError("expect lower case only");
+ break;
+ }
+ this->skipTo(nameEnd);
+ *checkEnd = false;
+ } break;
+ case MarkType::kTable:
+ this->skipNoName();
+ break; // unnamed
+ case MarkType::kSubtopic:
+ case MarkType::kTopic:
+ // fixme: start with cap, allow space, hyphen, stop on comma
+ // one topic can have multiple type names delineated by comma
+ result = this->topicName();
+ if (result.size() == 0 && this->hasEndToken()) {
+ break;
+ }
+ return result;
+ default:
+ // fixme: don't allow silent failures
+ SkASSERT(0);
+ }
+ result.push_back(builder);
+ return result;
+}
+
+string BmhParser::typedefName() {
+ if (this->hasEndToken()) {
+ if (!fParent || !fParent->fName.length()) {
+ return this->reportError<string>("missing parent typedef name");
+ }
+ SkASSERT(fMC == this->peek());
+ this->next();
+ SkASSERT(fMC == this->peek());
+ this->next();
+ SkASSERT(fMC != this->peek());
+ return fParent->fName;
+ }
+ string builder;
+ const Definition* parent = this->parentSpace();
+ if (parent && parent->fName.length() > 0) {
+ builder = parent->fName + "::";
+ }
+ builder += TextParser::typedefName();
+ return uniqueRootName(builder, MarkType::kTypedef);
+}
+
+string BmhParser::uniqueName(string base, MarkType markType) {
+ string builder(base);
+ if (!builder.length()) {
+ builder = fParent->fName;
+ }
+ if (!fParent) {
+ return builder;
+ }
+ int number = 2;
+ string numBuilder(builder);
+ do {
+ for (auto& iter : fParent->fChildren) {
+ if (markType == iter->fMarkType) {
+ if (iter->fName == numBuilder) {
+ if (iter->fDeprecated) {
+ iter->fClone = true;
+ } else {
+ fCloned = true;
+ }
+ numBuilder = builder + '_' + to_string(number);
+ goto tryNext;
+ }
+ }
+ }
+ break;
+tryNext: ;
+ } while (++number);
+ return numBuilder;
+}
+
+string BmhParser::uniqueRootName(string base, MarkType markType) {
+ auto checkName = [markType](const Definition& def, string numBuilder) -> bool {
+ return markType == def.fMarkType && def.fName == numBuilder;
+ };
+
+ string builder(base);
+ if (!builder.length()) {
+ builder = fParent->fName;
+ }
+ int number = 2;
+ string numBuilder(builder);
+ Definition* cloned = nullptr;
+ do {
+ if (fRoot) {
+ for (auto& iter : fRoot->fBranches) {
+ if (checkName(*iter.second, numBuilder)) {
+ cloned = iter.second;
+ goto tryNext;
+ }
+ }
+ for (auto& iter : fRoot->fLeaves) {
+ if (checkName(iter.second, numBuilder)) {
+ cloned = &iter.second;
+ goto tryNext;
+ }
+ }
+ } else if (fParent) {
+ for (auto& iter : fParent->fChildren) {
+ if (checkName(*iter, numBuilder)) {
+ cloned = &*iter;
+ goto tryNext;
+ }
+ }
+ }
+ break;
+tryNext: ;
+ if ("()" == builder.substr(builder.length() - 2)) {
+ builder = builder.substr(0, builder.length() - 2);
+ }
+ if (MarkType::kMethod == markType) {
+ cloned->fCloned = true;
+ if (cloned->fDeprecated) {
+ cloned->fClone = true;
+ } else {
+ fCloned = true;
+ }
+ } else {
+ fCloned = true;
+ }
+ numBuilder = builder + '_' + to_string(number);
+ } while (++number);
+ return numBuilder;
+}
+
+void BmhParser::validate() const {
+ for (int index = 0; index <= (int) Last_MarkType; ++index) {
+ SkASSERT(kMarkProps[index].fMarkType == (MarkType) index);
+ }
+ const char* last = "";
+ for (int index = 0; index <= (int) Last_MarkType; ++index) {
+ const char* next = kMarkProps[index].fName;
+ if (!last[0]) {
+ last = next;
+ continue;
+ }
+ if (!next[0]) {
+ continue;
+ }
+ SkASSERT(strcmp(last, next) < 0);
+ last = next;
+ }
+}
+
+string BmhParser::word(string prefix, string delimiter) {
+ string builder(prefix);
+ this->skipWhiteSpace();
+ const char* lineEnd = fLine + this->lineLength();
+ const char* nameStart = fChar;
+ while (fChar < lineEnd) {
+ char ch = this->next();
+ if (' ' >= ch) {
+ break;
+ }
+ if (',' == ch) {
+ return this->reportError<string>("no comma needed");
+ break;
+ }
+ if (fMC == ch) {
+ return builder;
+ }
+ if (!isalnum(ch) && '_' != ch && ':' != ch && '-' != ch) {
+ return this->reportError<string>("unexpected char");
+ }
+ if (':' == ch) {
+ // expect pair, and expect word to start with Sk
+ if (nameStart[0] != 'S' || nameStart[1] != 'k') {
+ return this->reportError<string>("expected Sk");
+ }
+ if (':' != this->peek()) {
+ return this->reportError<string>("expected ::");
+ }
+ this->next();
+ } else if ('-' == ch) {
+ // expect word not to start with Sk or kX where X is A-Z
+ if (nameStart[0] == 'k' && nameStart[1] >= 'A' && nameStart[1] <= 'Z') {
+ return this->reportError<string>("didn't expected kX");
+ }
+ if (nameStart[0] == 'S' && nameStart[1] == 'k') {
+ return this->reportError<string>("expected Sk");
+ }
+ }
+ }
+ if (prefix.size()) {
+ builder += delimiter;
+ }
+ builder.append(nameStart, fChar - nameStart - 1);
+ return builder;
+}
diff --git a/tools/bookmaker/bmhParser.h b/tools/bookmaker/bmhParser.h
new file mode 100644
index 0000000000..2eb0053477
--- /dev/null
+++ b/tools/bookmaker/bmhParser.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef bmhParser_DEFINED
+#define bmhParser_DEFINED
+
+#include "SkCommandLineFlags.h"
+
+#include "definition.h"
+#include "parserCommon.h"
+
+class BmhParser : public ParserCommon {
+public:
+ enum class MarkLookup {
+ kRequire,
+ kAllowUnknown,
+ };
+
+ enum class ExampleOptions {
+ kText,
+ kPng,
+ kAll
+ };
+
+ enum class Exemplary {
+ kNo,
+ kYes,
+ kOptional,
+ };
+
+ enum class TableState {
+ kNone,
+ kColumnStart,
+ kColumnEnd,
+ };
+
+ enum class HasTag {
+ kNo,
+ kYes,
+ };
+
+ enum class TrimExtract {
+ kNo,
+ kYes,
+ };
+
+ BmhParser(bool skip) : ParserCommon()
+ , fMaps {
+ { &fClassMap, MarkType::kClass }
+ , { &fConstMap, MarkType::kConst }
+ , { &fDefineMap, MarkType::kDefine }
+ , { &fEnumMap, MarkType::kEnum }
+ , { &fClassMap, MarkType::kEnumClass }
+ , { &fMethodMap, MarkType::kMethod }
+ , { &fClassMap, MarkType::kStruct }
+ , { &fTypedefMap, MarkType::kTypedef }
+ }
+ , fSkip(skip) {
+ this->reset();
+ }
+
+ ~BmhParser() override {}
+
+ bool addDefinition(const char* defStart, bool hasEnd, MarkType markType,
+ const vector<string>& typeNameBuilder, HasTag hasTag);
+ bool checkEndMarker(MarkType markType, string name) const;
+ bool checkExamples() const;
+ const char* checkForFullTerminal(const char* end, const Definition* ) const;
+ bool checkParamReturn(const Definition* definition) const;
+ bool dumpExamples(FILE* fiddleOut, Definition& def, bool* continuation) const;
+ bool dumpExamples(const char* fiddleJsonFileName) const;
+ bool checkExampleHashes() const;
+ bool childOf(MarkType markType) const;
+ string className(MarkType markType);
+ bool collectExternals();
+ int endHashCount() const;
+ bool endTableColumn(const char* end, const char* terminator);
+ bool exampleToScript(Definition*, ExampleOptions, string* result ) const;
+ string extractText(const Definition* , TrimExtract ) const;
+ RootDefinition* findBmhObject(MarkType markType, string typeName);
+ bool findDefinitions();
+ Definition* findExample(string name) const;
+ 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;
+
+ bool parseFromFile(const char* path) override {
+ if (!INHERITED::parseSetup(path)) {
+ return false;
+ }
+ fCheckMethods = !strstr(path, "undocumented.bmh");
+ return findDefinitions();
+ }
+
+ void parseHashAnchor(Definition* );
+ void parseHashFormula(Definition* );
+ void parseHashLine(Definition* );
+ bool popParentStack(Definition* );
+ void reportDuplicates(const Definition& , string dup) const;
+ void resetExampleHashes();
+
+ void reset() override {
+ INHERITED::resetCommon();
+ fRoot = nullptr;
+ fWorkingColumn = nullptr;
+ fRow = nullptr;
+ fTableState = TableState::kNone;
+ fMC = '#';
+ fInChar = false;
+ fInCharCommentString = false;
+ fInComment = false;
+ fInEnum = false;
+ fInString = false;
+ 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);
+ bool skipToString();
+ void spellCheck(const char* match, SkCommandLineFlags::StringArray report) const;
+ void spellStatus(const char* match, SkCommandLineFlags::StringArray report) const;
+ vector<string> topicName();
+ vector<string> typeName(MarkType markType, bool* expectEnd);
+ string typedefName() override;
+ string uniqueName(string base, MarkType markType);
+ string uniqueRootName(string base, MarkType markType);
+ void validate() const;
+ string word(string prefix, string delimiter);
+
+public:
+ struct MarkProps {
+ const char* fName;
+ MarkType fMarkType;
+ Resolvable fResolve;
+ Exemplary fExemplary; // worthy of an example
+ uint64_t fParentMask;
+ };
+
+ struct DefinitionMap {
+ unordered_map<string, RootDefinition>* fMap;
+ MarkType fMarkType;
+ };
+
+ vector<DefinitionMap> fMaps;
+
+ static MarkProps kMarkProps[Last_MarkType + 1];
+ forward_list<RootDefinition> fTopics;
+ forward_list<Definition> fMarkup;
+ forward_list<RootDefinition> fExternals;
+ vector<string> fInputFiles;
+ unordered_map<string, RootDefinition> fClassMap;
+ unordered_map<string, RootDefinition> fConstMap;
+ unordered_map<string, RootDefinition> fDefineMap;
+ unordered_map<string, RootDefinition> fEnumMap;
+ unordered_map<string, RootDefinition> fMethodMap;
+ unordered_map<string, RootDefinition> fTypedefMap;
+ unordered_map<string, Definition*> fTopicMap;
+ unordered_map<string, Definition*> fAliasMap;
+ unordered_map<string, Definition*> fPhraseMap;
+ NameMap fGlobalNames;
+ RootDefinition* fRoot;
+ Definition* fWorkingColumn;
+ Definition* fRow;
+ const char* fColStart;
+ TableState fTableState;
+ mutable char fMC; // markup character
+ bool fAnonymous;
+ bool fCloned;
+ bool fInChar;
+ bool fInCharCommentString;
+ bool fInEnum;
+ bool fInComment;
+ bool fInString;
+ bool fCheckMethods;
+ bool fSkip = false;
+ bool fWroteOut = false;
+private:
+ typedef ParserCommon INHERITED;
+};
+
+#endif
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp
index 5b282146ad..bbbcb1b70f 100644
--- a/tools/bookmaker/bookmaker.cpp
+++ b/tools/bookmaker/bookmaker.cpp
@@ -5,14 +5,13 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
#include "SkOSPath.h"
-#ifdef SK_BUILD_FOR_WIN
-#include <Windows.h>
-#endif
-
-const string kSpellingFileName("spelling.txt");
+#include "bmhParser.h"
+#include "fiddleParser.h"
+#include "mdOut.h"
+#include "includeWriter.h"
+#include "selfCheck.h"
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.");
@@ -81,2594 +80,6 @@ see head of spellCheck.cpp for additional todos
parameters may be reused in other methods
*/
-#define M(mt) (1LL << (int) MarkType::k##mt)
-#define M_D M(Description)
-#define M_CS M(Class) | M(Struct)
-#define M_MD M(Method) | M(Define)
-#define M_MDCM M_MD | M(Const) | M(Member)
-#define M_ST M(Subtopic) | M(Topic)
-#define M_CSST M_CS | M_ST
-#ifdef M_E
-#undef M_E
-#endif
-#define M_E M(Enum) | M(EnumClass)
-
-#define R_Y Resolvable::kYes
-#define R_N Resolvable::kNo
-#define R_O Resolvable::kOut
-#define R_K Resolvable::kCode
-#define R_F Resolvable::kFormula
-#define R_C Resolvable::kClone
-
-#define E_Y Exemplary::kYes
-#define E_N Exemplary::kNo
-#define E_O Exemplary::kOptional
-
-// ToDo: add column to denote which marks are one-liners
-BmhParser::MarkProps BmhParser::kMarkProps[] = {
-// names without formal definitions (e.g. Column) aren't included
- { "", MarkType::kNone, R_Y, E_N, 0 }
-, { "A", MarkType::kAnchor, R_N, E_N, 0 }
-, { "Alias", MarkType::kAlias, R_N, E_N, M_ST | M(Const) }
-, { "Bug", MarkType::kBug, R_N, E_N, M_CSST | M_MDCM | M_E
- | M(Example) | M(NoExample) }
-, { "Class", MarkType::kClass, R_Y, E_O, M_CSST }
-, { "Code", MarkType::kCode, R_K, E_N, M_CSST | M_E | M_MD | M(Typedef) }
-, { "", MarkType::kColumn, R_Y, E_N, M(Row) }
-, { "", MarkType::kComment, R_N, E_N, 0 }
-, { "Const", MarkType::kConst, R_Y, E_O, M_E | M_CSST }
-, { "Define", MarkType::kDefine, R_O, E_Y, M_ST }
-, { "Deprecated", MarkType::kDeprecated, R_Y, E_N, M_CS | M_MDCM | M_E }
-, { "Description", MarkType::kDescription, R_Y, E_N, M(Example) | M(NoExample) }
-, { "Details", MarkType::kDetails, R_N, E_N, M(Const) }
-, { "Duration", MarkType::kDuration, R_N, E_N, M(Example) | M(NoExample) }
-, { "Enum", MarkType::kEnum, R_Y, E_O, M_CSST }
-, { "EnumClass", MarkType::kEnumClass, R_Y, E_O, M_CSST }
-, { "Example", MarkType::kExample, R_O, E_N, M_CSST | M_E | M_MD | M(Const) }
-, { "Experimental", MarkType::kExperimental, R_Y, E_N, M_CS | M_MDCM | M_E }
-, { "External", MarkType::kExternal, R_Y, E_N, 0 }
-, { "File", MarkType::kFile, R_Y, E_N, M(Topic) }
-, { "Filter", MarkType::kFilter, R_N, E_N, M(Subtopic) | M(Code) }
-, { "Formula", MarkType::kFormula, R_F, E_N, M(Column) | M(Description)
- | M_E | M_ST | M_MDCM }
-, { "Function", MarkType::kFunction, R_O, E_N, M(Example) | M(NoExample) }
-, { "Height", MarkType::kHeight, R_N, E_N, M(Example) | M(NoExample) }
-, { "Illustration", MarkType::kIllustration, R_N, E_N, M_CSST | M_MD }
-, { "Image", MarkType::kImage, R_N, E_N, M(Example) | M(NoExample) }
-, { "In", MarkType::kIn, R_N, E_N, M_CSST | M_E | M(Method) | M(Typedef) | M(Code) }
-, { "Legend", MarkType::kLegend, R_Y, E_N, M(Table) }
-, { "Line", MarkType::kLine, R_N, E_N, M_CSST | M_E | M(Method) | M(Typedef) }
-, { "", MarkType::kLink, R_N, E_N, M(Anchor) }
-, { "List", MarkType::kList, R_Y, E_N, M(Method) | M_CSST | M_E | M_D }
-, { "Literal", MarkType::kLiteral, R_N, E_N, M(Code) }
-, { "", MarkType::kMarkChar, R_N, E_N, 0 }
-, { "Member", MarkType::kMember, R_Y, E_O, M_CSST }
-, { "Method", MarkType::kMethod, R_Y, E_Y, M_CSST }
-, { "NoExample", MarkType::kNoExample, R_N, E_N, M_CSST | M_E | M_MD }
-, { "NoJustify", MarkType::kNoJustify, R_N, E_N, M(Const) | M(Member) }
-, { "Outdent", MarkType::kOutdent, R_N, E_N, M(Code) }
-, { "Param", MarkType::kParam, R_Y, E_N, M(Method) | M(Define) }
-, { "PhraseDef", MarkType::kPhraseDef, R_Y, E_N, M_ST }
-, { "", MarkType::kPhraseParam, R_Y, E_N, 0 }
-, { "", MarkType::kPhraseRef, R_N, E_N, 0 }
-, { "Platform", MarkType::kPlatform, R_N, E_N, M(Example) | M(NoExample) }
-, { "Populate", MarkType::kPopulate, R_N, E_N, M(Code) | M(Method) }
-, { "Private", MarkType::kPrivate, R_N, E_N, M_CSST | M_MDCM | M_E }
-, { "Return", MarkType::kReturn, R_Y, E_N, M(Method) }
-, { "", MarkType::kRow, R_Y, E_N, M(Table) | M(List) }
-, { "SeeAlso", MarkType::kSeeAlso, R_C, E_N, M_CSST | M_E | M_MD | M(Typedef) }
-, { "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(Alias) | M_ST }
-, { "Subtopic", MarkType::kSubtopic, R_Y, E_Y, M_CSST | M_E }
-, { "Table", MarkType::kTable, R_Y, E_N, M(Method) | M_CSST | M_E }
-, { "Template", MarkType::kTemplate, R_Y, E_N, M_CSST }
-, { "", MarkType::kText, R_N, E_N, 0 }
-, { "ToDo", MarkType::kToDo, R_N, E_N, 0 }
-, { "Topic", MarkType::kTopic, R_Y, E_Y, 0 }
-, { "Typedef", MarkType::kTypedef, R_Y, E_O, M_CSST | M_E }
-, { "Union", MarkType::kUnion, R_Y, E_N, M_CSST }
-, { "Using", MarkType::kUsing, R_Y, E_O, M_CSST }
-, { "Volatile", MarkType::kVolatile, R_N, E_N, M(StdOut) }
-, { "Width", MarkType::kWidth, R_N, E_N, M(Example) | M(NoExample) }
-};
-
-#undef R_O
-#undef R_N
-#undef R_Y
-#undef R_K
-#undef R_F
-#undef R_C
-
-#undef M_E
-#undef M_CSST
-#undef M_ST
-#undef M_CS
-#undef M_MCD
-#undef M_D
-#undef M
-
-#undef E_Y
-#undef E_N
-#undef E_O
-
-bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markType,
- const vector<string>& typeNameBuilder, HasTag hasTag) {
- Definition* definition = nullptr;
- switch (markType) {
- case MarkType::kComment:
- if (!this->skipToDefinitionEnd(markType)) {
- return false;
- }
- return true;
- // these types may be referred to by name
- case MarkType::kClass:
- case MarkType::kStruct:
- case MarkType::kConst:
- case MarkType::kDefine:
- case MarkType::kEnum:
- case MarkType::kEnumClass:
- case MarkType::kMember:
- case MarkType::kMethod:
- case MarkType::kTypedef: {
- if (!typeNameBuilder.size()) {
- return this->reportError<bool>("unnamed markup");
- }
- if (typeNameBuilder.size() > 1) {
- return this->reportError<bool>("expected one name only");
- }
- string name = typeNameBuilder[0];
- 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) {
- return this->reportError<bool>("expected parent");
- }
- if (fParent == fRoot && hasEnd) {
- RootDefinition* rootParent = fRoot->rootParent();
- if (rootParent) {
- fRoot = rootParent;
- }
- definition = fParent;
- } else {
- if (!hasEnd && fRoot->find(name, RootDefinition::AllowParens::kNo)) {
- return this->reportError<bool>("duplicate symbol");
- }
- if (MarkType::kStruct == markType || MarkType::kClass == markType
- || MarkType::kEnumClass == markType) {
- // if class or struct, build fRoot hierarchy
- // and change isDefined to search all parents of fRoot
- SkASSERT(!hasEnd);
- RootDefinition* childRoot = new RootDefinition;
- (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 {
- definition = &fRoot->fLeaves[name];
- }
- }
- }
- if (hasEnd) {
- Exemplary hasExample = Exemplary::kNo;
- bool hasExcluder = false;
- for (auto child : definition->fChildren) {
- if (MarkType::kExample == child->fMarkType) {
- hasExample = Exemplary::kYes;
- }
- hasExcluder |= MarkType::kPrivate == child->fMarkType
- || MarkType::kDeprecated == child->fMarkType
- || MarkType::kExperimental == child->fMarkType
- || MarkType::kNoExample == child->fMarkType;
- }
- if (kMarkProps[(int) markType].fExemplary != hasExample
- && kMarkProps[(int) markType].fExemplary != Exemplary::kOptional) {
- if (string::npos == fFileName.find("undocumented")
- && !hasExcluder) {
- hasExample == Exemplary::kNo ?
- this->reportWarning("missing example") :
- this->reportWarning("unexpected example");
- }
-
- }
- if (MarkType::kMethod == markType) {
- if (fCheckMethods && !definition->checkMethod()) {
- return false;
- }
- }
- if (HasTag::kYes == hasTag) {
- if (!this->checkEndMarker(markType, definition->fName)) {
- return false;
- }
- }
- if (!this->popParentStack(definition)) {
- return false;
- }
- if (fRoot == definition) {
- fRoot = nullptr;
- }
- } else {
- definition->fStart = defStart;
- this->skipSpace();
- definition->fFileName = fFileName;
- definition->fContentStart = fChar;
- definition->fLineCount = fLineCount;
- definition->fClone = fCloned;
- if (MarkType::kConst == markType) {
- // todo: require that fChar points to def on same line as markup
- // additionally add definition to class children if it is not already there
- if (definition->fParent != fRoot) {
-// fRoot->fChildren.push_back(definition);
- }
- }
- SkASSERT(string::npos == name.find('\n'));
- definition->fName = name;
- if (MarkType::kMethod == markType) {
- if (string::npos != name.find(':', 0)) {
- definition->setCanonicalFiddle();
- } else {
- definition->fFiddle = name;
- }
- } else {
- definition->fFiddle = Definition::NormalizedName(name);
- }
- definition->fMarkType = markType;
- definition->fAnonymous = fAnonymous;
- this->setAsParent(definition);
- }
- } break;
- case MarkType::kTopic:
- case MarkType::kSubtopic:
- SkASSERT(1 == typeNameBuilder.size());
- if (!hasEnd) {
- if (!typeNameBuilder.size()) {
- return this->reportError<bool>("unnamed topic");
- }
- fTopics.emplace_front(markType, defStart, fLineCount, fParent, fMC);
- RootDefinition* rootDefinition = &fTopics.front();
- definition = rootDefinition;
- definition->fFileName = fFileName;
- definition->fContentStart = fChar;
- if (MarkType::kTopic == markType) {
- if (fParent) {
- return this->reportError<bool>("#Topic must be root");
- }
- // topic name is unappended
- definition->fName = typeNameBuilder[0];
- } else {
- if (!fParent) {
- return this->reportError<bool>("#Subtopic may not be root");
- }
- Definition* parent = fParent;
- while (MarkType::kTopic != parent->fMarkType && MarkType::kSubtopic != parent->fMarkType) {
- parent = parent->fParent;
- if (!parent) {
- // subtopic must have subtopic or topic in parent chain
- return this->reportError<bool>("#Subtopic missing parent");
- }
- }
- if (MarkType::kSubtopic == parent->fMarkType) {
- // subtopic prepends parent subtopic name, but not parent topic name
- definition->fName = parent->fName + '_';
- }
- definition->fName += typeNameBuilder[0];
- definition->fFiddle = parent->fFiddle + '_';
- }
- rootDefinition->fNames.fName = rootDefinition->fName;
- definition->fFiddle += Definition::NormalizedName(typeNameBuilder[0]);
- this->setAsParent(definition);
- }
- {
- SkASSERT(hasEnd ? fParent : definition);
- string fullTopic = hasEnd ? fParent->fFiddle : definition->fFiddle;
- Definition* defPtr = fTopicMap[fullTopic];
- if (hasEnd) {
- if (HasTag::kYes == hasTag && !this->checkEndMarker(markType, fullTopic)) {
- return false;
- }
- if (!definition) {
- definition = defPtr;
- } else if (definition != defPtr) {
- return this->reportError<bool>("mismatched topic");
- }
- } else {
- if (nullptr != defPtr) {
- return this->reportError<bool>("already declared topic");
- }
- fTopicMap[fullTopic] = definition;
- }
- }
- if (hasEnd) {
- if (!this->popParentStack(definition)) {
- return false;
- }
- }
- break;
- case MarkType::kFormula:
- // hasEnd : single line / multiple line
- if (!fParent || MarkType::kFormula != fParent->fMarkType) {
- SkASSERT(!definition || MarkType::kFormula == definition->fMarkType);
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
- definition = &fMarkup.front();
- definition->fContentStart = fChar;
- definition->fName = typeNameBuilder[0];
- definition->fFiddle = fParent->fFiddle;
- fParent = definition;
- } else {
- SkASSERT(fParent && MarkType::kFormula == fParent->fMarkType);
- SkASSERT(fMC == defStart[0]);
- SkASSERT(fMC == defStart[-1]);
- definition = fParent;
- definition->fTerminator = fChar;
- if (!this->popParentStack(definition)) {
- return false;
- }
- this->parseHashFormula(definition);
- fParent->fChildren.push_back(definition);
- }
- break;
- // these types are children of parents, but are not in named maps
- case MarkType::kDescription:
- case MarkType::kStdOut:
- // may be one-liner
- case MarkType::kAlias:
- case MarkType::kNoExample:
- case MarkType::kParam:
- case MarkType::kPhraseDef:
- case MarkType::kReturn:
- case MarkType::kToDo:
- if (hasEnd) {
- if (markType == fParent->fMarkType) {
- definition = fParent;
- if (MarkType::kBug == markType || MarkType::kReturn == markType
- || MarkType::kToDo == markType) {
- this->skipNoName();
- }
- if (!this->popParentStack(fParent)) { // if not one liner, pop
- return false;
- }
- if (MarkType::kParam == markType || MarkType::kReturn == markType
- || MarkType::kPhraseDef == markType) {
- if (!this->checkParamReturn(definition)) {
- return false;
- }
- }
- if (MarkType::kPhraseDef == markType) {
- string key = definition->fName;
- if (fPhraseMap.end() != fPhraseMap.find(key)) {
- this->reportError<bool>("duplicate phrase key");
- }
- fPhraseMap[key] = definition;
- }
- } else {
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
- definition = &fMarkup.front();
- definition->fName = typeNameBuilder[0];
- definition->fFiddle = fParent->fFiddle;
- definition->fContentStart = fChar;
- string endBracket;
- endBracket += fMC;
- endBracket += fMC;
- definition->fContentEnd = this->trimmedBracketEnd(endBracket);
- this->skipToEndBracket(endBracket.c_str());
- SkAssertResult(fMC == this->next());
- SkAssertResult(fMC == this->next());
- definition->fTerminator = fChar;
- TextParser checkForChildren(definition);
- if (checkForChildren.strnchr(fMC, definition->fContentEnd)) {
- this->reportError<bool>("put ## on separate line");
- }
- 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<bool>("duplicate alias");
- }
- fAliasMap[key] = definition;
- definition->fFiddle = definition->fParent->fFiddle;
- }
- break;
- } else if (MarkType::kPhraseDef == markType) {
- bool hasParams = '(' == this->next();
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
- definition = &fMarkup.front();
- definition->fName = typeNameBuilder[0];
- definition->fFiddle = fParent->fFiddle;
- definition->fContentStart = fChar;
- if (hasParams) {
- char lastChar;
- do {
- const char* subEnd = this->anyOf(",)\n");
- if (!subEnd || '\n' == *subEnd) {
- return this->reportError<bool>("unexpected phrase list end");
- }
- fMarkup.emplace_front(MarkType::kPhraseParam, fChar, fLineCount, fParent,
- fMC);
- Definition* phraseParam = &fMarkup.front();
- phraseParam->fContentStart = fChar;
- phraseParam->fContentEnd = subEnd;
- phraseParam->fName = string(fChar, subEnd - fChar);
- definition->fChildren.push_back(phraseParam);
- this->skipTo(subEnd);
- lastChar = this->next();
- phraseParam->fTerminator = fChar;
- } while (')' != lastChar);
- this->skipWhiteSpace();
- definition->fContentStart = fChar;
- }
- this->setAsParent(definition);
- break;
- }
- // not one-liners
- case MarkType::kCode:
- case MarkType::kExample:
- case MarkType::kFile:
- case MarkType::kFunction:
- case MarkType::kLegend:
- case MarkType::kList:
- case MarkType::kPrivate:
- case MarkType::kTable:
- if (hasEnd) {
- definition = fParent;
- if (markType != fParent->fMarkType) {
- return this->reportError<bool>("end element mismatch");
- } else if (!this->popParentStack(fParent)) {
- return false;
- }
- if (MarkType::kExample == markType) {
- if (definition->fChildren.size() == 0) {
- TextParser emptyCheck(definition);
- if (emptyCheck.eof() || !emptyCheck.skipWhiteSpace()) {
- return this->reportError<bool>("missing example body");
- }
- }
-// can't do this here; phrase refs may not have been defined yet
-// this->setWrapper(definition);
- }
- } else {
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
- definition = &fMarkup.front();
- definition->fContentStart = fChar;
- definition->fName = typeNameBuilder[0];
- definition->fFiddle = fParent->fFiddle;
- char suffix = '\0';
- bool tryAgain;
- do {
- tryAgain = false;
- for (const auto& child : fParent->fChildren) {
- if (child->fFiddle == definition->fFiddle) {
- if (MarkType::kExample != child->fMarkType) {
- continue;
- }
- if ('\0' == suffix) {
- suffix = 'a';
- } else if (++suffix > 'z') {
- return reportError<bool>("too many examples");
- }
- definition->fFiddle = fParent->fFiddle + '_';
- definition->fFiddle += suffix;
- tryAgain = true;
- break;
- }
- }
- } while (tryAgain);
- this->setAsParent(definition);
- }
- break;
- // always treated as one-liners (can't detect misuse easily)
- case MarkType::kAnchor:
- case MarkType::kBug:
- case MarkType::kDeprecated:
- case MarkType::kDetails:
- case MarkType::kDuration:
- case MarkType::kExperimental:
- case MarkType::kFilter:
- case MarkType::kHeight:
- case MarkType::kIllustration:
- case MarkType::kImage:
- case MarkType::kIn:
- case MarkType::kLine:
- case MarkType::kLiteral:
- case MarkType::kNoJustify:
- case MarkType::kOutdent:
- case MarkType::kPlatform:
- case MarkType::kPopulate:
- case MarkType::kSeeAlso:
- case MarkType::kSet:
- case MarkType::kSubstitute:
- case MarkType::kVolatile:
- case MarkType::kWidth:
- // todo : add check disallowing children?
- if (hasEnd && MarkType::kAnchor != markType && MarkType::kLine != markType) {
- return this->reportError<bool>("one liners omit end element");
- } else if (!hasEnd && MarkType::kAnchor == markType) {
- return this->reportError<bool>("anchor line must have end element last");
- }
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
- definition = &fMarkup.front();
- definition->fName = typeNameBuilder[0];
- definition->fFiddle = Definition::NormalizedName(typeNameBuilder[0]);
- definition->fContentStart = fChar;
- definition->fContentEnd = this->trimmedBracketEnd('\n');
- definition->fTerminator = this->lineEnd() - 1;
- fParent->fChildren.push_back(definition);
- if (MarkType::kAnchor == markType) {
- this->parseHashAnchor(definition);
- } else if (MarkType::kLine == markType) {
- this->parseHashLine(definition);
- } else if (IncompleteAllowed(markType)) {
- this->skipSpace();
- fParent->fDeprecated = true;
- fParent->fDetails =
- this->skipExact("soon") ? Definition::Details::kSoonToBe_Deprecated :
- this->skipExact("testing") ? Definition::Details::kTestingOnly_Experiment :
- this->skipExact("do not use") ? Definition::Details::kDoNotUse_Experiment :
- this->skipExact("not ready") ? Definition::Details::kNotReady_Experiment :
- Definition::Details::kNone;
- this->skipSpace();
- if ('\n' != this->peek()) {
- return this->reportError<bool>("unexpected text after #Deprecated");
- }
- }
- break;
- case MarkType::kExternal:
- (void) this->collectExternals(); // FIXME: detect errors in external defs?
- break;
- default:
- SkASSERT(0); // fixme : don't let any types be invisible
- return true;
- }
- if (fParent) {
- SkASSERT(definition);
- SkASSERT(definition->fName.length() > 0);
- }
- return true;
-}
-
-void BmhParser::reportDuplicates(const Definition& def, string dup) const {
- if (MarkType::kExample == def.fMarkType && dup == def.fFiddle) {
- TextParser reporter(&def);
- reporter.reportError("duplicate example name");
- }
- for (auto& child : def.fChildren ) {
- reportDuplicates(*child, dup);
- }
-}
-
-
-static Definition* find_fiddle(Definition* def, string name) {
- if (MarkType::kExample == def->fMarkType && name == def->fFiddle) {
- return def;
- }
- for (auto& child : def->fChildren) {
- Definition* result = find_fiddle(child, name);
- if (result) {
- return result;
- }
- }
- return nullptr;
-}
-
-Definition* BmhParser::findExample(string name) const {
- for (const auto& topic : fTopicMap) {
- if (topic.second->fParent) {
- continue;
- }
- Definition* def = find_fiddle(topic.second, name);
- if (def) {
- return def;
- }
- }
- return nullptr;
-}
-
-static bool check_example_hashes(Definition* def) {
- if (MarkType::kExample == def->fMarkType) {
- if (def->fHash.length()) {
- return true;
- }
- for (auto child : def->fChildren) {
- if (MarkType::kPlatform == child->fMarkType) {
- if (string::npos != string(child->fContentStart, child->length()).find("!fiddle")) {
- return true;
- }
- }
- }
- return def->reportError<bool>("missing hash");
- }
- for (auto& child : def->fChildren) {
- if (!check_example_hashes(child)) {
- return false;
- }
- }
- return true;
-}
-
-bool BmhParser::checkExampleHashes() const {
- for (const auto& topic : fTopicMap) {
- if (!topic.second->fParent && !check_example_hashes(topic.second)) {
- return false;
- }
- }
- return true;
-}
-
-static void reset_example_hashes(Definition* def) {
- if (MarkType::kExample == def->fMarkType) {
- def->fHash.clear();
- return;
- }
- for (auto& child : def->fChildren) {
- reset_example_hashes(child);
- }
-}
-
-void BmhParser::resetExampleHashes() {
- for (const auto& topic : fTopicMap) {
- if (!topic.second->fParent) {
- reset_example_hashes(topic.second);
- }
- }
-}
-
-static void find_examples(const Definition& def, vector<string>* exampleNames) {
- if (MarkType::kExample == def.fMarkType) {
- exampleNames->push_back(def.fFiddle);
- }
- for (auto& child : def.fChildren ) {
- find_examples(*child, exampleNames);
- }
-}
-
-bool BmhParser::checkEndMarker(MarkType markType, string match) const {
- TextParser tp(fFileName, fLine, fChar, fLineCount);
- tp.skipSpace();
- if (fMC != tp.next()) {
- return this->reportError<bool>("mismatched end marker expect #");
- }
- const char* nameStart = tp.fChar;
- tp.skipToNonName();
- string markName(nameStart, tp.fChar - nameStart);
- if (kMarkProps[(int) markType].fName != markName) {
- return this->reportError<bool>("expected #XXX ## to match");
- }
- tp.skipSpace();
- nameStart = tp.fChar;
- tp.skipToNonName();
- markName = string(nameStart, tp.fChar - nameStart);
- if ("" == markName) {
- if (fMC != tp.next() || fMC != tp.next()) {
- return this->reportError<bool>("expected ##");
- }
- return true;
- }
- std::replace(markName.begin(), markName.end(), '-', '_');
- auto defPos = match.rfind(markName);
- if (string::npos == defPos) {
- return this->reportError<bool>("mismatched end marker v1");
- }
- if (markName.size() != match.size() - defPos) {
- return this->reportError<bool>("mismatched end marker v2");
- }
- return true;
-}
-
-bool BmhParser::checkExamples() const {
- vector<string> exampleNames;
- for (const auto& topic : fTopicMap) {
- if (topic.second->fParent) {
- continue;
- }
- find_examples(*topic.second, &exampleNames);
- }
- std::sort(exampleNames.begin(), exampleNames.end());
- string* last = nullptr;
- string reported;
- bool checkOK = true;
- for (auto& nameIter : exampleNames) {
- if (last && *last == nameIter && reported != *last) {
- reported = *last;
- SkDebugf("%s\n", reported.c_str());
- for (const auto& topic : fTopicMap) {
- if (topic.second->fParent) {
- continue;
- }
- this->reportDuplicates(*topic.second, reported);
- }
- checkOK = false;
- }
- last = &nameIter;
- }
- return checkOK;
-}
-
-bool BmhParser::checkParamReturn(const Definition* definition) const {
- const char* parmEndCheck = definition->fContentEnd;
- while (parmEndCheck < definition->fTerminator) {
- if (fMC == parmEndCheck[0]) {
- break;
- }
- if (' ' < parmEndCheck[0]) {
- this->reportError<bool>(
- "use full end marker on multiline #Param and #Return");
- }
- ++parmEndCheck;
- }
- return true;
-}
-
-bool BmhParser::childOf(MarkType markType) const {
- auto childError = [this](MarkType markType) -> bool {
- string errStr = "expected ";
- errStr += kMarkProps[(int) markType].fName;
- errStr += " parent";
- return this->reportError<bool>(errStr.c_str());
- };
-
- if (markType == fParent->fMarkType) {
- return true;
- }
- if (this->hasEndToken()) {
- if (!fParent->fParent) {
- return this->reportError<bool>("expected grandparent");
- }
- if (markType == fParent->fParent->fMarkType) {
- return true;
- }
- }
- return childError(markType);
-}
-
-string BmhParser::className(MarkType markType) {
- const char* end = this->lineEnd();
- const char* mc = this->strnchr(fMC, end);
- string classID;
- TextParserSave savePlace(this);
- this->skipSpace();
- const char* wordStart = fChar;
- this->skipToNonName();
- const char* wordEnd = fChar;
- classID = string(wordStart, wordEnd - wordStart);
- if (!mc) {
- savePlace.restore();
- }
- string builder;
- const Definition* parent = this->parentSpace();
- if (parent && parent->fName != classID) {
- builder += parent->fName;
- }
- if (mc) {
- if (mc + 1 < fEnd && fMC == mc[1]) { // if ##
- if (markType != fParent->fMarkType) {
- return this->reportError<string>("unbalanced method");
- }
- if (builder.length() > 0 && classID.size() > 0) {
- if (builder != fParent->fName) {
- builder += "::";
- builder += classID;
- if (builder != fParent->fName) {
- return this->reportError<string>("name mismatch");
- }
- }
- }
- this->skipLine();
- return fParent->fName;
- }
- fChar = mc;
- this->next();
- }
- this->skipWhiteSpace();
- if (MarkType::kEnum == markType && fChar >= end) {
- fAnonymous = true;
- builder += "::_anonymous";
- return uniqueRootName(builder, markType);
- }
- builder = this->word(builder, "::");
- return builder;
-}
-
-bool BmhParser::collectExternals() {
- do {
- this->skipWhiteSpace();
- if (this->eof()) {
- break;
- }
- if (fMC == this->peek()) {
- this->next();
- if (this->eof()) {
- break;
- }
- if (fMC == this->peek()) {
- this->skipLine();
- break;
- }
- if (' ' >= this->peek()) {
- this->skipLine();
- continue;
- }
- if (this->startsWith(kMarkProps[(int) MarkType::kExternal].fName)) {
- this->skipToNonName();
- continue;
- }
- }
- this->skipToAlpha();
- const char* wordStart = fChar;
- this->skipToWhiteSpace();
- if (fChar - wordStart > 0) {
- fExternals.emplace_front(MarkType::kExternal, wordStart, fChar, fLineCount, fParent,
- fMC);
- RootDefinition* definition = &fExternals.front();
- definition->fFileName = fFileName;
- definition->fName = string(wordStart ,fChar - wordStart);
- definition->fFiddle = Definition::NormalizedName(definition->fName);
- }
- } while (!this->eof());
- return true;
-}
-
-bool BmhParser::dumpExamples(FILE* fiddleOut, Definition& def, bool* continuation) const {
- if (MarkType::kExample == def.fMarkType) {
- string result;
- if (!this->exampleToScript(&def, BmhParser::ExampleOptions::kAll, &result)) {
- return false;
- }
- if (result.length() > 0) {
- result += "\n";
- result += "}";
- if (*continuation) {
- fprintf(fiddleOut, ",\n");
- } else {
- *continuation = true;
- }
- fprintf(fiddleOut, "%s", result.c_str());
- }
- return true;
- }
- for (auto& child : def.fChildren ) {
- if (!this->dumpExamples(fiddleOut, *child, continuation)) {
- return false;
- }
- }
- return true;
-}
-
-bool BmhParser::dumpExamples(const char* fiddleJsonFileName) const {
- string oldFiddle(fiddleJsonFileName);
- string newFiddle(fiddleJsonFileName);
- newFiddle += "_new";
- FILE* fiddleOut = fopen(newFiddle.c_str(), "wb");
- if (!fiddleOut) {
- SkDebugf("could not open output file %s\n", newFiddle.c_str());
- return false;
- }
- fprintf(fiddleOut, "{\n");
- bool continuation = false;
- for (const auto& topic : fTopicMap) {
- if (topic.second->fParent) {
- continue;
- }
- this->dumpExamples(fiddleOut, *topic.second, &continuation);
- }
- fprintf(fiddleOut, "\n}\n");
- fclose(fiddleOut);
- if (ParserCommon::WrittenFileDiffers(oldFiddle, newFiddle)) {
- ParserCommon::CopyToFile(oldFiddle, newFiddle);
- SkDebugf("wrote %s\n", fiddleJsonFileName);
- } else {
- remove(newFiddle.c_str());
- }
- return true;
-}
-
-int BmhParser::endHashCount() const {
- const char* end = fLine + this->lineLength();
- int count = 0;
- while (fLine < end && fMC == *--end) {
- count++;
- }
- return count;
-}
-
-bool BmhParser::endTableColumn(const char* end, const char* terminator) {
- if (!this->popParentStack(fParent)) {
- return false;
- }
- fWorkingColumn->fContentEnd = end;
- fWorkingColumn->fTerminator = terminator;
- fColStart = fChar - 1;
- this->skipSpace();
- fTableState = TableState::kColumnStart;
- return true;
-}
-
-static size_t count_indent(string text, size_t test, size_t end) {
- size_t result = test;
- while (test < end) {
- if (' ' != text[test]) {
- break;
- }
- ++test;
- }
- return test - result;
-}
-
-static void add_code(string text, int pos, int end,
- size_t outIndent, size_t textIndent, string& example) {
- do {
- // fix this to move whole paragraph in, out, but preserve doc indent
- int nextIndent = count_indent(text, pos, end);
- size_t len = text.find('\n', pos);
- if (string::npos == len) {
- len = end;
- }
- if ((size_t) (pos + nextIndent) < len) {
- size_t indent = outIndent + nextIndent;
- SkASSERT(indent >= textIndent);
- indent -= textIndent;
- for (size_t index = 0; index < indent; ++index) {
- example += ' ';
- }
- pos += nextIndent;
- while ((size_t) pos < len) {
- example += '"' == text[pos] ? "\\\"" :
- '\\' == text[pos] ? "\\\\" :
- text.substr(pos, 1);
- ++pos;
- }
- example += "\\n";
- } else {
- pos += nextIndent;
- }
- if ('\n' == text[pos]) {
- ++pos;
- }
- } while (pos < end);
-}
-
-bool BmhParser::IsExemplary(const Definition* def) {
- return kMarkProps[(int) def->fMarkType].fExemplary != Exemplary::kNo;
-}
-
-bool BmhParser::exampleToScript(Definition* def, ExampleOptions exampleOptions,
- string* result) const {
- bool hasFiddle = true;
- const Definition* platform = def->hasChild(MarkType::kPlatform);
- if (platform) {
- TextParser platParse(platform);
- hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd);
- }
- if (!hasFiddle) {
- *result = "";
- return true;
- }
- string text = this->extractText(def, TrimExtract::kNo);
- bool textOut = string::npos != text.find("SkDebugf(")
- || string::npos != text.find("dump(")
- || string::npos != text.find("dumpHex(");
- string heightStr = "256";
- string widthStr = "256";
- string normalizedName(def->fFiddle);
- string code;
- string imageStr = "0";
- string srgbStr = "false";
- string durationStr = "0";
- for (auto iter : def->fChildren) {
- switch (iter->fMarkType) {
- case MarkType::kDuration:
- durationStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
- break;
- case MarkType::kHeight:
- heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
- break;
- case MarkType::kWidth:
- widthStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
- break;
- case MarkType::kDescription:
- // ignore for now
- break;
- case MarkType::kFunction: {
- // emit this, but don't wrap this in draw()
- string funcText = this->extractText(&*iter, TrimExtract::kNo);
- size_t pos = 0;
- while (pos < funcText.length() && ' ' > funcText[pos]) {
- ++pos;
- }
- size_t indent = count_indent(funcText, pos, funcText.length());
- add_code(funcText, pos, funcText.length(), 0, indent, code);
- code += "\\n";
- } break;
- case MarkType::kComment:
- break;
- case MarkType::kImage:
- imageStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
- break;
- case MarkType::kToDo:
- break;
- case MarkType::kBug:
- case MarkType::kMarkChar:
- case MarkType::kPlatform:
- case MarkType::kPhraseRef:
- // ignore for now
- break;
- case MarkType::kSet:
- if ("sRGB" == string(iter->fContentStart,
- iter->fContentEnd - iter->fContentStart)) {
- srgbStr = "true";
- } else {
- SkASSERT(0); // more work to do
- return false;
- }
- break;
- case MarkType::kStdOut:
- textOut = true;
- break;
- default:
- SkASSERT(0); // more coding to do
- }
- }
- string animatedStr = "0" != durationStr ? "true" : "false";
- string textOutStr = textOut ? "true" : "false";
- size_t pos = 0;
- while (pos < text.length() && ' ' > text[pos]) {
- ++pos;
- }
- size_t end = text.length();
- size_t outIndent = 0;
- size_t textIndent = count_indent(text, pos, end);
- if ("" == def->fWrapper) {
- this->setWrapper(def);
- }
- if (def->fWrapper.length() > 0) {
- code += def->fWrapper;
- code += "\\n";
- outIndent = 4;
- }
- add_code(text, pos, end, outIndent, textIndent, code);
- if (def->fWrapper.length() > 0) {
- code += "}";
- }
- string example = "\"" + normalizedName + "\": {\n";
- string filename = def->fileName();
- string baseFile = filename.substr(0, filename.length() - 4);
- if (ExampleOptions::kText == exampleOptions) {
- example += " \"code\": \"" + code + "\",\n";
- example += " \"hash\": \"" + def->fHash + "\",\n";
- example += " \"file\": \"" + baseFile + "\",\n";
- example += " \"name\": \"" + def->fName + "\",";
- } else {
- example += " \"code\": \"" + code + "\",\n";
- if (ExampleOptions::kPng == exampleOptions) {
- example += " \"width\": " + widthStr + ",\n";
- example += " \"height\": " + heightStr + ",\n";
- example += " \"hash\": \"" + def->fHash + "\",\n";
- example += " \"file\": \"" + baseFile + "\",\n";
- example += " \"name\": \"" + def->fName + "\"\n";
- example += "}";
- } else {
- example += " \"options\": {\n";
- example += " \"width\": " + widthStr + ",\n";
- example += " \"height\": " + heightStr + ",\n";
- example += " \"source\": " + imageStr + ",\n";
- example += " \"srgb\": " + srgbStr + ",\n";
- example += " \"f16\": false,\n";
- example += " \"textOnly\": " + textOutStr + ",\n";
- example += " \"animated\": " + animatedStr + ",\n";
- example += " \"duration\": " + durationStr + "\n";
- example += " },\n";
- example += " \"fast\": true";
- }
- }
- *result = example;
- return true;
-}
-
-string BmhParser::extractText(const Definition* def, TrimExtract trimExtract) const {
- string result;
- TextParser parser(def);
- auto childIter = def->fChildren.begin();
- while (!parser.eof()) {
- const char* end = def->fChildren.end() == childIter ? parser.fEnd : (*childIter)->fStart;
- string fragment(parser.fChar, end - parser.fChar);
- trim_end(fragment);
- if (TrimExtract::kYes == trimExtract) {
- trim_start(fragment);
- if (result.length()) {
- result += '\n';
- result += '\n';
- }
- }
- if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) {
- result += fragment;
- }
- parser.skipTo(end);
- if (def->fChildren.end() != childIter) {
- Definition* child = *childIter;
- if (MarkType::kPhraseRef == child->fMarkType) {
- auto phraseIter = fPhraseMap.find(child->fName);
- if (fPhraseMap.end() == phraseIter) {
- return def->reportError<string>("missing phrase definition");
- }
- Definition* phrase = phraseIter->second;
- // count indent of last line in result
- size_t lastLF = result.rfind('\n');
- size_t startPos = string::npos == lastLF ? 0 : lastLF;
- size_t lastLen = result.length() - startPos;
- size_t indent = count_indent(result, startPos, result.length()) + 4;
- string phraseStr = this->extractText(phrase, TrimExtract::kNo);
- startPos = 0;
- bool firstTime = true;
- size_t endPos;
- do {
- endPos = phraseStr.find('\n', startPos);
- size_t len = (string::npos != endPos ? endPos : phraseStr.length()) - startPos;
- if (firstTime && lastLen + len + 1 < 100) { // FIXME: make 100 global const or something
- result += ' ';
- } else {
- result += '\n';
- result += string(indent, ' ');
- }
- firstTime = false;
- string tmp = phraseStr.substr(startPos, len);
- result += tmp;
- startPos = endPos + 1;
- } while (string::npos != endPos);
- result += '\n';
- }
- parser.skipTo(child->fTerminator);
- std::advance(childIter, 1);
- }
- }
- 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* ) {";
- string text = this->extractText(def, TrimExtract::kNo);
- size_t nonSpace = 0;
- while (nonSpace < text.length() && ' ' >= text[nonSpace]) {
- ++nonSpace;
- }
- bool hasFunc = !text.compare(nonSpace, sizeof(drawWrapper) - 1, drawWrapper);
- bool noCanvas = !text.compare(nonSpace, sizeof(drawNoCanvas) - 1, drawNoCanvas);
- bool hasCanvas = string::npos != text.find("SkCanvas canvas");
- SkASSERT(!hasFunc || !noCanvas);
- bool preprocessor = text[0] == '#';
- bool wrapCode = !hasFunc && !noCanvas && !preprocessor;
- if (wrapCode) {
- def->fWrapper = hasCanvas ? string(drawNoCanvas) : string(drawWrapper);
- }
-}
-
-RootDefinition* BmhParser::findBmhObject(MarkType markType, string typeName) {
- const auto& mapIter = std::find_if(fMaps.begin(), fMaps.end(),
- [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
- if (mapIter == fMaps.end()) {
- return nullptr;
- }
- return &(*mapIter->fMap)[typeName];
-}
-
-// FIXME: some examples may produce different output on different platforms
-// if the text output can be different, think of how to author that
-
-bool BmhParser::findDefinitions() {
- bool lineStart = true;
- const char* lastChar = nullptr;
- const char* lastMC = nullptr;
- fParent = nullptr;
- while (!this->eof()) {
- if (this->peek() == fMC) {
- lastMC = fChar;
- this->next();
- if (this->peek() == fMC) {
- this->next();
- if (!lineStart && ' ' < this->peek()) {
- if (!fParent || MarkType::kFormula != fParent->fMarkType) {
- return this->reportError<bool>("expected definition");
- }
- }
- if (this->peek() != fMC) {
- if (MarkType::kColumn == fParent->fMarkType) {
- SkASSERT(TableState::kColumnEnd == fTableState);
- if (!this->endTableColumn(lastChar, lastMC)) {
- return false;
- }
- SkASSERT(fRow);
- if (!this->popParentStack(fParent)) {
- return false;
- }
- fRow->fContentEnd = fWorkingColumn->fContentEnd;
- fWorkingColumn = nullptr;
- fRow = nullptr;
- fTableState = TableState::kNone;
- } else {
- vector<string> parentName;
- parentName.push_back(fParent->fName);
- if (!this->addDefinition(fChar - 1, true, fParent->fMarkType, parentName,
- HasTag::kNo)) {
- return false;
- }
- }
- } else {
- SkAssertResult(this->next() == fMC);
- fMC = this->next(); // change markup character
- if (' ' >= fMC) {
- return this->reportError<bool>("illegal markup character");
- }
- fMarkup.emplace_front(MarkType::kMarkChar, fChar - 4, fLineCount, fParent, fMC);
- Definition* markChar = &fMarkup.front();
- markChar->fContentStart = fChar - 1;
- this->skipToEndBracket('\n');
- markChar->fContentEnd = fChar;
- markChar->fTerminator = fChar;
- fParent->fChildren.push_back(markChar);
- }
- } else if (this->peek() >= 'A' && this->peek() <= 'Z') {
- const char* defStart = fChar - 1;
- MarkType markType = this->getMarkType(MarkLookup::kRequire);
- bool hasEnd = this->hasEndToken();
- if (!hasEnd && fParent) {
- MarkType parentType = fParent->fMarkType;
- uint64_t parentMask = kMarkProps[(int) markType].fParentMask;
- if (parentMask && !(parentMask & (1LL << (int) parentType))) {
- return this->reportError<bool>("invalid parent");
- }
- }
- if (!this->skipName(kMarkProps[(int) markType].fName)) {
- return this->reportError<bool>("illegal markup character");
- }
- if (!this->skipSpace()) {
- return this->reportError<bool>("unexpected end");
- }
- lineStart = '\n' == this->peek();
- bool expectEnd = true;
- vector<string> typeNameBuilder = this->typeName(markType, &expectEnd);
- if (fCloned && MarkType::kMethod != markType && MarkType::kExample != markType
- && !fAnonymous) {
- return this->reportError<bool>("duplicate name");
- }
- if (hasEnd && expectEnd) {
- if (fMC == this->peek()) {
- return this->reportError<bool>("missing body");
- }
- }
- if (!this->addDefinition(defStart, hasEnd, markType, typeNameBuilder,
- HasTag::kYes)) {
- return false;
- }
- continue;
- } else if (this->peek() == ' ') {
- if (!fParent || (MarkType::kFormula != fParent->fMarkType
- && MarkType::kLegend != fParent->fMarkType
- && MarkType::kList != fParent->fMarkType
- && MarkType::kLine != fParent->fMarkType
- && MarkType::kTable != fParent->fMarkType)) {
- int endHashes = this->endHashCount();
- if (endHashes <= 1) {
- if (fParent) {
- if (TableState::kColumnEnd == fTableState) {
- if (!this->endTableColumn(lastChar, lastMC)) {
- return false;
- }
- } else { // one line comment
- fMarkup.emplace_front(MarkType::kComment, fChar - 1, fLineCount,
- fParent, fMC);
- Definition* comment = &fMarkup.front();
- comment->fContentStart = fChar - 1;
- this->skipToEndBracket('\n');
- comment->fContentEnd = fChar;
- comment->fTerminator = fChar;
- fParent->fChildren.push_back(comment);
- }
- } else {
- fChar = fLine + this->lineLength() - 1;
- }
- } else { // table row
- if (2 != endHashes) {
- string errorStr = "expect ";
- errorStr += fMC;
- errorStr += fMC;
- return this->reportError<bool>(errorStr.c_str());
- }
- if (!fParent || MarkType::kTable != fParent->fMarkType) {
- return this->reportError<bool>("missing table");
- }
- }
- } else if (TableState::kNone == fTableState) {
- // fixme? no nested tables for now
- fColStart = fChar - 1;
- fMarkup.emplace_front(MarkType::kRow, fColStart, fLineCount, fParent, fMC);
- fRow = &fMarkup.front();
- fRow->fName = fParent->fName;
- this->skipWhiteSpace();
- fRow->fContentStart = fChar;
- this->setAsParent(fRow);
- fTableState = TableState::kColumnStart;
- }
- if (TableState::kColumnStart == fTableState) {
- fMarkup.emplace_front(MarkType::kColumn, fColStart, fLineCount, fParent, fMC);
- fWorkingColumn = &fMarkup.front();
- fWorkingColumn->fName = fParent->fName;
- fWorkingColumn->fContentStart = fChar;
- this->setAsParent(fWorkingColumn);
- fTableState = TableState::kColumnEnd;
- continue;
- }
- } else if (this->peek() >= 'a' && this->peek() <= 'z') {
- // expect zero or more letters and underscores (no spaces) then hash
- const char* phraseNameStart = fChar;
- this->skipPhraseName();
- string phraseKey = string(phraseNameStart, fChar - phraseNameStart);
- char delimiter = this->next();
- vector<string> params;
- vector<const char*> paramsLoc;
- if (fMC != delimiter) {
- if ('(' != delimiter) {
- return this->reportError<bool>("expect # after phrase name");
- }
- // phrase may take comma delimited parameter list
- do {
- const char* subEnd = this->anyOf(",)\n");
- if (!subEnd || '\n' == *subEnd) {
- return this->reportError<bool>("unexpected phrase list end");
- }
- params.push_back(string(fChar, subEnd - fChar));
- paramsLoc.push_back(fChar);
- this->skipTo(subEnd);
-
- } while (')' != this->next());
- }
- const char* start = phraseNameStart;
- SkASSERT('#' == start[-1]);
- --start;
- if (start > fStart && ' ' >= start[-1]) {
- --start; // preserve whether to add whitespace before substitution
- }
- fMarkup.emplace_front(MarkType::kPhraseRef, start, fLineCount, fParent, fMC);
- Definition* markChar = &fMarkup.front();
- this->skipExact("#");
- markChar->fContentStart = fChar;
- markChar->fContentEnd = fChar;
- markChar->fTerminator = fChar;
- markChar->fName = phraseKey;
- fParent->fChildren.push_back(markChar);
- int paramLocIndex = 0;
- for (auto param : params) {
- const char* paramLoc = paramsLoc[paramLocIndex++];
- fMarkup.emplace_front(MarkType::kPhraseParam, paramLoc, fLineCount, fParent,
- fMC);
- Definition* phraseParam = &fMarkup.front();
- phraseParam->fContentStart = paramLoc;
- phraseParam->fContentEnd = paramLoc + param.length();
- phraseParam->fTerminator = paramLoc + param.length();
- phraseParam->fName = param;
- markChar->fChildren.push_back(phraseParam);
- }
- }
- }
- char nextChar = this->next();
- if (' ' < nextChar) {
- lastChar = fChar;
- lineStart = false;
- } else if (nextChar == '\n') {
- lineStart = true;
- }
- }
- if (fParent) {
- return fParent->reportError<bool>("mismatched end");
- }
- return true;
-}
-
-MarkType BmhParser::getMarkType(MarkLookup lookup) const {
- for (int index = 0; index <= Last_MarkType; ++index) {
- int typeLen = strlen(kMarkProps[index].fName);
- if (typeLen == 0) {
- continue;
- }
- if (fChar + typeLen >= fEnd || fChar[typeLen] > ' ') {
- continue;
- }
- int chCompare = strncmp(fChar, kMarkProps[index].fName, typeLen);
- if (chCompare < 0) {
- goto fail;
- }
- if (chCompare == 0) {
- return (MarkType) index;
- }
- }
-fail:
- if (MarkLookup::kRequire == lookup) {
- return this->reportError<MarkType>("unknown mark type");
- }
- return MarkType::kNone;
-}
-
- // 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;
- while (len > 0 && (isalnum(filename[len]) || '_' == filename[len] || '.' == filename[len])) {
- --len;
- }
- filename = filename.substr(len + 1);
- if (filename.substr(0, 2) != "Sk") {
- return true;
- }
- size_t under = filename.find('_');
- SkASSERT(under);
- string className = filename.substr(0, under);
- fOut = fopen(filename.c_str(), "wb");
- if (!fOut) {
- SkDebugf("could not open output file %s\n", filename.c_str());
- return false;
- }
- auto mapEntry = fBmhParser.fClassMap.find(className);
- if (fBmhParser.fClassMap.end() == mapEntry) {
- remove(filename.c_str());
- return true;
- }
- const Definition* classMarkup = &mapEntry->second;
- const Definition* root = classMarkup->fParent;
- SkASSERT(root);
- SkASSERT(root->fTerminator);
- SkASSERT('\n' == root->fTerminator[0]);
- SkASSERT(!root->fParent);
- fStart = root->fStart;
- fChar = fStart;
- 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());
- } else {
- remove(filename.c_str());
- }
- return true;
-}
-
-// returns true if topic has method
-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);
- }
- if (MarkType::kMethod != child->fMarkType) {
- continue;
- }
- 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;
- }
-}
-
-bool BmhParser::hasEndToken() const {
- const char* ptr = fLine;
- char test;
- do {
- if (ptr >= fEnd) {
- return false;
- }
- test = *ptr++;
- if ('\n' == test) {
- return false;
- }
- } while (fMC != test || fMC != *ptr);
- return true;
-}
-
-string BmhParser::memberName() {
- const char* wordStart;
- const char* prefixes[] = { "static", "const" };
- do {
- this->skipSpace();
- wordStart = fChar;
- this->skipToNonName();
- } while (this->anyOf(wordStart, prefixes, SK_ARRAY_COUNT(prefixes)));
- if ('*' == this->peek()) {
- this->next();
- }
- return this->className(MarkType::kMember);
-}
-
-string BmhParser::methodName() {
- if (this->hasEndToken()) {
- if (!fParent || !fParent->fName.length()) {
- return this->reportError<string>("missing parent method name");
- }
- SkASSERT(fMC == this->peek());
- this->next();
- SkASSERT(fMC == this->peek());
- this->next();
- SkASSERT(fMC != this->peek());
- return fParent->fName;
- }
- string builder;
- const char* end = this->lineEnd();
- const char* paren = this->strnchr('(', end);
- if (!paren) {
- return this->reportError<string>("missing method name and reference");
- }
- {
- TextParserSave endCheck(this);
- while (end < fEnd && !this->strnchr(')', end)) {
- fChar = end + 1;
- end = this->lineEnd();
- }
- if (end >= fEnd) {
- return this->reportError<string>("missing method end paren");
- }
- endCheck.restore();
- }
- const char* nameStart = paren;
- char ch;
- bool expectOperator = false;
- bool isConstructor = false;
- const char* nameEnd = nullptr;
- while (nameStart > fChar && ' ' != (ch = *--nameStart)) {
- if (!isalnum(ch) && '_' != ch) {
- if (nameEnd) {
- break;
- }
- expectOperator = true;
- continue;
- }
- if (!nameEnd) {
- nameEnd = nameStart + 1;
- }
- }
- if (!nameEnd) {
- return this->reportError<string>("unexpected method name char");
- }
- if (' ' == nameStart[0]) {
- ++nameStart;
- }
- if (nameEnd <= nameStart) {
- return this->reportError<string>("missing method name");
- }
- if (nameStart >= paren) {
- return this->reportError<string>("missing method name length");
- }
- string name(nameStart, nameEnd - nameStart);
- bool allLower = true;
- for (int index = 0; index < (int) (nameEnd - nameStart); ++index) {
- if (!islower(nameStart[index])) {
- allLower = false;
- break;
- }
- }
- if (expectOperator && "operator" != name) {
- return this->reportError<string>("expected operator");
- }
- const Definition* parent = this->parentSpace();
- if (parent && parent->fName.length() > 0) {
- size_t parentNameIndex = parent->fName.rfind(':');
- parentNameIndex = string::npos == parentNameIndex ? 0 : parentNameIndex + 1;
- string parentName = parent->fName.substr(parentNameIndex);
- if (parentName == name) {
- isConstructor = true;
- } else if ('~' == name[0]) {
- if (parentName != name.substr(1)) {
- return this->reportError<string>("expected destructor");
- }
- isConstructor = true;
- }
- builder = parent->fName + "::";
- }
- bool addConst = false;
- if (isConstructor || expectOperator) {
- paren = this->strnchr(')', end) + 1;
- TextParserSave saveState(this);
- this->skipTo(paren);
- if (this->skipExact("_const")) {
- addConst = true;
- }
- saveState.restore();
- }
- builder.append(nameStart, paren - nameStart);
- if (addConst) {
- builder.append("_const");
- }
- if (!expectOperator && allLower) {
- builder.append("()");
- }
- int parens = 0;
- while (fChar < end || parens > 0) {
- if ('(' == this->peek()) {
- ++parens;
- } else if (')' == this->peek()) {
- --parens;
- }
- this->next();
- }
- TextParserSave saveState(this);
- this->skipWhiteSpace();
- if (this->startsWith("const")) {
- this->skipName("const");
- } else {
- saveState.restore();
- }
-// this->next();
- if (string::npos != builder.find('\n')) {
- builder.erase(std::remove(builder.begin(), builder.end(), '\n'), builder.end());
- }
- return uniqueRootName(builder, MarkType::kMethod);
-}
-
-const Definition* BmhParser::parentSpace() const {
- Definition* parent = nullptr;
- Definition* test = fParent;
- while (test) {
- if (MarkType::kClass == test->fMarkType ||
- MarkType::kEnumClass == test->fMarkType ||
- MarkType::kStruct == test->fMarkType) {
- parent = test;
- break;
- }
- test = test->fParent;
- }
- 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) {
- --start;
- }
- SkASSERT (start < end);
- // if end is preceeeded by \n#MarkType ## backup to there
- TextParser parser(fFileName, start, fChar, fLineCount);
- parser.skipWhiteSpace();
- if (parser.eof() || fMC != parser.next()) {
- return end;
- }
- const char* markName = kMarkProps[(int) definition->fMarkType].fName;
- if (!parser.skipExact(markName)) {
- return end;
- }
- parser.skipWhiteSpace();
- 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();
- }
- }
- if (parser.eof() || fMC != parser.next()) {
- return end;
- }
- if (!parser.eof() && fMC != parser.next()) {
- return end;
- }
- SkASSERT(parser.eof());
- return start;
-}
-
-void BmhParser::parseHashAnchor(Definition* definition) {
- this->skipToEndBracket(fMC);
- fMarkup.emplace_front(MarkType::kLink, fChar, fLineCount, definition, fMC);
- SkAssertResult(fMC == this->next());
- this->skipWhiteSpace();
- Definition* link = &fMarkup.front();
- link->fContentStart = fChar;
- link->fContentEnd = this->trimmedBracketEnd(fMC);
- this->skipToEndBracket(fMC);
- SkAssertResult(fMC == this->next());
- SkAssertResult(fMC == this->next());
- link->fTerminator = fChar;
- definition->fContentEnd = link->fContentEnd;
- definition->fTerminator = fChar;
- definition->fChildren.emplace_back(link);
-}
-
-void BmhParser::parseHashFormula(Definition* definition) {
- const char* start = definition->fContentStart;
- definition->trimEnd();
- const char* end = definition->fContentEnd;
- fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition, fMC);
- Definition* text = &fMarkup.front();
- text->fContentStart = start;
- text->fContentEnd = end;
- text->fTerminator = definition->fTerminator;
- definition->fChildren.emplace_back(text);
-}
-
-void BmhParser::parseHashLine(Definition* definition) {
- const char* nextLF = this->strnchr('\n', this->fEnd);
- const char* start = fChar;
- const char* end = this->trimmedBracketEnd(fMC);
- this->skipToEndBracket(fMC, nextLF);
- if (fMC != this->next() || fMC != this->next()) {
- return this->reportError<void>("expected ## to delineate line");
- }
- fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition, fMC);
- Definition* text = &fMarkup.front();
- if (!islower(start[0]) && (!isdigit(start[0])
- || MarkType::kConst != definition->fParent->fMarkType)) {
- return this->reportError<void>("expect lower case start");
- }
- string contents = string(start, end - start);
- if (string::npos != contents.find('.')) {
- return this->reportError<void>("expect phrase, not sentence");
- }
- size_t firstSpace = contents.find(' ');
- if (string::npos == firstSpace || 0 == firstSpace || 's' != start[firstSpace - 1]) {
- if (MarkType::kMethod == fParent->fMarkType && "experimental" != contents
- && "incomplete" != contents) {
- return this->reportError<void>( "expect phrase in third person present"
- " tense (1st word should end in 's'");
- }
- }
- text->fContentStart = start;
- text->fContentEnd = end;
- text->fTerminator = fChar;
- definition->fContentEnd = text->fContentEnd;
- definition->fTerminator = fChar;
- definition->fChildren.emplace_back(text);
-}
-
-bool BmhParser::popParentStack(Definition* definition) {
- if (!fParent) {
- return this->reportError<bool>("missing parent");
- }
- if (definition != fParent) {
- return this->reportError<bool>("definition end is not parent");
- }
- if (!definition->fStart) {
- return this->reportError<bool>("definition missing start");
- }
- if (definition->fContentEnd) {
- return this->reportError<bool>("definition already ended");
- }
- // more to figure out to handle table columns, at minimum
- const char* end = fChar;
- if (fMC != end[0]) {
- while (end > definition->fContentStart && ' ' >= end[-1]) {
- --end;
- }
- SkASSERT(&end[-1] >= definition->fContentStart && fMC == end[-1]
- && (MarkType::kColumn == definition->fMarkType
- || (&end[-2] >= definition->fContentStart && fMC == end[-2])));
- end -= 2;
- }
- end = checkForFullTerminal(end, definition);
- definition->fContentEnd = end;
- definition->fTerminator = fChar;
- fParent = definition->fParent;
- if (!fParent || (MarkType::kTopic == fParent->fMarkType && !fParent->fParent)) {
- fRoot = nullptr;
- }
- return true;
-}
-
-TextParser::TextParser(const Definition* definition) :
- TextParser(definition->fFileName, definition->fContentStart, definition->fContentEnd,
- definition->fLineCount) {
-}
-
-string TextParser::ReportFilename(string file) {
- string fullName;
-#ifdef SK_BUILD_FOR_WIN
- TCHAR pathChars[MAX_PATH];
- DWORD pathLen = GetCurrentDirectory(MAX_PATH, pathChars);
- for (DWORD index = 0; index < pathLen; ++index) {
- fullName += pathChars[index] == (char)pathChars[index] ? (char)pathChars[index] : '?';
- }
- fullName += '\\';
-#endif
- fullName += file;
- return fullName;
-}
-
-void TextParser::reportError(const char* errorStr) const {
- this->reportWarning(errorStr);
- SkDebugf(""); // convenient place to set a breakpoint
-}
-
-void TextParser::reportWarning(const char* errorStr) const {
- const char* lineStart = fLine;
- if (lineStart >= fEnd) {
- lineStart = fChar;
- }
- SkASSERT(lineStart < fEnd);
- TextParser err(fFileName, lineStart, fEnd, fLineCount);
- size_t lineLen = this->lineLength();
- ptrdiff_t spaces = fChar - lineStart;
- while (spaces > 0 && (size_t) spaces > lineLen) {
- ++err.fLineCount;
- err.fLine += lineLen;
- spaces -= lineLen;
- lineLen = err.lineLength();
- }
- string fullName = this->ReportFilename(fFileName);
- SkDebugf("\n%s(%zd): error: %s\n", fullName.c_str(), err.fLineCount, errorStr);
- if (0 == lineLen) {
- SkDebugf("[blank line]\n");
- } else {
- while (lineLen > 0 && '\n' == err.fLine[lineLen - 1]) {
- --lineLen;
- }
- SkDebugf("%.*s\n", (int) lineLen, err.fLine);
- SkDebugf("%*s^\n", (int) spaces, "");
- }
-}
-
-void TextParser::setForErrorReporting(const Definition* definition, const char* str) {
- fFileName = definition->fFileName;
- fStart = definition->fContentStart;
- fLine = str;
- while (fLine > fStart && fLine[-1] != '\n') {
- --fLine;
- }
- fChar = str;
- fEnd = definition->fContentEnd;
- fLineCount = definition->fLineCount;
- const char* lineInc = fStart;
- while (lineInc < str) {
- fLineCount += '\n' == *lineInc++;
- }
-}
-
-string TextParser::typedefName() {
- // look for typedef as one of three forms:
- // typedef return-type (*NAME)(params);
- // typedef alias NAME;
- // typedef std::function<alias> NAME;
- string builder;
- const char* end = this->doubleLF();
- if (!end) {
- end = fEnd;
- }
- const char* altEnd = this->strnstr("#Typedef ##", end);
- if (altEnd) {
- end = this->strnchr('\n', end);
- }
- if (!end) {
- return this->reportError<string>("missing typedef std::function end bracket >");
- }
- bool stdFunction = this->startsWith("std::function");
- if (stdFunction) {
- if (!this->skipToEndBracket('>')) {
- return this->reportError<string>("missing typedef std::function end bracket >");
- }
- this->next();
- this->skipWhiteSpace();
- builder += string(fChar, end - fChar);
- } else {
- const char* paren = this->strnchr('(', end);
- if (!paren) {
- const char* lastWord = nullptr;
- do {
- this->skipToWhiteSpace();
- if (fChar < end && isspace(fChar[0])) {
- const char* whiteStart = fChar;
- this->skipWhiteSpace();
- // FIXME: test should be for fMC
- if ('#' == fChar[0]) {
- end = whiteStart;
- break;
- }
- lastWord = fChar;
- } else {
- break;
- }
- } while (true);
- if (!lastWord) {
- return this->reportError<string>("missing typedef name");
- }
- builder += string(lastWord, end - lastWord);
- } else {
- this->skipTo(paren);
- this->next();
- if ('*' != this->next()) {
- return this->reportError<string>("missing typedef function asterisk");
- }
- const char* nameStart = fChar;
- if (!this->skipToEndBracket(')')) {
- return this->reportError<string>("missing typedef function )");
- }
- builder += string(nameStart, fChar - nameStart);
- if (!this->skipToEndBracket('(')) {
- return this->reportError<string>("missing typedef params (");
- }
- if (! this->skipToEndBracket(')')) {
- return this->reportError<string>("missing typedef params )");
- }
- this->skipTo(end);
- }
- }
- return builder;
-}
-
-bool BmhParser::skipNoName() {
- if ('\n' == this->peek()) {
- this->next();
- return true;
- }
- this->skipWhiteSpace();
- if (fMC != this->peek()) {
- return this->reportError<bool>("expected end mark 1");
- }
- this->next();
- if (fMC != this->peek()) {
- return this->reportError<bool>("expected end mark 2");
- }
- this->next();
- return true;
-}
-
-bool BmhParser::skipToDefinitionEnd(MarkType markType) {
- if (this->eof()) {
- return this->reportError<bool>("missing end");
- }
- const char* start = fLine;
- int startLineCount = fLineCount;
- int stack = 1;
- ptrdiff_t lineLen;
- bool foundEnd = false;
- do {
- lineLen = this->lineLength();
- if (fMC != *fChar++) {
- continue;
- }
- if (fMC == *fChar) {
- continue;
- }
- if (' ' == *fChar) {
- continue;
- }
- MarkType nextType = this->getMarkType(MarkLookup::kAllowUnknown);
- if (markType != nextType) {
- continue;
- }
- bool hasEnd = this->hasEndToken();
- if (hasEnd) {
- if (!--stack) {
- foundEnd = true;
- continue;
- }
- } else {
- ++stack;
- }
- } while ((void) ++fLineCount, (void) (fLine += lineLen), (void) (fChar = fLine),
- !this->eof() && !foundEnd);
- if (foundEnd) {
- return true;
- }
- fLineCount = startLineCount;
- fLine = start;
- fChar = start;
- return this->reportError<bool>("unbalanced stack");
-}
-
-bool BmhParser::skipToString() {
- this->skipSpace();
- if (fMC != this->peek()) {
- return this->reportError<bool>("expected end mark 3");
- }
- this->next();
- this->skipSpace();
- // body is text from here to double fMC
- // no single fMC allowed, no linefeed allowed
- return true;
-}
-
-vector<string> BmhParser::topicName() {
- vector<string> result;
- this->skipWhiteSpace();
- const char* lineEnd = fLine + this->lineLength();
- const char* nameStart = fChar;
- while (fChar < lineEnd) {
- char ch = this->next();
- SkASSERT(',' != ch);
- if ('\n' == ch) {
- break;
- }
- if (fMC == ch) {
- break;
- }
- }
- if (fChar - 1 > nameStart) {
- string builder(nameStart, fChar - nameStart - 1);
- trim_start_end(builder);
- result.push_back(builder);
- }
- if (fChar < lineEnd && fMC == this->peek()) {
- this->next();
- }
- return result;
-}
-
-// typeName parsing rules depend on mark type
-vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
- fAnonymous = false;
- fCloned = false;
- vector<string> result;
- string builder;
- if (fParent) {
- builder = fParent->fName;
- }
- switch (markType) {
- case MarkType::kDefine:
- case MarkType::kEnum:
- // enums may be nameless
- case MarkType::kConst:
- case MarkType::kEnumClass:
- case MarkType::kClass:
- case MarkType::kStruct:
- // expect name
- builder = this->className(markType);
- break;
- case MarkType::kExample:
- // check to see if one already exists -- if so, number this one
- builder = this->uniqueName(string(), markType);
- this->skipNoName();
- break;
- case MarkType::kCode:
- case MarkType::kDescription:
- case MarkType::kExternal:
- case MarkType::kFunction:
- case MarkType::kLegend:
- case MarkType::kList:
- case MarkType::kNoExample:
- case MarkType::kPrivate:
- this->skipNoName();
- break;
- case MarkType::kFormula:
- case MarkType::kLine:
- this->skipToString();
- break;
- case MarkType::kAlias:
- case MarkType::kAnchor:
- case MarkType::kBug: // fixme: expect number
- case MarkType::kDeprecated:
- case MarkType::kDetails:
- case MarkType::kDuration:
- case MarkType::kExperimental:
- case MarkType::kFile:
- case MarkType::kFilter:
- case MarkType::kHeight:
- case MarkType::kIllustration:
- case MarkType::kImage:
- case MarkType::kIn:
- case MarkType::kLiteral:
- case MarkType::kNoJustify:
- case MarkType::kOutdent:
- case MarkType::kPlatform:
- case MarkType::kPopulate:
- case MarkType::kReturn:
- case MarkType::kSeeAlso:
- case MarkType::kSet:
- case MarkType::kSubstitute:
- case MarkType::kToDo:
- case MarkType::kVolatile:
- case MarkType::kWidth:
- *checkEnd = false; // no name, may have text body
- break;
- case MarkType::kStdOut:
- this->skipNoName();
- break; // unnamed
- case MarkType::kMember:
- builder = this->memberName();
- break;
- case MarkType::kMethod:
- builder = this->methodName();
- break;
- case MarkType::kTypedef:
- builder = this->typedefName();
- break;
- case MarkType::kParam:
- // fixme: expect camelCase for param
- builder = this->word("", "");
- this->skipSpace();
- *checkEnd = false;
- break;
- case MarkType::kPhraseDef: {
- const char* nameEnd = this->anyOf("(\n");
- builder = string(fChar, nameEnd - fChar);
- this->skipLower();
- if (fChar != nameEnd) {
- this->reportError("expect lower case only");
- break;
- }
- this->skipTo(nameEnd);
- *checkEnd = false;
- } break;
- case MarkType::kTable:
- this->skipNoName();
- break; // unnamed
- case MarkType::kSubtopic:
- case MarkType::kTopic:
- // fixme: start with cap, allow space, hyphen, stop on comma
- // one topic can have multiple type names delineated by comma
- result = this->topicName();
- if (result.size() == 0 && this->hasEndToken()) {
- break;
- }
- return result;
- default:
- // fixme: don't allow silent failures
- SkASSERT(0);
- }
- result.push_back(builder);
- return result;
-}
-
-string BmhParser::typedefName() {
- if (this->hasEndToken()) {
- if (!fParent || !fParent->fName.length()) {
- return this->reportError<string>("missing parent typedef name");
- }
- SkASSERT(fMC == this->peek());
- this->next();
- SkASSERT(fMC == this->peek());
- this->next();
- SkASSERT(fMC != this->peek());
- return fParent->fName;
- }
- string builder;
- const Definition* parent = this->parentSpace();
- if (parent && parent->fName.length() > 0) {
- builder = parent->fName + "::";
- }
- builder += TextParser::typedefName();
- return uniqueRootName(builder, MarkType::kTypedef);
-}
-
-string BmhParser::uniqueName(string base, MarkType markType) {
- string builder(base);
- if (!builder.length()) {
- builder = fParent->fName;
- }
- if (!fParent) {
- return builder;
- }
- int number = 2;
- string numBuilder(builder);
- do {
- for (auto& iter : fParent->fChildren) {
- if (markType == iter->fMarkType) {
- if (iter->fName == numBuilder) {
- if (iter->fDeprecated) {
- iter->fClone = true;
- } else {
- fCloned = true;
- }
- numBuilder = builder + '_' + to_string(number);
- goto tryNext;
- }
- }
- }
- break;
-tryNext: ;
- } while (++number);
- return numBuilder;
-}
-
-string BmhParser::uniqueRootName(string base, MarkType markType) {
- auto checkName = [markType](const Definition& def, string numBuilder) -> bool {
- return markType == def.fMarkType && def.fName == numBuilder;
- };
-
- string builder(base);
- if (!builder.length()) {
- builder = fParent->fName;
- }
- int number = 2;
- string numBuilder(builder);
- Definition* cloned = nullptr;
- do {
- if (fRoot) {
- for (auto& iter : fRoot->fBranches) {
- if (checkName(*iter.second, numBuilder)) {
- cloned = iter.second;
- goto tryNext;
- }
- }
- for (auto& iter : fRoot->fLeaves) {
- if (checkName(iter.second, numBuilder)) {
- cloned = &iter.second;
- goto tryNext;
- }
- }
- } else if (fParent) {
- for (auto& iter : fParent->fChildren) {
- if (checkName(*iter, numBuilder)) {
- cloned = &*iter;
- goto tryNext;
- }
- }
- }
- break;
-tryNext: ;
- if ("()" == builder.substr(builder.length() - 2)) {
- builder = builder.substr(0, builder.length() - 2);
- }
- if (MarkType::kMethod == markType) {
- cloned->fCloned = true;
- if (cloned->fDeprecated) {
- cloned->fClone = true;
- } else {
- fCloned = true;
- }
- } else {
- fCloned = true;
- }
- numBuilder = builder + '_' + to_string(number);
- } while (++number);
- return numBuilder;
-}
-
-void BmhParser::validate() const {
- for (int index = 0; index <= (int) Last_MarkType; ++index) {
- SkASSERT(kMarkProps[index].fMarkType == (MarkType) index);
- }
- const char* last = "";
- for (int index = 0; index <= (int) Last_MarkType; ++index) {
- const char* next = kMarkProps[index].fName;
- if (!last[0]) {
- last = next;
- continue;
- }
- if (!next[0]) {
- continue;
- }
- SkASSERT(strcmp(last, next) < 0);
- last = next;
- }
-}
-
-string BmhParser::word(string prefix, string delimiter) {
- string builder(prefix);
- this->skipWhiteSpace();
- const char* lineEnd = fLine + this->lineLength();
- const char* nameStart = fChar;
- while (fChar < lineEnd) {
- char ch = this->next();
- if (' ' >= ch) {
- break;
- }
- if (',' == ch) {
- return this->reportError<string>("no comma needed");
- break;
- }
- if (fMC == ch) {
- return builder;
- }
- if (!isalnum(ch) && '_' != ch && ':' != ch && '-' != ch) {
- return this->reportError<string>("unexpected char");
- }
- if (':' == ch) {
- // expect pair, and expect word to start with Sk
- if (nameStart[0] != 'S' || nameStart[1] != 'k') {
- return this->reportError<string>("expected Sk");
- }
- if (':' != this->peek()) {
- return this->reportError<string>("expected ::");
- }
- this->next();
- } else if ('-' == ch) {
- // expect word not to start with Sk or kX where X is A-Z
- if (nameStart[0] == 'k' && nameStart[1] >= 'A' && nameStart[1] <= 'Z') {
- return this->reportError<string>("didn't expected kX");
- }
- if (nameStart[0] == 'S' && nameStart[1] == 'k') {
- return this->reportError<string>("expected Sk");
- }
- }
- }
- if (prefix.size()) {
- builder += delimiter;
- }
- builder.append(nameStart, fChar - nameStart - 1);
- return builder;
-}
// pass one: parse text, collect definitions
// pass two: lookup references
@@ -2848,7 +259,7 @@ int main(int argc, char** const argv) {
mdOut.checkAnchors();
}
}
- if (runAll || (FLAGS_catalog && FLAGS_ref.isEmpty())) {
+ if (runAll || (FLAGS_catalog && !FLAGS_ref.isEmpty())) {
Catalog cparser(&bmhParser);
cparser.fDebugOut = FLAGS_stdout;
if (!FLAGS_bmh.isEmpty() && !cparser.openCatalog(FLAGS_bmh[0])) {
diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h
index 5a72315bf9..59083a75c4 100644
--- a/tools/bookmaker/bookmaker.h
+++ b/tools/bookmaker/bookmaker.h
@@ -8,43 +8,49 @@
#ifndef bookmaker_DEFINED
#define bookmaker_DEFINED
-#include "SkCommandLineFlags.h"
-#include "SkData.h"
-#include "SkJSONCPP.h"
-
#include <algorithm>
#include <cmath>
#include <cctype>
+#include <cstring>
#include <forward_list>
#include <list>
+#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
+#include "SkTypes.h"
+
+using std::forward_list;
+using std::list;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+class Definition;
+
+class NonAssignable {
+public:
+ NonAssignable(NonAssignable const&) = delete;
+ NonAssignable& operator=(NonAssignable const&) = delete;
+ NonAssignable() {}
+};
+
#define FPRINTF(...) \
if (fDebugOut) { \
SkDebugf(__VA_ARGS__); \
} \
fprintf(fOut, __VA_ARGS__)
-
// std::to_string isn't implemented on android
-#include <sstream>
-
template <typename T>
-std::string to_string(T value)
+string to_string(T value)
{
std::ostringstream os ;
os << value ;
return os.str() ;
}
-using std::forward_list;
-using std::list;
-using std::unordered_map;
-using std::string;
-using std::vector;
-
enum class KeyWord {
kNone,
kSK_API,
@@ -199,12 +205,6 @@ enum class KeyProperty {
kPreprocessor,
};
-enum class StatusFilter {
- kCompleted,
- kInProgress,
- kUnknown,
-};
-
struct IncludeKey {
const char* fName;
KeyWord fKeyWord;
@@ -213,937 +213,6 @@ struct IncludeKey {
extern const IncludeKey kKeyWords[];
-static inline bool has_nonwhitespace(string s) {
- bool nonwhite = false;
- for (const char& c : s) {
- if (' ' < c) {
- nonwhite = true;
- break;
- }
- }
- return nonwhite;
-}
-
-static inline void trim_end(string &s) {
- s.erase(std::find_if(s.rbegin(), s.rend(),
- std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
-}
-
-static inline void trim_end_spaces(string &s) {
- while (s.length() > 0 && ' ' == s.back()) {
- s.pop_back();
- }
-}
-
-static inline void trim_start(string &s) {
- s.erase(s.begin(), std::find_if(s.begin(), s.end(),
- std::not1(std::ptr_fun<int, int>(std::isspace))));
-}
-
-static inline void trim_start_end(string& s) {
- trim_start(s);
- trim_end(s);
-}
-
-class NonAssignable {
-public:
- NonAssignable(NonAssignable const&) = delete;
- NonAssignable& operator=(NonAssignable const&) = delete;
- NonAssignable() {}
-};
-
-class Definition;
-
-class TextParser : public NonAssignable {
- TextParser() {} // only for ParserCommon, TextParserSave
- friend class ParserCommon;
- friend class TextParserSave;
-public:
- virtual ~TextParser() {}
-
- TextParser(string fileName, const char* start, const char* end, int lineCount)
- : fFileName(fileName)
- , fStart(start)
- , fLine(start)
- , fChar(start)
- , fEnd(end)
- , fLineCount(lineCount)
- {
- }
-
- TextParser(const Definition* );
-
- const char* anyOf(const char* str) const {
- const char* ptr = fChar;
- while (ptr < fEnd) {
- if (strchr(str, ptr[0])) {
- return ptr;
- }
- ++ptr;
- }
- return nullptr;
- }
-
- const char* anyOf(const char* wordStart, const char* wordList[], size_t wordListCount) const {
- const char** wordPtr = wordList;
- const char** wordEnd = wordPtr + wordListCount;
- const size_t matchLen = fChar - wordStart;
- while (wordPtr < wordEnd) {
- const char* word = *wordPtr++;
- if (strlen(word) == matchLen && !strncmp(wordStart, word, matchLen)) {
- return word;
- }
- }
- 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;
- if (start <= fStart) {
- return '\0';
- }
- if (strncmp(start, pattern, len)) {
- return '\0';
- }
- 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) {
- *loc = result;
- }
- 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;
- const char* doubleStart = nullptr;
- while (++ptr < fEnd) {
- if (!doubleStart) {
- if ('\n' == ptr[0]) {
- doubleStart = ptr;
- }
- continue;
- }
- if ('\n' == ptr[0] || '#' == ptr[0]) {
- return doubleStart;
- }
- if (' ' < ptr[0]) {
- doubleStart = nullptr;
- }
- }
- return nullptr;
- }
-
- bool endsWith(const char* match) {
- int matchLen = strlen(match);
- if (matchLen > fChar - fLine) {
- return false;
- }
- return !strncmp(fChar - matchLen, match, matchLen);
- }
-
- bool eof() const { return fChar >= fEnd; }
-
- const char* lineEnd() const {
- const char* ptr = fChar;
- do {
- if (ptr >= fEnd) {
- return ptr;
- }
- char test = *ptr++;
- if (test == '\n' || test == '\0') {
- break;
- }
- } while (true);
- return ptr;
- }
-
- ptrdiff_t lineLength() const {
- return this->lineEnd() - fLine;
- }
-
- bool match(TextParser* );
-
- char next() {
- SkASSERT(fChar < fEnd);
- char result = *fChar++;
- if ('\n' == result) {
- ++fLineCount;
- fLine = fChar;
- }
- return result;
- }
-
- char peek() const { SkASSERT(fChar < fEnd); return *fChar; }
-
- void restorePlace(const TextParser& save) {
- fChar = save.fChar;
- fLine = save.fLine;
- fLineCount = save.fLineCount;
- }
-
- void savePlace(TextParser* save) {
- save->fChar = fChar;
- save->fLine = fLine;
- save->fLineCount = fLineCount;
- }
-
- void reportError(const char* errorStr) const;
- static string ReportFilename(string file);
- void reportWarning(const char* errorStr) const;
-
- template <typename T> T reportError(const char* errorStr) const {
- this->reportError(errorStr);
- return T();
- }
-
- bool sentenceEnd(const char* check) const {
- while (check > fStart) {
- --check;
- if (' ' < check[0] && '.' != check[0]) {
- return false;
- }
- if ('.' == check[0]) {
- return ' ' >= check[1];
- }
- if ('\n' == check[0] && '\n' == check[1]) {
- return true;
- }
- }
- return true;
- }
-
- void setForErrorReporting(const Definition* , const char* );
-
- bool skipToBalancedEndBracket(char startB, char endB) {
- SkASSERT(fChar < fEnd);
- SkASSERT(startB == fChar[0]);
- int startCount = 0;
- do {
- char test = this->next();
- startCount += startB == test;
- startCount -= endB == test;
- } while (startCount && fChar < fEnd);
- return !startCount;
- }
-
- bool skipToEndBracket(char endBracket, const char* end = nullptr) {
- if (nullptr == end) {
- end = fEnd;
- }
- while (fChar[0] != endBracket) {
- if (fChar >= end) {
- return false;
- }
- (void) this->next();
- }
- return true;
- }
-
- bool skipToEndBracket(const char* endBracket) {
- size_t len = strlen(endBracket);
- while (strncmp(fChar, endBracket, len)) {
- if (fChar >= fEnd) {
- return false;
- }
- (void) this->next();
- }
- return true;
- }
-
- bool skipLine() {
- return skipToEndBracket('\n');
- }
-
- void skipTo(const char* skip) {
- while (fChar < skip) {
- this->next();
- }
- }
-
- void skipToAlpha() {
- while (fChar < fEnd && !isalpha(fChar[0])) {
- fChar++;
- }
- }
-
- // returns true if saw close brace
- bool skipToAlphaNum() {
- bool sawCloseBrace = false;
- while (fChar < fEnd && !isalnum(fChar[0])) {
- sawCloseBrace |= '}' == *fChar++;
- }
- return sawCloseBrace;
- }
-
- bool skipExact(const char* pattern) {
- if (!this->startsWith(pattern)) {
- return false;
- }
- this->skipName(pattern);
- return true;
- }
-
- // 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() {
- 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++;
- }
- 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) {
- int match = 0;
- while (!this->eof() && '\n' != fChar[0]) {
- match += open == this->peek();
- match -= close == this->next();
- }
- return match;
- }
-
- bool skipToLineStart() {
- if (!this->skipLine()) {
- return false;
- }
- if (!this->eof()) {
- return this->skipWhiteSpace();
- }
- return true;
- }
-
- void skipToLineStart(int* indent, bool* sawReturn) {
- SkAssertResult(this->skipLine());
- this->skipWhiteSpace(indent, sawReturn);
- }
-
- void skipLower() {
- while (fChar < fEnd && (islower(fChar[0]) || '_' == fChar[0])) {
- fChar++;
- }
- }
-
- void skipToNonAlphaNum() {
- while (fChar < fEnd && (isalnum(fChar[0]) || '_' == fChar[0])) {
- fChar++;
- }
- }
-
- void skipToNonName() {
- while (fChar < fEnd && (isalnum(fChar[0])
- || '_' == fChar[0] || '-' == fChar[0]
- || (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1])
- || ('.' == fChar[0] && fChar + 1 < fEnd && isalpha(fChar[1])))) {
- if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
- fChar++;
- }
- fChar++;
- }
- }
-
- void skipPhraseName() {
- while (fChar < fEnd && (islower(fChar[0]) || '_' == fChar[0])) {
- fChar++;
- }
- }
-
- void skipToSpace() {
- while (fChar < fEnd && ' ' != fChar[0]) {
- fChar++;
- }
- }
-
- void skipToWhiteSpace() {
- while (fChar < fEnd && ' ' < fChar[0]) {
- fChar++;
- }
- }
-
- bool skipName(const char* word) {
- size_t len = strlen(word);
- if (len <= (size_t) (fEnd - fChar) && !strncmp(word, fChar, len)) {
- for (size_t i = 0; i < len; ++i) {
- this->next();
- }
- }
- return this->eof() || ' ' >= fChar[0];
- }
-
- bool skipSpace() {
- while (' ' == this->peek()) {
- (void) this->next();
- if (fChar >= fEnd) {
- return false;
- }
- }
- return true;
- }
-
- bool skipWord(const char* word) {
- if (!this->skipWhiteSpace()) {
- return false;
- }
- const char* save = fChar;
- if (!this->skipName(word)) {
- fChar = save;
- return false;
- }
- if (!this->skipWhiteSpace()) {
- return false;
- }
- return true;
- }
-
- bool skipWhiteSpace() {
- while (' ' >= this->peek()) {
- (void) this->next();
- if (fChar >= fEnd) {
- return false;
- }
- }
- return true;
- }
-
- void skipWhiteSpace(int* indent, bool* skippedReturn) {
- while (' ' >= this->peek()) {
- *indent = *skippedReturn ? *indent + 1 : 1;
- if ('\n' == this->peek()) {
- *skippedReturn |= true;
- *indent = 0;
- }
- (void) this->next();
- SkASSERT(fChar < fEnd);
- }
- }
-
- bool startsWith(const char* str) const {
- size_t len = strlen(str);
- ptrdiff_t lineLen = fEnd - fChar;
- return len <= (size_t) lineLen && 0 == strncmp(str, fChar, len);
- }
-
- // ignores minor white space differences
- bool startsWith(const char* str, size_t oLen) const {
- size_t tIndex = 0;
- size_t tLen = fEnd - fChar;
- size_t oIndex = 0;
- while (oIndex < oLen && tIndex < tLen) {
- bool tSpace = ' ' >= fChar[tIndex];
- bool oSpace = ' ' >= str[oIndex];
- if (tSpace != oSpace) {
- break;
- }
- if (tSpace) {
- do {
- ++tIndex;
- } while (tIndex < tLen && ' ' >= fChar[tIndex]);
- do {
- ++oIndex;
- } while (oIndex < oLen && ' ' >= str[oIndex]);
- continue;
- }
- if (fChar[tIndex] != str[oIndex]) {
- break;
- }
- ++tIndex;
- ++oIndex;
- }
- return oIndex >= oLen;
- }
-
- const char* strnchr(char ch, const char* end) const {
- const char* ptr = fChar;
- while (ptr < end) {
- if (ptr[0] == ch) {
- return ptr;
- }
- ++ptr;
- }
- return nullptr;
- }
-
- const char* strnstr(const char *match, const char* end) const {
- size_t matchLen = strlen(match);
- SkASSERT(matchLen > 0);
- ptrdiff_t len = end - fChar;
- SkASSERT(len >= 0);
- if ((size_t) len < matchLen ) {
- return nullptr;
- }
- size_t count = len - matchLen;
- for (size_t index = 0; index <= count; index++) {
- if (0 == strncmp(&fChar[index], match, matchLen)) {
- return &fChar[index];
- }
- }
- return nullptr;
- }
-
- const char* trimmedBracketEnd(const char bracket) const {
- int max = (int) (this->lineLength());
- int index = 0;
- while (index < max && bracket != fChar[index]) {
- ++index;
- }
- SkASSERT(index < max);
- while (index > 0 && ' ' >= fChar[index - 1]) {
- --index;
- }
- return fChar + index;
- }
-
- const char* trimmedBracketEnd(string bracket) const {
- size_t max = (size_t) (this->lineLength());
- string line(fChar, max);
- size_t index = line.find(bracket);
- SkASSERT(index < max);
- while (index > 0 && ' ' >= fChar[index - 1]) {
- --index;
- }
- return fChar + index;
- }
-
- const char* trimmedBracketNoEnd(const char bracket) const {
- int max = (int) (fEnd - fChar);
- int index = 0;
- while (index < max && bracket != fChar[index]) {
- ++index;
- }
- SkASSERT(index < max);
- while (index > 0 && ' ' >= fChar[index - 1]) {
- --index;
- }
- return fChar + index;
- }
-
- const char* trimmedLineEnd() const {
- const char* result = this->lineEnd();
- while (result > fChar && ' ' >= result[-1]) {
- --result;
- }
- return result;
- }
-
- void trimEnd() {
- while (fEnd > fStart && ' ' >= fEnd[-1]) {
- --fEnd;
- }
- }
-
- // FIXME: nothing else in TextParser knows from C++ --
- // there could be a class between TextParser and ParserCommon
- virtual string typedefName();
-
- const char* wordEnd() const {
- const char* end = fChar;
- while (isalnum(end[0]) || '_' == end[0] || '-' == end[0]) {
- ++end;
- }
- return end;
- }
-
- string fFileName;
- const char* fStart;
- const char* fLine;
- const char* fChar;
- const char* fEnd;
- size_t fLineCount;
-};
-
-class TextParserSave {
-public:
- TextParserSave(TextParser* parser) {
- fParser = parser;
- fSave.fFileName = parser->fFileName;
- fSave.fStart = parser->fStart;
- fSave.fLine = parser->fLine;
- fSave.fChar = parser->fChar;
- fSave.fEnd = parser->fEnd;
- fSave.fLineCount = parser->fLineCount;
- }
-
- void restore() const {
- fParser->fFileName = fSave.fFileName;
- fParser->fStart = fSave.fStart;
- fParser->fLine = fSave.fLine;
- fParser->fChar = fSave.fChar;
- fParser->fEnd = fSave.fEnd;
- fParser->fLineCount = fSave.fLineCount;
- }
-
-private:
- TextParser* fParser;
- TextParser fSave;
-};
-
-
-class EscapeParser : public TextParser {
-public:
- EscapeParser(const char* start, const char* end) :
- TextParser("", start, end, 0) {
- const char* reader = fStart;
- fStorage = new char[end - start];
- char* writer = fStorage;
- while (reader < fEnd) {
- char ch = *reader++;
- if (ch != '\\') {
- *writer++ = ch;
- } else {
- char ctrl = *reader++;
- if (ctrl == 'u') {
- unsigned unicode = 0;
- for (int i = 0; i < 4; ++i) {
- unicode <<= 4;
- SkASSERT((reader[0] >= '0' && reader[0] <= '9') ||
- (reader[0] >= 'A' && reader[0] <= 'F') ||
- (reader[0] >= 'a' && reader[0] <= 'f'));
- int nibble = *reader++ - '0';
- if (nibble > 9) {
- nibble = (nibble & ~('a' - 'A')) - 'A' + '9' + 1;
- }
- unicode |= nibble;
- }
- SkASSERT(unicode < 256);
- *writer++ = (unsigned char) unicode;
- } else {
- SkASSERT(ctrl == 'n');
- *writer++ = '\n';
- }
- }
- }
- fStart = fLine = fChar = fStorage;
- fEnd = writer;
- }
-
- ~EscapeParser() override {
- delete fStorage;
- }
-private:
- char* fStorage;
-};
-
-class RootDefinition;
-
-class Definition : public NonAssignable {
-public:
- enum Type {
- kNone,
- kWord,
- kMark,
- kKeyWord,
- kBracket,
- kPunctuation,
- kFileType,
- };
-
- enum class MethodType {
- kNone,
- kConstructor,
- kDestructor,
- kOperator,
- };
-
- enum class Operator {
- kUnknown,
- kAdd,
- kAddTo,
- kArray,
- kCast,
- kCopy,
- kDelete,
- kDereference,
- kEqual,
- kMinus,
- kMove,
- kMultiply,
- kMultiplyBy,
- kNew,
- kNotEqual,
- kSubtract,
- kSubtractFrom,
- };
-
- enum class Format {
- kIncludeReturn,
- kOmitReturn,
- };
-
- enum class Details {
- kNone,
- kSoonToBe_Deprecated,
- kTestingOnly_Experiment,
- kDoNotUse_Experiment,
- kNotReady_Experiment,
- };
-
- enum class DetailsType {
- kPhrase,
- kSentence,
- };
-
- Definition() {}
-
- Definition(const char* start, const char* end, int line, Definition* parent, char mc)
- : fStart(start)
- , fContentStart(start)
- , fContentEnd(end)
- , fParent(parent)
- , fLineCount(line)
- , fType(Type::kWord)
- , fMC(mc) {
- if (parent) {
- SkASSERT(parent->fFileName.length() > 0);
- fFileName = parent->fFileName;
- }
- this->setParentIndex();
- }
-
- Definition(MarkType markType, const char* start, int line, Definition* parent, char mc)
- : Definition(markType, start, nullptr, line, parent, mc) {
- }
-
- Definition(MarkType markType, const char* start, const char* end, int line, Definition* parent, char mc)
- : Definition(start, end, line, parent, mc) {
- fMarkType = markType;
- fType = Type::kMark;
- }
-
- Definition(Bracket bracket, const char* start, int lineCount, Definition* parent, char mc)
- : Definition(start, nullptr, lineCount, parent, mc) {
- fBracket = bracket;
- fType = Type::kBracket;
- }
-
- Definition(KeyWord keyWord, const char* start, const char* end, int lineCount,
- Definition* parent, char mc)
- : Definition(start, end, lineCount, parent, mc) {
- fKeyWord = keyWord;
- fType = Type::kKeyWord;
- }
-
- Definition(Punctuation punctuation, const char* start, int lineCount, Definition* parent, char mc)
- : Definition(start, nullptr, lineCount, parent, mc) {
- fPunctuation = punctuation;
- fType = Type::kPunctuation;
- }
-
- virtual ~Definition() {}
-
- virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; }
- bool boilerplateIfDef();
-
- bool boilerplateEndIf() {
- return true;
- }
-
- bool checkMethod() const;
- bool crossCheck2(const Definition& includeToken) const;
- bool crossCheck(const Definition& includeToken) const;
- bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const;
-
- Definition* csParent() {
- Definition* test = fParent;
- while (test) {
- if (MarkType::kStruct == test->fMarkType || MarkType::kClass == test->fMarkType) {
- return test;
- }
- test = test->fParent;
- }
- return nullptr;
- }
-
- string fiddleName() const;
- string fileName() const;
- const Definition* findClone(string match) const;
- string formatFunction(Format format) const;
- const Definition* hasChild(MarkType markType) const;
- bool hasMatch(string name) const;
- Definition* hasParam(string ref);
- string incompleteMessage(DetailsType ) const;
- bool isClone() const { return fClone; }
-
- const Definition* iRootParent() const {
- const Definition* test = fParent;
- while (test) {
- if (KeyWord::kClass == test->fKeyWord || KeyWord::kStruct == test->fKeyWord) {
- return test;
- }
- test = test->fParent;
- }
- return nullptr;
- }
-
- virtual bool isRoot() const { return false; }
- bool isStructOrClass() const;
-
- int length() const {
- return (int) (fContentEnd - fContentStart);
- }
-
- const char* methodEnd() const;
- bool methodHasReturn(string name, TextParser* methodParser) const;
- string methodName() const;
- bool nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
- string* paramName) const;
- static string NormalizedName(string name);
- bool paramsMatch(string fullRef, string name) const;
- bool parseOperator(size_t doubleColons, string& result);
-
- string printableName() const {
- string result(fName);
- std::replace(result.begin(), result.end(), '_', ' ');
- return result;
- }
-
- template <typename T> T reportError(const char* errorStr) const {
- TextParser tp(this);
- tp.reportError(errorStr);
- return T();
- }
-
- virtual RootDefinition* rootParent() { SkASSERT(0); return nullptr; }
- virtual const RootDefinition* rootParent() const { SkASSERT(0); return nullptr; }
- void setCanonicalFiddle();
-
- void setParentIndex() {
- fParentIndex = fParent ? (int) fParent->fTokens.size() : -1;
- }
-
- string simpleName() {
- size_t doubleColon = fName.rfind("::");
- return string::npos == doubleColon ? fName : fName.substr(doubleColon + 2);
- }
-
- const Definition* subtopicParent() const {
- Definition* test = fParent;
- while (test) {
- if (MarkType::kTopic == test->fMarkType || MarkType::kSubtopic == test->fMarkType) {
- return test;
- }
- test = test->fParent;
- }
- return nullptr;
- }
-
- const Definition* topicParent() const {
- Definition* test = fParent;
- while (test) {
- if (MarkType::kTopic == test->fMarkType) {
- return test;
- }
- test = test->fParent;
- }
- return nullptr;
- }
-
- void trimEnd();
-
- string fText; // if text is constructed instead of in a file, it's put here
- const char* fStart = nullptr; // .. in original text file, or the start of fText
- const char* fContentStart; // start past optional markup name
- string fName;
- string fFiddle; // if its a constructor or operator, fiddle name goes here
- string fCode; // suitable for autogeneration of #Code blocks in bmh
- const char* fContentEnd = nullptr; // the end of the contained text
- const char* fTerminator = nullptr; // the end of the markup, normally ##\n or \n
- Definition* fParent = nullptr;
- list<Definition> fTokens;
- vector<Definition*> fChildren;
- string fHash; // generated by fiddle
- string fFileName;
- mutable string fWrapper; // used by Example to wrap into proper function
- size_t fLineCount = 0;
- int fParentIndex = 0;
- MarkType fMarkType = MarkType::kNone;
- KeyWord fKeyWord = KeyWord::kNone;
- Bracket fBracket = Bracket::kNone;
- Punctuation fPunctuation = Punctuation::kNone;
- MethodType fMethodType = MethodType::kNone;
- Operator fOperator = Operator::kUnknown;
- Type fType = Type::kNone;
- char fMC = '#';
- bool fClone = false;
- bool fCloned = false;
- bool fDeprecated = false;
- bool fOperatorConst = false;
- bool fPrivate = false;
- Details fDetails = Details::kNone;
- bool fMemberStart = false;
- bool fAnonymous = false;
- mutable bool fVisited = false;
-};
-
-class SubtopicKeys {
-public:
- static constexpr const char* kClasses = "Classes";
- static constexpr const char* kConstants = "Constants";
- static constexpr const char* kConstructors = "Constructors";
- static constexpr const char* kDefines = "Defines";
- static constexpr const char* kMemberFunctions = "Member_Functions";
- static constexpr const char* kMembers = "Members";
- static constexpr const char* kOperators = "Operators";
- static constexpr const char* kOverview = "Overview";
- static constexpr const char* kRelatedFunctions = "Related_Functions";
- static constexpr const char* kStructs = "Structs";
- static constexpr const char* kTypedefs = "Typedefs";
-
- static const char* kGeneratedSubtopics[];
-};
-
struct NameMap {
void copyToParent(NameMap* parent) const;
@@ -1154,1568 +223,16 @@ struct NameMap {
unordered_map<string, Definition*> fRefMap; // e.g., from #Substitute entry to #Topic entry
};
-class RootDefinition : public Definition {
-public:
- enum class AllowParens {
- kNo,
- kYes,
- };
-
- struct SubtopicContents {
- SubtopicContents()
- : fShowClones(false) {
- }
-
- vector<Definition*> fMembers;
- bool fShowClones;
- };
-
- RootDefinition() {
- }
-
- 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) {
- }
-
- ~RootDefinition() override {
- for (auto& iter : fBranches) {
- delete iter.second;
- }
- }
-
- RootDefinition* asRoot() override { return this; }
- void clearVisited();
- bool dumpUnVisited();
- Definition* find(string ref, AllowParens );
- bool isRoot() const override { return true; }
-
- SubtopicContents& populator(const char* key) {
- return fPopulators[key];
- }
-
- RootDefinition* rootParent() override { return fRootParent; }
- const RootDefinition* rootParent() const override { return fRootParent; }
- void setRootParent(RootDefinition* rootParent) { fRootParent = rootParent; }
-
- unordered_map<string, RootDefinition*> fBranches;
- unordered_map<string, Definition> fLeaves;
- unordered_map<string, SubtopicContents> fPopulators;
- NameMap fNames;
-private:
- RootDefinition* fRootParent = nullptr;
-};
-
-struct IClassDefinition : public Definition {
- unordered_map<string, Definition*> fConsts;
- unordered_map<string, Definition*> fDefines;
- unordered_map<string, Definition*> fEnums;
- unordered_map<string, Definition*> fMembers;
- unordered_map<string, Definition*> fMethods;
- unordered_map<string, Definition*> fStructs;
- unordered_map<string, Definition*> fTypedefs;
-};
-
-struct Reference {
- Reference()
- : fLocation(nullptr)
- , fDefinition(nullptr) {
- }
-
- const char* fLocation; // .. in original text file
- const Definition* fDefinition;
-};
-
-struct TypeNames {
- const char* fName;
- MarkType fMarkType;
+enum class Resolvable {
+ kNo, // neither resolved nor output
+ kYes, // resolved, output
+ kOut, // mostly resolved, output (FIXME: is this really different from kYes?)
+ kCode, // resolve methods as they are used, not as they are prototyped
+ kFormula, // kCode, plus make most spaces non-breaking
+ 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
};
-class ParserCommon : public TextParser {
-public:
- enum class OneFile {
- kNo,
- kYes,
- };
-
- enum class OneLine {
- kNo,
- kYes,
- };
-
- enum class IndentKind {
- kConstOut,
- kEnumChild,
- kEnumChild2,
- kEnumHeader,
- kEnumHeader2,
- kMethodOut,
- kStructMember,
- };
-
- struct IndentState {
- IndentState(IndentKind kind, int indent)
- : fKind(kind)
- , fIndent(indent) {
- }
-
- IndentKind fKind;
- int fIndent;
- };
-
- ParserCommon() : TextParser()
- , fParent(nullptr)
- , fDebugOut(false)
- , fValidate(false)
- , fReturnOnWrite(false)
- {
- }
-
- ~ParserCommon() override {
- }
-
- void addDefinition(Definition* def) {
- fParent->fChildren.push_back(def);
- fParent = def;
- }
-
- 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;
- fIndentStack.emplace_back(kind, fIndent);
- }
-
- void indentOut() {
- SkASSERT(fIndent >= 4);
- SkASSERT(fIndentStack.back().fIndent == fIndent);
- fIndent -= 4;
- fIndentStack.pop_back();
- }
-
- void indentToColumn(int column) {
- SkASSERT(column >= fColumn);
- SkASSERT(!fReturnOnWrite);
- SkASSERT(column < 80);
- FPRINTF("%*s", column - fColumn, "");
- fColumn = column;
- fSpaces += column - fColumn;
- }
-
- bool leadingPunctuation(const char* str, size_t len) const {
- if (!fPendingSpace) {
- return false;
- }
- if (len < 2) {
- return false;
- }
- if ('.' != str[0] && ',' != str[0] && ';' != str[0] && ':' != str[0]) {
- return false;
- }
- return ' ' >= str[1];
- }
-
- void lf(int count) {
- fPendingLF = SkTMax(fPendingLF, count);
- this->nl();
- }
-
- void lfAlways(int count) {
- this->lf(count);
- this->writePending();
- }
-
- void lfcr() {
- this->lf(1);
- }
-
- void nl() {
- SkASSERT(!fReturnOnWrite);
- fLinefeeds = 0;
- fSpaces = 0;
- fColumn = 0;
- fPendingSpace = 0;
- }
-
- bool parseFile(const char* file, const char* suffix, OneFile );
- bool parseStatus(const char* file, const char* suffix, StatusFilter filter);
- virtual bool parseFromFile(const char* path) = 0;
- bool parseSetup(const char* path);
-
- void popObject() {
- fParent->fContentEnd = fParent->fTerminator = fChar;
- fParent = fParent->fParent;
- }
-
- static char* ReadToBuffer(string filename, int* size);
-
- virtual void reset() = 0;
-
- void resetCommon() {
- fLine = fChar = fStart;
- fLineCount = 0;
- fLinesWritten = 1;
- fParent = nullptr;
- fIndent = 0;
- fOut = nullptr;
- fMaxLF = 2;
- fPendingLF = 0;
- fPendingSpace = 0;
- fOutdentNext = false;
- fWritingIncludes = false;
- fDebugWriteCodeBlock = false;
- nl();
- }
-
- void setAsParent(Definition* definition) {
- if (fParent) {
- fParent->fChildren.push_back(definition);
- definition->fParent = fParent;
- }
- fParent = definition;
- }
-
- void singleLF() {
- fMaxLF = 1;
- }
-
- void stringAppend(string& result, char ch) const;
- void stringAppend(string& result, string str) const;
- void stringAppend(string& result, const Definition* ) const;
-
- void writeBlock(int size, const char* data) {
- SkAssertResult(writeBlockTrim(size, data));
- }
-
- bool writeBlockIndent(int size, const char* data, bool ignoreIndent);
-
- void writeBlockSeparator() {
- this->writeString(
- "# ------------------------------------------------------------------------------");
- this->lf(2);
- }
-
- bool writeBlockTrim(int size, const char* data);
-
- void writeCommentHeader() {
- this->lf(2);
- this->writeString("/**");
- this->writeSpace();
- }
-
- void writeCommentTrailer(OneLine oneLine) {
- if (OneLine::kNo == oneLine) {
- this->lf(1);
- } else {
- this->writeSpace();
- }
- this->writeString("*/");
- this->lfcr();
- }
-
- void writePending();
-
- // write a pending space, so that two consecutive calls
- // don't double write, and trailing spaces on lines aren't written
- void writeSpace(int count = 1) {
- SkASSERT(!fReturnOnWrite);
- SkASSERT(!fPendingLF);
- SkASSERT(!fLinefeeds);
- SkASSERT(fColumn > 0);
- SkASSERT(!fSpaces);
- fPendingSpace = count;
- }
-
- void writeString(const char* str);
-
- void writeString(string str) {
- this->writeString(str.c_str());
- }
-
- static bool WrittenFileDiffers(string filename, string readname);
-
- unordered_map<string, sk_sp<SkData>> fRawData;
- unordered_map<string, vector<char>> fLFOnly;
- 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)
- int fSpaces; // number of spaces (indent) last written, zeroed on non-space
- int fColumn; // current column; number of chars past last linefeed
- int fIndent; // desired indention
- int fPendingSpace; // one or two spaces should preceed the next string or block
- size_t fLinesWritten; // as opposed to fLineCount, number of lines read
- char fLastChar; // last written
- bool fDebugOut; // set true to write to std out
- bool fValidate; // set true to check anchor defs and refs
- bool fOutdentNext; // set at end of embedded struct to prevent premature outdent
- bool fWroteSomething; // used to detect empty content; an alternative source is preferable
- bool fReturnOnWrite; // used to detect non-empty content; allowing early return
- bool fWritingIncludes; // set true when writing includes to check >100 columns
- mutable bool fDebugWriteCodeBlock;
-
-private:
- typedef TextParser INHERITED;
-};
-
-struct JsonStatus {
- const Json::Value& fObject;
- Json::Value::iterator fIter;
- string fName;
- StatusFilter fStatusFilter;
-};
-
-class JsonCommon : public ParserCommon {
-public:
- bool empty() { return fStack.empty(); }
- bool parseFromFile(const char* path) override;
-
- void reset() override {
- fStack.clear();
- INHERITED::resetCommon();
- }
-
- vector<JsonStatus> fStack;
- Json::Value fRoot;
-private:
- typedef ParserCommon INHERITED;
-};
-
-class StatusIter : public JsonCommon {
-public:
- StatusIter(const char* statusFile, const char* suffix, StatusFilter);
- ~StatusIter() override {}
- string baseDir();
- bool next(string* file, StatusFilter* filter);
-private:
- const char* fSuffix;
- StatusFilter fFilter;
-};
-
-class BmhParser : public ParserCommon {
-public:
- enum class MarkLookup {
- kRequire,
- kAllowUnknown,
- };
-
- enum class Resolvable {
- kNo, // neither resolved nor output
- kYes, // resolved, output
- kOut, // mostly resolved, output (FIXME: is this really different from kYes?)
- kCode, // resolve methods as they are used, not as they are prototyped
- kFormula, // kCode, plus make most spaces non-breaking
- 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 {
- kText,
- kPng,
- kAll
- };
-
- enum class Exemplary {
- kNo,
- kYes,
- kOptional,
- };
-
- enum class TableState {
- kNone,
- kColumnStart,
- kColumnEnd,
- };
-
- enum class HasTag {
- kNo,
- kYes,
- };
-
- enum class TrimExtract {
- kNo,
- kYes,
- };
-
- BmhParser(bool skip) : ParserCommon()
- , fMaps {
- { &fClassMap, MarkType::kClass }
- , { &fConstMap, MarkType::kConst }
- , { &fDefineMap, MarkType::kDefine }
- , { &fEnumMap, MarkType::kEnum }
- , { &fClassMap, MarkType::kEnumClass }
- , { &fMethodMap, MarkType::kMethod }
- , { &fClassMap, MarkType::kStruct }
- , { &fTypedefMap, MarkType::kTypedef }
- }
- , fSkip(skip) {
- this->reset();
- }
-
- ~BmhParser() override {}
-
- bool addDefinition(const char* defStart, bool hasEnd, MarkType markType,
- const vector<string>& typeNameBuilder, HasTag hasTag);
- bool checkEndMarker(MarkType markType, string name) const;
- bool checkExamples() const;
- const char* checkForFullTerminal(const char* end, const Definition* ) const;
- bool checkParamReturn(const Definition* definition) const;
- bool dumpExamples(FILE* fiddleOut, Definition& def, bool* continuation) const;
- bool dumpExamples(const char* fiddleJsonFileName) const;
- bool checkExampleHashes() const;
- bool childOf(MarkType markType) const;
- string className(MarkType markType);
- bool collectExternals();
- int endHashCount() const;
- bool endTableColumn(const char* end, const char* terminator);
- bool exampleToScript(Definition*, ExampleOptions, string* result ) const;
- string extractText(const Definition* , TrimExtract ) const;
- RootDefinition* findBmhObject(MarkType markType, string typeName);
- bool findDefinitions();
- Definition* findExample(string name) const;
- 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;
-
- bool parseFromFile(const char* path) override {
- if (!INHERITED::parseSetup(path)) {
- return false;
- }
- fCheckMethods = !strstr(path, "undocumented.bmh");
- return findDefinitions();
- }
-
- void parseHashAnchor(Definition* );
- void parseHashFormula(Definition* );
- void parseHashLine(Definition* );
- bool popParentStack(Definition* );
- void reportDuplicates(const Definition& , string dup) const;
- void resetExampleHashes();
-
- void reset() override {
- INHERITED::resetCommon();
- fRoot = nullptr;
- fWorkingColumn = nullptr;
- fRow = nullptr;
- fTableState = TableState::kNone;
- fMC = '#';
- fInChar = false;
- fInCharCommentString = false;
- fInComment = false;
- fInEnum = false;
- fInString = false;
- 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);
- bool skipToString();
- void spellCheck(const char* match, SkCommandLineFlags::StringArray report) const;
- void spellStatus(const char* match, SkCommandLineFlags::StringArray report) const;
- vector<string> topicName();
- vector<string> typeName(MarkType markType, bool* expectEnd);
- string typedefName() override;
- string uniqueName(string base, MarkType markType);
- string uniqueRootName(string base, MarkType markType);
- void validate() const;
- string word(string prefix, string delimiter);
-
-public:
- struct MarkProps {
- const char* fName;
- MarkType fMarkType;
- Resolvable fResolve;
- Exemplary fExemplary; // worthy of an example
- uint64_t fParentMask;
- };
-
- struct DefinitionMap {
- unordered_map<string, RootDefinition>* fMap;
- MarkType fMarkType;
- };
-
- vector<DefinitionMap> fMaps;
-
- static MarkProps kMarkProps[Last_MarkType + 1];
- forward_list<RootDefinition> fTopics;
- forward_list<Definition> fMarkup;
- forward_list<RootDefinition> fExternals;
- vector<string> fInputFiles;
- unordered_map<string, RootDefinition> fClassMap;
- unordered_map<string, RootDefinition> fConstMap;
- unordered_map<string, RootDefinition> fDefineMap;
- unordered_map<string, RootDefinition> fEnumMap;
- unordered_map<string, RootDefinition> fMethodMap;
- unordered_map<string, RootDefinition> fTypedefMap;
- unordered_map<string, Definition*> fTopicMap;
- unordered_map<string, Definition*> fAliasMap;
- unordered_map<string, Definition*> fPhraseMap;
- NameMap fGlobalNames;
- RootDefinition* fRoot;
- Definition* fWorkingColumn;
- Definition* fRow;
- const char* fColStart;
- TableState fTableState;
- mutable char fMC; // markup character
- bool fAnonymous;
- bool fCloned;
- bool fInChar;
- bool fInCharCommentString;
- bool fInEnum;
- bool fInComment;
- bool fInString;
- bool fCheckMethods;
- bool fSkip = false;
- bool fWroteOut = false;
-private:
- typedef ParserCommon INHERITED;
-};
-
-class IncludeParser : public ParserCommon {
-public:
- enum class IsStruct {
- kNo,
- kYes,
- };
-
- enum class Elided {
- kNo,
- kYes,
- };
-
- struct CheckCode {
- enum class State {
- kNone,
- kClassDeclaration,
- kConstructor,
- kForwardDeclaration,
- kMethod,
- };
-
- void reset() {
- fInDebugCode = nullptr;
- fPrivateBrace = 0;
- fBraceCount = 0;
- fIndent = 0;
- fDoubleReturn = 0;
- fState = State::kNone;
- fPrivateProtected = false;
- fTypedefReturn = false;
- fSkipAPI = false;
- fSkipInline = false;
- fSkipWarnUnused = false;
- fWriteReturn = false;
- }
-
- const char* fInDebugCode;
- int fPrivateBrace;
- int fBraceCount;
- int fIndent;
- int fDoubleReturn;
- State fState;
- bool fPrivateProtected;
- bool fTypedefReturn;
- bool fSkipAPI;
- bool fSkipInline;
- bool fSkipWarnUnused;
- bool fWriteReturn;
- };
-
- IncludeParser() : ParserCommon()
- , fMaps {
- { &fIConstMap, MarkType::kConst }
- , { &fIDefineMap, MarkType::kDefine }
- , { &fIEnumMap, MarkType::kEnum }
- , { &fIEnumMap, MarkType::kEnumClass }
- , { &fIStructMap, MarkType::kStruct }
- , { &fITemplateMap, MarkType::kTemplate }
- , { &fITypedefMap, MarkType::kTypedef }
- , { &fIUnionMap, MarkType::kUnion }
- }
- {
- this->reset();
- }
-
- ~IncludeParser() override {}
-
- void addKeyword(KeyWord keyWord);
-
- void addPunctuation(Punctuation punctuation) {
- fParent->fTokens.emplace_back(punctuation, fChar, fLineCount, fParent, '\0');
- }
-
- void addWord() {
- fParent->fTokens.emplace_back(fIncludeWord, fChar, fLineCount, fParent, '\0');
- fIncludeWord = nullptr;
- }
-
- bool advanceInclude(TextParser& i);
- bool inAlignAs() const;
- void checkForMissingParams(const vector<string>& methodParams,
- const vector<string>& foundParams);
- bool checkForWord();
- string className() const;
-
- string codeBlock(const Definition& def, bool inProgress) const {
- return codeBlock(def.fMarkType, def.fName, inProgress);
- }
-
- string codeBlock(MarkType markType, string name, bool inProgress) const {
- if (MarkType::kClass == markType || MarkType::kStruct == markType) {
- auto map = fIClassMap.find(name);
- SkASSERT(fIClassMap.end() != map || inProgress);
- return fIClassMap.end() != map ? map->second.fCode : "";
- }
- if (MarkType::kConst == markType) {
- auto map = fIConstMap.find(name);
- SkASSERT(fIConstMap.end() != map);
- return map->second->fCode;
- }
- if (MarkType::kDefine == markType) {
- auto map = fIDefineMap.find(name);
- SkASSERT(fIDefineMap.end() != map);
- return map->second->fCode;
- }
- if (MarkType::kEnum == markType || MarkType::kEnumClass == markType) {
- auto map = fIEnumMap.find(name);
- SkASSERT(fIEnumMap.end() != map);
- return map->second->fCode;
- }
- if (MarkType::kTypedef == markType) {
- auto map = fITypedefMap.find(name);
- SkASSERT(fITypedefMap.end() != map);
- return map->second->fCode;
- }
- SkASSERT(0);
- return "";
- }
-
- void codeBlockAppend(string& result, char ch) const;
- void codeBlockSpaces(string& result, int indent) const;
-
- bool crossCheck(BmhParser& );
- IClassDefinition* defineClass(const Definition& includeDef, string className);
- void dumpClassTokens(IClassDefinition& classDef);
- void dumpComment(const Definition& );
- void dumpCommonTail(const Definition& );
- void dumpConst(const Definition& , string className);
- void dumpDefine(const Definition& );
- void dumpEnum(const Definition& , string name);
- bool dumpGlobals(string* globalFileName, long int* globalTell);
- void dumpMethod(const Definition& , string className);
- void dumpMember(const Definition& );
- bool dumpTokens();
- bool dumpTokens(string skClassName, string globalFileName, long int* globalTell);
- void dumpTypedef(const Definition& , string className);
-
- string elidedCodeBlock(const Definition& );
- string filteredBlock(string inContents, string filterContents);
- bool findComments(const Definition& includeDef, Definition* markupDef);
- Definition* findIncludeObject(const Definition& includeDef, MarkType markType,
- string typeName);
- static KeyWord FindKey(const char* start, const char* end);
- Definition* findMethod(const Definition& bmhDef);
- Bracket grandParentBracket() const;
- const Definition* include(string ) const;
- bool isClone(const Definition& token);
- bool isConstructor(const Definition& token, string className);
- bool isInternalName(const Definition& token);
- bool isMember(const Definition& token) const;
- bool isOperator(const Definition& token);
- Definition* parentBracket(Definition* parent) const;
- bool parseChar();
- bool parseComment(string filename, const char* start, const char* end, int lineCount,
- Definition* markupDef);
- bool parseClass(Definition* def, IsStruct);
- bool parseConst(Definition* child, Definition* markupDef);
- bool parseDefine(Definition* child, Definition* markupDef);
- bool parseEnum(Definition* child, Definition* markupDef);
-
- bool parseFromFile(const char* path) override {
- this->reset();
- if (!INHERITED::parseSetup(path)) {
- return false;
- }
- string name(path);
- return this->parseInclude(name);
- }
-
- bool parseInclude(string name);
- bool parseMember(Definition* child, Definition* markupDef);
- bool parseMethod(Definition* child, Definition* markupDef);
- bool parseObject(Definition* child, Definition* markupDef);
- bool parseObjects(Definition* parent, Definition* markupDef);
- bool parseTemplate(Definition* child, Definition* markupDef);
- bool parseTypedef(Definition* child, Definition* markupDef);
- bool parseUsing();
- bool parseUnion();
-
- void popBracket() {
- if (Definition::Type::kKeyWord == fParent->fType
- && KeyWord::kTypename == fParent->fKeyWord) {
- this->popObject();
- }
- SkASSERT(Definition::Type::kBracket == fParent->fType);
- this->popObject();
- Bracket bracket = this->topBracket();
- this->setBracketShortCuts(bracket);
- }
-
- void pushBracket(Bracket bracket) {
- this->setBracketShortCuts(bracket);
- fParent->fTokens.emplace_back(bracket, fChar, fLineCount, fParent, '\0');
- Definition* container = &fParent->fTokens.back();
- this->addDefinition(container);
- }
-
- bool references(const SkString& file) const;
-
- static void RemoveFile(const char* docs, const char* includes);
- static void RemoveOneFile(const char* docs, const char* includesFileOrPath);
-
- void reset() override {
- INHERITED::resetCommon();
- fRootTopic = nullptr;
- fConstExpr = nullptr;
- fInBrace = nullptr;
- fIncludeWord = nullptr;
- fLastObject = nullptr;
- fPriorEnum = nullptr;
- fPriorObject = nullptr;
- fPrev = '\0';
- fInChar = false;
- fInCharCommentString = false;
- fInComment = false;
- fInDefine = false;
- fInEnum = false;
- fInFunction = false;
- fInString = false;
- fFailed = false;
- }
-
- void setBracketShortCuts(Bracket bracket) {
- fInComment = Bracket::kSlashSlash == bracket || Bracket::kSlashStar == bracket;
- fInString = Bracket::kString == bracket;
- fInChar = Bracket::kChar == bracket;
- fInCharCommentString = fInChar || fInComment || fInString;
- }
-
- Bracket topBracket() const;
-
- template <typename T>
- string uniqueName(const unordered_map<string, T>& m, string typeName) {
- string base(typeName.size() > 0 ? typeName : "_anonymous");
- string name(base);
- int anonCount = 1;
- do {
- auto iter = m.find(name);
- if (iter == m.end()) {
- return name;
- }
- name = base + '_';
- name += to_string(++anonCount);
- } while (true);
- // should never get here
- return string();
- }
-
- void validate() const;
- void writeCodeBlock();
- string writeCodeBlock(const Definition&, MarkType );
- string writeCodeBlock(TextParser& i, MarkType , int indent);
-
- void writeDefinition(const Definition& def) {
- if (def.length() > 1) {
- this->writeBlock((int) (def.fContentEnd - def.fContentStart), def.fContentStart);
- this->lf(1);
- }
- }
-
- void writeDefinition(const Definition& def, string name, int spaces) {
- this->writeBlock((int) (def.fContentEnd - def.fContentStart), def.fContentStart);
- this->writeSpace(spaces);
- this->writeString(name);
- this->lf(1);
- }
-
- void writeEndTag() {
- this->lf(1);
- this->writeString("##");
- this->lf(1);
- }
-
- void writeEndTag(const char* tagType) {
- this->lf(1);
- this->writeString(string("#") + tagType + " ##");
- this->lf(1);
- }
-
- void writeEndTag(const char* tagType, const char* tagID, int spaces = 1) {
- this->lf(1);
- this->writeString(string("#") + tagType + " " + tagID);
- this->writeSpace(spaces);
- this->writeString("##");
- this->lf(1);
- }
-
- void writeEndTag(const char* tagType, string tagID, int spaces = 1) {
- this->writeEndTag(tagType, tagID.c_str(), spaces);
- }
-
- void writeIncompleteTag(const char* tagType, string tagID, int spaces = 1) {
- this->writeString(string("#") + tagType + " " + tagID);
- this->writeSpace(spaces);
- this->writeString("incomplete");
- this->writeSpace();
- this->writeString("##");
- this->lf(1);
- }
-
- void writeIncompleteTag(const char* tagType) {
- this->writeString(string("#") + tagType + " incomplete ##");
- this->lf(1);
- }
-
- void writeTableHeader(const char* col1, size_t pad, const char* col2) {
- this->lf(1);
- this->writeString("#Table");
- this->lf(1);
- this->writeString("#Legend");
- this->lf(1);
- string legend = "# ";
- legend += col1;
- if (pad > strlen(col1)) {
- legend += string(pad - strlen(col1), ' ');
- }
- legend += " # ";
- legend += col2;
- legend += " ##";
- this->writeString(legend);
- this->lf(1);
- this->writeString("#Legend ##");
- this->lf(1);
- }
-
- void writeTableRow(size_t pad, string col1) {
- this->lf(1);
- string row = "# " + col1 + string(pad - col1.length(), ' ') + " # ##";
- this->writeString(row);
- this->lf(1);
- }
-
- void writeTableRow(size_t pad1, string col1, size_t pad2, string col2) {
- this->lf(1);
- string row = "# " + col1 + string(pad1 - col1.length(), ' ') + " # " +
- col2 + string(pad2 - col2.length(), ' ') + " ##";
- this->writeString(row);
- this->lf(1);
- }
-
- void writeTableTrailer() {
- this->lf(1);
- this->writeString("#Table ##");
- this->lf(1);
- }
-
- void writeTag(const char* tagType) {
- this->lf(1);
- this->writeString("#");
- this->writeString(tagType);
- }
-
- void writeTagNoLF(const char* tagType, const char* tagID) {
- this->writeString("#");
- this->writeString(tagType);
- this->writeSpace();
- this->writeString(tagID);
- }
-
- void writeTagNoLF(const char* tagType, string tagID) {
- this->writeTagNoLF(tagType, tagID.c_str());
- }
-
- void writeTag(const char* tagType, const char* tagID) {
- this->lf(1);
- this->writeTagNoLF(tagType, tagID);
- }
-
- void writeTag(const char* tagType, string tagID) {
- this->writeTag(tagType, tagID.c_str());
- }
-
- void writeTagTable(string tagType, string body) {
- this->writeTag(tagType.c_str());
- this->writeSpace(1);
- this->writeString("#");
- this->writeSpace(1);
- this->writeString(body);
- this->writeSpace(1);
- this->writeString("##");
- }
-
-protected:
- static void ValidateKeyWords();
-
- struct DefinitionMap {
- unordered_map<string, Definition*>* fInclude;
- MarkType fMarkType;
- };
-
- vector<DefinitionMap> fMaps;
- unordered_map<string, Definition> fIncludeMap;
- list<Definition> fGlobals;
- unordered_map<string, IClassDefinition> fIClassMap;
- unordered_map<string, Definition*> fIConstMap;
- unordered_map<string, Definition*> fIDefineMap;
- unordered_map<string, Definition*> fIEnumMap;
- unordered_map<string, Definition*> fIFunctionMap;
- unordered_map<string, Definition*> fIStructMap;
- unordered_map<string, Definition*> fITemplateMap;
- unordered_map<string, Definition*> fITypedefMap;
- unordered_map<string, Definition*> fIUnionMap;
- CheckCode fCheck;
- Definition* fRootTopic;
- Definition* fConstExpr;
- Definition* fInBrace;
- Definition* fLastObject;
- Definition* fPriorEnum;
- Definition* fPriorObject;
- int fPriorIndex;
- const char* fIncludeWord;
- Elided fElided;
- char fPrev;
- bool fInChar;
- bool fInCharCommentString;
- bool fInComment;
- bool fInDefine;
- bool fInEnum;
- bool fInFunction;
- bool fInString;
- bool fFailed;
-
- typedef ParserCommon INHERITED;
-};
-
-class IncludeWriter : public IncludeParser {
-public:
- enum class Word {
- kStart,
- kCap,
- kFirst,
- kUnderline,
- kMixed,
- };
-
- enum class Phrase {
- kNo,
- kYes,
- };
-
- enum class PunctuationState {
- kStart,
- kDelimiter,
- kParen, // treated as a delimiter unless following a space, and followed by word
- kPeriod,
- kSpace,
- };
-
- enum class RefType {
- kUndefined,
- kNormal,
- kExternal,
- };
-
- enum class SkipFirstLine {
- kNo,
- kYes,
- };
-
- enum class Wrote {
- kNone,
- kLF,
- kChars,
- };
-
- enum class MemberPass {
- kCount,
- kOut,
- };
-
- enum class ItemState {
- kNone,
- kName,
- kValue,
- kComment,
- };
-
- struct IterState {
- IterState (list<Definition>::iterator tIter, list<Definition>::iterator tIterEnd)
- : fDefIter(tIter)
- , fDefEnd(tIterEnd) {
- }
- list<Definition>::iterator fDefIter;
- list<Definition>::iterator fDefEnd;
- };
-
- struct ParentPair {
- const Definition* fParent;
- const ParentPair* fPrev;
- };
-
- struct Preprocessor {
- Preprocessor() {
- reset();
- }
-
- void reset() {
- fDefinition = nullptr;
- fStart = nullptr;
- fEnd = nullptr;
- fWord = false;
- }
-
- const Definition* fDefinition;
- const char* fStart;
- const char* fEnd;
- bool fWord;
- };
-
- struct Item {
- void reset() {
- fName = "";
- fValue = "";
- }
-
- string fName;
- string fValue;
- };
-
- struct LastItem {
- const char* fStart;
- const char* fEnd;
- };
-
- struct ItemLength {
- int fCurName;
- int fCurValue;
- int fLongestName;
- int fLongestValue;
- };
-
- IncludeWriter() : IncludeParser() {
- this->reset();
- }
-
- ~IncludeWriter() override {}
-
- bool contentFree(int size, const char* data) const {
- while (size > 0 && data[0] <= ' ') {
- --size;
- ++data;
- }
- while (size > 0 && data[size - 1] <= ' ') {
- --size;
- }
- return 0 == size;
- }
-
- 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* bmhConst);
- void constSizeMembers(const RootDefinition* root);
- bool defineOut(const Definition& );
- 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;
- ItemState enumMemberName(const Definition& child,
- const Definition* token, Item* , LastItem* , const Definition** enumItem);
- void enumMemberOut(const Definition* currentEnumItem, const Definition& child,
- const Item& , Preprocessor& );
- void enumMembersOut(Definition& child);
- bool enumPreprocessor(Definition* token, MemberPass pass,
- vector<IterState>& iterStack, IterState** iterState, Preprocessor* );
- void enumSizeItems(const Definition& child);
- bool findEnumSubtopic(string undername, const Definition** ) const;
- void firstBlock(int size, const char* data);
- bool firstBlockTrim(int size, const char* data);
- Definition* findMemberCommentBlock(const vector<Definition*>& bmhChildren, string name) const;
- Definition* findMethod(string name, RootDefinition* ) const;
-
- void indentDeferred(IndentKind kind) {
- if (fIndentNext) {
- this->indentIn(kind);
- fIndentNext = false;
- }
- }
-
- int lookupMethod(const PunctuationState punctuation, const Word word,
- const int start, const int run, int lastWrite,
- const char* data, bool hasIndirection);
- int lookupReference(const PunctuationState punctuation, const Word word,
- const int start, const int run, int lastWrite, const char last,
- const char* data);
- const Definition* matchMemberName(string matchName, const Definition& child) const;
- void methodOut(Definition* method, const Definition& child);
- bool populate(Definition* def, ParentPair* parentPair, RootDefinition* root);
- bool populate(BmhParser& bmhParser);
-
- void reset() override {
- INHERITED::resetCommon();
- fBmhParser = nullptr;
- fDeferComment = nullptr;
- fBmhMethod = nullptr;
- fEnumDef = nullptr;
- fMethodDef = nullptr;
- fBmhConst = nullptr;
- fConstDef = nullptr;
- fLastDescription = nullptr;
- fStartSetter = nullptr;
- fBmhStructDef = nullptr;
- fContinuation = nullptr;
- fInStruct = false;
- fWroteMethod = false;
- fIndentNext = false;
- fPendingMethod = false;
- fFirstWrite = false;
- fStructEnded = false;
- fWritingIncludes = true;
- }
-
- 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);
- void setStart(const char* start, const Definition * );
- void setStartBack(const char* start, const Definition * );
- Definition* structMemberOut(const Definition* memberStart, const Definition& child);
- void structOut(const Definition* root, const Definition& child,
- const char* commentStart, const char* commentEnd);
- void structSizeMembers(const Definition& child);
- bool writeHeader(std::pair<const string, Definition>& );
-private:
- vector<const Definition* > fICSStack;
- BmhParser* fBmhParser;
- Definition* fDeferComment;
- const Definition* fBmhMethod;
- const Definition* fEnumDef;
- const Definition* fMethodDef;
- const Definition* fBmhConst;
- const Definition* fConstDef;
- const Definition* fLastDescription;
- const Definition* fStartSetter;
- Definition* fBmhStructDef;
- const char* fContinuation; // used to construct paren-qualified method name
- int fAnonymousEnumCount;
- int fEnumItemValueTab;
- int fEnumItemCommentTab;
- int fStructMemberTab;
- int fStructValueTab;
- int fStructCommentTab;
- int fStructMemberLength;
- int fConstValueTab;
- int fConstCommentTab;
- int fConstLength;
- bool fInStruct; // set if struct is inside class
- bool fWroteMethod;
- bool fIndentNext;
- bool fPendingMethod;
- bool fFirstWrite; // set to write file information just after includes
- bool fStructEnded; // allow resetting indent after struct is complete
-
- typedef IncludeParser INHERITED;
-};
-
-class FiddleBase : public JsonCommon {
-protected:
- FiddleBase(BmhParser* bmh)
- : fBmhParser(bmh)
- , fContinuation(false)
- , fTextOut(false)
- , fPngOut(false)
- {
- this->reset();
- }
-
- void reset() override {
- INHERITED::reset();
- }
-
- Definition* findExample(string name) const { return fBmhParser->findExample(name); }
- bool parseFiddles();
- virtual bool pngOut(Definition* example) = 0;
- virtual bool textOut(Definition* example, const char* stdOutStart,
- const char* stdOutEnd) = 0;
-
- BmhParser* fBmhParser; // must be writable; writes example hash
- string fFullName;
- bool fContinuation;
- bool fTextOut;
- bool fPngOut;
-private:
- typedef JsonCommon INHERITED;
-};
-
-class FiddleParser : public FiddleBase {
-public:
- FiddleParser(BmhParser* bmh) : FiddleBase(bmh) {
- fTextOut = true;
- }
-
- bool parseFromFile(const char* path) override {
- if (!INHERITED::parseFromFile(path)) {
- return false;
- }
- fBmhParser->resetExampleHashes();
- if (!INHERITED::parseFiddles()) {
- return false;
- }
- return fBmhParser->checkExampleHashes();
- }
-
-private:
- bool pngOut(Definition* example) override {
- return true;
- }
-
- bool textOut(Definition* example, const char* stdOutStart,
- const char* stdOutEnd) override;
-
- typedef FiddleBase INHERITED;
-};
-
-class Catalog : public FiddleBase {
-public:
- Catalog(BmhParser* bmh) : FiddleBase(bmh) {}
-
- bool appendFile(string path);
- bool closeCatalog(const char* outDir);
- bool openCatalog(const char* inDir);
- bool openStatus(const char* inDir);
-
- bool parseFromFile(const char* path) override ;
-private:
- bool pngOut(Definition* example) override;
- bool textOut(Definition* example, const char* stdOutStart,
- const char* stdOutEnd) override;
-
- string fDocsDir;
-
- typedef FiddleBase INHERITED;
-};
-
-class HackParser : public ParserCommon {
-public:
- HackParser(const BmhParser& bmhParser)
- : ParserCommon()
- , fBmhParser(bmhParser) {
- this->reset();
- }
-
- bool parseFromFile(const char* path) override {
- if (!INHERITED::parseSetup(path)) {
- return false;
- }
- return hackFiles();
- }
-
- void reset() override {
- INHERITED::resetCommon();
- }
-
- void replaceWithPop(const Definition* );
-
-private:
- const BmhParser& fBmhParser;
- bool hackFiles();
-
- typedef ParserCommon INHERITED;
-};
-
-class MdOut : public ParserCommon {
-public:
- struct SubtopicDescriptions {
- string fSingular;
- string fPlural;
- string fOneLiner;
- string fDetails;
- };
-
- MdOut(BmhParser& bmh, IncludeParser& inc) : ParserCommon()
- , fBmhParser(bmh)
- , fIncludeParser(inc) {
- this->reset();
- this->addPopulators();
- fBmhParser.setUpGlobalSubstitutes();
- }
-
- bool buildReferences(const char* docDir, const char* mdOutDirOrFile);
- bool buildStatus(const char* docDir, const char* mdOutDir);
- void checkAnchors();
-
-private:
- enum class TableState {
- kNone,
- kRow,
- kColumn,
- };
-
- struct AnchorDef {
- string fDef;
- MarkType fMarkType;
- };
-
- 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);
- string linkName(const Definition* ) const;
- string linkRef(string leadingSpaces, Definition*, string ref, BmhParser::Resolvable );
- void markTypeOut(Definition* , const Definition** prior);
- void mdHeaderOut(int depth) { mdHeaderOutLF(depth, 2); }
- void mdHeaderOutLF(int depth, int lf);
- 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* );
-
- SubtopicDescriptions& populator(string key) {
- auto entry = fPopulators.find(key);
- // FIXME: this should have been detected earlier
- SkASSERT(fPopulators.end() != entry);
- return entry->second;
- }
-
- void reset() override {
- INHERITED::resetCommon();
- fEnumClass = nullptr;
- fMethod = nullptr;
- fRoot = nullptr;
- fSubtopic = nullptr;
- fLastParam = nullptr;
- fTableState = TableState::kNone;
- fAddRefFailed = false;
- fHasFiddle = false;
- fInDescription = false;
- fInList = false;
- fResolveAndIndent = false;
- fLiteralAndIndent = false;
- fLastDef = nullptr;
- fParamEnd = nullptr;
- fInProgress = false;
- }
-
- BmhParser::Resolvable resolvable(const Definition* definition) const {
- MarkType markType = definition->fMarkType;
- if (MarkType::kCode == markType) {
- for (auto child : definition->fChildren) {
- if (MarkType::kLiteral == child->fMarkType) {
- return BmhParser::Resolvable::kLiteral;
- }
- }
- }
- if ((MarkType::kExample == markType
- || MarkType::kFunction == markType) && fHasFiddle) {
- return BmhParser::Resolvable::kNo;
- }
- return BmhParser::kMarkProps[(int) markType].fResolve;
- }
-
- void resolveOut(const char* start, const char* end, BmhParser::Resolvable );
- void returnHeaderOut(const Definition** prior, Definition* def);
- void rowOut(string col1, const Definition* col2);
- void rowOut(const char * name, string description, bool literalName);
-
- void subtopicOut(string name);
- void subtopicsOut(Definition* def);
- void subtopicOut(string key, const vector<Definition*>& data, const Definition* csParent,
- const Definition* topicParent, bool showClones);
- bool subtopicRowOut(string keyName, const Definition* entry);
- void summaryOut(const Definition* def, MarkType , string name);
- string tableDataCodeDef(const Definition* def);
- string tableDataCodeDef(string def, string name);
- string tableDataCodeLocalRef(string name);
- string tableDataCodeLocalRef(string ref, string name);
- string tableDataCodeRef(const Definition* ref);
- string tableDataCodeRef(string ref, string name);
- void writeSubtopicTableHeader(string key);
-
- 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;
- const Definition* fLastDef;
- Definition* fMethod;
- RootDefinition* fRoot; // used in generating populated tables; always struct or class
- RootDefinition* fSubtopic; // used in resolving symbols
- const Definition* fLastParam;
- TableState fTableState;
- unordered_map<string, SubtopicDescriptions> fPopulators;
- unordered_map<string, string> fPhraseParams;
- const char* fParamEnd;
- bool fAddRefFailed;
- bool fHasFiddle;
- bool fInDescription; // FIXME: for now, ignore unfound camelCase in description since it may
- // be defined in example which at present cannot be linked to
- bool fInList;
- bool fLiteralAndIndent;
- bool fResolveAndIndent;
- bool fOddRow;
- bool fHasDetails;
- bool fInProgress;
- typedef ParserCommon INHERITED;
-};
-
-
-// some methods cannot be trivially parsed; look for class-name / ~ / operator
-class MethodParser : public TextParser {
-public:
- MethodParser(string className, string fileName,
- const char* start, const char* end, int lineCount)
- : TextParser(fileName, start, end, lineCount)
- , fClassName(className) {
- size_t doubleColons = className.find_last_of("::");
- if (string::npos != doubleColons) {
- fLocalName = className.substr(doubleColons + 1);
- SkASSERT(fLocalName.length() > 0);
- }
- }
-
- ~MethodParser() override {}
-
- string localName() const {
- return fLocalName;
- }
-
- void setLocalName(string name) {
- if (name == fClassName) {
- fLocalName = "";
- } else {
- fLocalName = name;
- }
- }
-
- // returns true if close brace was skipped
- int skipToMethodStart() {
- if (!fClassName.length()) {
- return this->skipToAlphaNum();
- }
- int braceCount = 0;
- while (!this->eof() && !isalnum(this->peek()) && '~' != this->peek()) {
- braceCount += '{' == this->peek();
- braceCount -= '}' == this->peek();
- this->next();
- }
- return braceCount;
- }
-
- void skipToMethodEnd(BmhParser::Resolvable resolvable) {
- if (this->eof()) {
- return;
- }
- string name = fLocalName.length() ? fLocalName : fClassName;
- if ('~' == this->peek()) {
- this->next();
- if (!this->startsWith(name.c_str())) {
- --fChar;
- return;
- }
- }
- 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)) {
- this->skipToEndBracket(')');
- SkAssertResult(')' == this->next());
- this->skipExact("_const") || (BmhParser::Resolvable::kCode == resolvable
- && this->skipExact(" const"));
- return;
- }
- }
- if (this->startsWith("Sk") && this->wordEndsWith(".h")) { // allow include refs
- this->skipToNonName();
- } else {
- this->skipFullName();
- if (this->endsWith("operator")) {
- const char* ptr = this->anyOf("\n (");
- if (ptr && '(' == *ptr) {
- this->skipToEndBracket(')');
- SkAssertResult(')' == this->next());
- this->skipExact("_const");
- }
- }
- }
- }
-
- bool wordEndsWith(const char* str) const {
- const char* space = this->strnchr(' ', fEnd);
- if (!space) {
- return false;
- }
- size_t len = strlen(str);
- if (space < fChar + len) {
- return false;
- }
- return !strncmp(str, space - len, len);
- }
-
-private:
- string fClassName;
- string fLocalName;
- typedef TextParser INHERITED;
-};
-
-bool SelfCheck(const BmhParser& );
-
#endif
-
diff --git a/tools/bookmaker/cataloger.cpp b/tools/bookmaker/cataloger.cpp
index fe0084d5bf..cad7505f4e 100644
--- a/tools/bookmaker/cataloger.cpp
+++ b/tools/bookmaker/cataloger.cpp
@@ -5,7 +5,8 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
+#include "bmhParser.h"
+#include "fiddleParser.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
diff --git a/tools/bookmaker/definition.cpp b/tools/bookmaker/definition.cpp
index fb96ed19a4..e6707f6c55 100644
--- a/tools/bookmaker/definition.cpp
+++ b/tools/bookmaker/definition.cpp
@@ -5,9 +5,11 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
#include "SkOSPath.h"
+#include "definition.h"
+#include "textParser.h"
+
#ifdef CONST
#undef CONST
#endif
diff --git a/tools/bookmaker/definition.h b/tools/bookmaker/definition.h
new file mode 100644
index 0000000000..632135b77b
--- /dev/null
+++ b/tools/bookmaker/definition.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef definition_DEFINED
+#define definition_DEFINED
+
+#include "textParser.h"
+
+class RootDefinition;
+class TextParser;
+
+class Definition : public NonAssignable {
+public:
+ enum Type {
+ kNone,
+ kWord,
+ kMark,
+ kKeyWord,
+ kBracket,
+ kPunctuation,
+ kFileType,
+ };
+
+ enum class MethodType {
+ kNone,
+ kConstructor,
+ kDestructor,
+ kOperator,
+ };
+
+ enum class Operator {
+ kUnknown,
+ kAdd,
+ kAddTo,
+ kArray,
+ kCast,
+ kCopy,
+ kDelete,
+ kDereference,
+ kEqual,
+ kMinus,
+ kMove,
+ kMultiply,
+ kMultiplyBy,
+ kNew,
+ kNotEqual,
+ kSubtract,
+ kSubtractFrom,
+ };
+
+ enum class Format {
+ kIncludeReturn,
+ kOmitReturn,
+ };
+
+ enum class Details {
+ kNone,
+ kSoonToBe_Deprecated,
+ kTestingOnly_Experiment,
+ kDoNotUse_Experiment,
+ kNotReady_Experiment,
+ };
+
+ enum class DetailsType {
+ kPhrase,
+ kSentence,
+ };
+
+ Definition() {}
+
+ Definition(const char* start, const char* end, int line, Definition* parent, char mc)
+ : fStart(start)
+ , fContentStart(start)
+ , fContentEnd(end)
+ , fParent(parent)
+ , fLineCount(line)
+ , fType(Type::kWord)
+ , fMC(mc) {
+ if (parent) {
+ SkASSERT(parent->fFileName.length() > 0);
+ fFileName = parent->fFileName;
+ }
+ this->setParentIndex();
+ }
+
+ Definition(MarkType markType, const char* start, int line, Definition* parent, char mc)
+ : Definition(markType, start, nullptr, line, parent, mc) {
+ }
+
+ Definition(MarkType markType, const char* start, const char* end, int line, Definition* parent, char mc)
+ : Definition(start, end, line, parent, mc) {
+ fMarkType = markType;
+ fType = Type::kMark;
+ }
+
+ Definition(Bracket bracket, const char* start, int lineCount, Definition* parent, char mc)
+ : Definition(start, nullptr, lineCount, parent, mc) {
+ fBracket = bracket;
+ fType = Type::kBracket;
+ }
+
+ Definition(KeyWord keyWord, const char* start, const char* end, int lineCount,
+ Definition* parent, char mc)
+ : Definition(start, end, lineCount, parent, mc) {
+ fKeyWord = keyWord;
+ fType = Type::kKeyWord;
+ }
+
+ Definition(Punctuation punctuation, const char* start, int lineCount, Definition* parent, char mc)
+ : Definition(start, nullptr, lineCount, parent, mc) {
+ fPunctuation = punctuation;
+ fType = Type::kPunctuation;
+ }
+
+ virtual ~Definition() {}
+
+ virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; }
+ bool boilerplateIfDef();
+
+ bool boilerplateEndIf() {
+ return true;
+ }
+
+ bool checkMethod() const;
+ bool crossCheck2(const Definition& includeToken) const;
+ bool crossCheck(const Definition& includeToken) const;
+ bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const;
+
+ Definition* csParent() {
+ Definition* test = fParent;
+ while (test) {
+ if (MarkType::kStruct == test->fMarkType || MarkType::kClass == test->fMarkType) {
+ return test;
+ }
+ test = test->fParent;
+ }
+ return nullptr;
+ }
+
+ string fiddleName() const;
+ string fileName() const;
+ const Definition* findClone(string match) const;
+ string formatFunction(Format format) const;
+ const Definition* hasChild(MarkType markType) const;
+ bool hasMatch(string name) const;
+ Definition* hasParam(string ref);
+ string incompleteMessage(DetailsType ) const;
+ bool isClone() const { return fClone; }
+
+ const Definition* iRootParent() const {
+ const Definition* test = fParent;
+ while (test) {
+ if (KeyWord::kClass == test->fKeyWord || KeyWord::kStruct == test->fKeyWord) {
+ return test;
+ }
+ test = test->fParent;
+ }
+ return nullptr;
+ }
+
+ virtual bool isRoot() const { return false; }
+ bool isStructOrClass() const;
+
+ int length() const {
+ return (int) (fContentEnd - fContentStart);
+ }
+
+ const char* methodEnd() const;
+ bool methodHasReturn(string name, TextParser* methodParser) const;
+ string methodName() const;
+ bool nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
+ string* paramName) const;
+ static string NormalizedName(string name);
+ bool paramsMatch(string fullRef, string name) const;
+ bool parseOperator(size_t doubleColons, string& result);
+
+ string printableName() const {
+ string result(fName);
+ std::replace(result.begin(), result.end(), '_', ' ');
+ return result;
+ }
+
+ template <typename T> T reportError(const char* errorStr) const {
+ TextParser tp(this);
+ tp.reportError(errorStr);
+ return T();
+ }
+
+ virtual RootDefinition* rootParent() { SkASSERT(0); return nullptr; }
+ virtual const RootDefinition* rootParent() const { SkASSERT(0); return nullptr; }
+ void setCanonicalFiddle();
+
+ void setParentIndex() {
+ fParentIndex = fParent ? (int) fParent->fTokens.size() : -1;
+ }
+
+ string simpleName() {
+ size_t doubleColon = fName.rfind("::");
+ return string::npos == doubleColon ? fName : fName.substr(doubleColon + 2);
+ }
+
+ const Definition* subtopicParent() const {
+ Definition* test = fParent;
+ while (test) {
+ if (MarkType::kTopic == test->fMarkType || MarkType::kSubtopic == test->fMarkType) {
+ return test;
+ }
+ test = test->fParent;
+ }
+ return nullptr;
+ }
+
+ const Definition* topicParent() const {
+ Definition* test = fParent;
+ while (test) {
+ if (MarkType::kTopic == test->fMarkType) {
+ return test;
+ }
+ test = test->fParent;
+ }
+ return nullptr;
+ }
+
+ void trimEnd();
+
+ string fText; // if text is constructed instead of in a file, it's put here
+ const char* fStart = nullptr; // .. in original text file, or the start of fText
+ const char* fContentStart; // start past optional markup name
+ string fName;
+ string fFiddle; // if its a constructor or operator, fiddle name goes here
+ string fCode; // suitable for autogeneration of #Code blocks in bmh
+ const char* fContentEnd = nullptr; // the end of the contained text
+ const char* fTerminator = nullptr; // the end of the markup, normally ##\n or \n
+ Definition* fParent = nullptr;
+ list<Definition> fTokens;
+ vector<Definition*> fChildren;
+ string fHash; // generated by fiddle
+ string fFileName;
+ mutable string fWrapper; // used by Example to wrap into proper function
+ size_t fLineCount = 0;
+ int fParentIndex = 0;
+ MarkType fMarkType = MarkType::kNone;
+ KeyWord fKeyWord = KeyWord::kNone;
+ Bracket fBracket = Bracket::kNone;
+ Punctuation fPunctuation = Punctuation::kNone;
+ MethodType fMethodType = MethodType::kNone;
+ Operator fOperator = Operator::kUnknown;
+ Type fType = Type::kNone;
+ char fMC = '#';
+ bool fClone = false;
+ bool fCloned = false;
+ bool fDeprecated = false;
+ bool fOperatorConst = false;
+ bool fPrivate = false;
+ Details fDetails = Details::kNone;
+ bool fMemberStart = false;
+ bool fAnonymous = false;
+ mutable bool fVisited = false;
+};
+
+class RootDefinition : public Definition {
+public:
+ enum class AllowParens {
+ kNo,
+ kYes,
+ };
+
+ struct SubtopicContents {
+ SubtopicContents()
+ : fShowClones(false) {
+ }
+
+ vector<Definition*> fMembers;
+ bool fShowClones;
+ };
+
+ RootDefinition() {
+ }
+
+ 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) {
+ }
+
+ ~RootDefinition() override {
+ for (auto& iter : fBranches) {
+ delete iter.second;
+ }
+ }
+
+ RootDefinition* asRoot() override { return this; }
+ void clearVisited();
+ bool dumpUnVisited();
+ Definition* find(string ref, AllowParens );
+ bool isRoot() const override { return true; }
+
+ SubtopicContents& populator(const char* key) {
+ return fPopulators[key];
+ }
+
+ RootDefinition* rootParent() override { return fRootParent; }
+ const RootDefinition* rootParent() const override { return fRootParent; }
+ void setRootParent(RootDefinition* rootParent) { fRootParent = rootParent; }
+
+ unordered_map<string, RootDefinition*> fBranches;
+ unordered_map<string, Definition> fLeaves;
+ unordered_map<string, SubtopicContents> fPopulators;
+ NameMap fNames;
+private:
+ RootDefinition* fRootParent = nullptr;
+};
+
+#endif
diff --git a/tools/bookmaker/fiddleParser.cpp b/tools/bookmaker/fiddleParser.cpp
index ec36e4b564..79c4a70252 100644
--- a/tools/bookmaker/fiddleParser.cpp
+++ b/tools/bookmaker/fiddleParser.cpp
@@ -5,7 +5,8 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
+#include "bmhParser.h"
+#include "fiddleParser.h"
// could make this more elaborate and look up the example definition in the bmh file;
// see if a simpler hint provided is sufficient
@@ -14,6 +15,10 @@ static bool report_error(const char* blockName, const char* errorMessage) {
return false;
}
+Definition* FiddleBase::findExample(string name) const {
+ return fBmhParser->findExample(name);
+}
+
bool FiddleBase::parseFiddles() {
if (fStack.empty()) {
return false;
@@ -86,6 +91,17 @@ bool FiddleBase::parseFiddles() {
return true;
}
+bool FiddleParser::parseFromFile(const char* path) {
+ if (!INHERITED::parseFromFile(path)) {
+ return false;
+ }
+ fBmhParser->resetExampleHashes();
+ if (!INHERITED::parseFiddles()) {
+ return false;
+ }
+ return fBmhParser->checkExampleHashes();
+}
+
bool FiddleParser::textOut(Definition* example, const char* stdOutStart,
const char* stdOutEnd) {
bool foundStdOut = false;
diff --git a/tools/bookmaker/fiddleParser.h b/tools/bookmaker/fiddleParser.h
new file mode 100644
index 0000000000..e6fa9a405d
--- /dev/null
+++ b/tools/bookmaker/fiddleParser.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef fiddleParser_DEFINED
+#define fiddleParser_DEFINED
+
+#include "parserCommon.h"
+
+class BmhParser;
+
+class FiddleBase : public JsonCommon {
+protected:
+ FiddleBase(BmhParser* bmh)
+ : fBmhParser(bmh)
+ , fContinuation(false)
+ , fTextOut(false)
+ , fPngOut(false)
+ {
+ this->reset();
+ }
+
+ void reset() override {
+ INHERITED::reset();
+ }
+
+ Definition* findExample(string name) const;
+ bool parseFiddles();
+ virtual bool pngOut(Definition* example) = 0;
+ virtual bool textOut(Definition* example, const char* stdOutStart,
+ const char* stdOutEnd) = 0;
+
+ BmhParser* fBmhParser; // must be writable; writes example hash
+ string fFullName;
+ bool fContinuation;
+ bool fTextOut;
+ bool fPngOut;
+private:
+ typedef JsonCommon INHERITED;
+};
+
+class FiddleParser : public FiddleBase {
+public:
+ FiddleParser(BmhParser* bmh) : FiddleBase(bmh) {
+ fTextOut = true;
+ }
+
+ bool parseFromFile(const char* path) override;
+
+private:
+ bool pngOut(Definition* example) override {
+ return true;
+ }
+
+ bool textOut(Definition* example, const char* stdOutStart,
+ const char* stdOutEnd) override;
+
+ typedef FiddleBase INHERITED;
+};
+
+class Catalog : public FiddleBase {
+public:
+ Catalog(BmhParser* bmh) : FiddleBase(bmh) {}
+
+ bool appendFile(string path);
+ bool closeCatalog(const char* outDir);
+ bool openCatalog(const char* inDir);
+ bool openStatus(const char* inDir);
+
+ bool parseFromFile(const char* path) override ;
+private:
+ bool pngOut(Definition* example) override;
+ bool textOut(Definition* example, const char* stdOutStart,
+ const char* stdOutEnd) override;
+
+ string fDocsDir;
+
+ typedef FiddleBase INHERITED;
+};
+
+#endif
diff --git a/tools/bookmaker/hackParser.cpp b/tools/bookmaker/hackParser.cpp
new file mode 100644
index 0000000000..03823a3f71
--- /dev/null
+++ b/tools/bookmaker/hackParser.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "bmhParser.h"
+
+ // 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;
+ while (len > 0 && (isalnum(filename[len]) || '_' == filename[len] || '.' == filename[len])) {
+ --len;
+ }
+ filename = filename.substr(len + 1);
+ if (filename.substr(0, 2) != "Sk") {
+ return true;
+ }
+ size_t under = filename.find('_');
+ SkASSERT(under);
+ string className = filename.substr(0, under);
+ fOut = fopen(filename.c_str(), "wb");
+ if (!fOut) {
+ SkDebugf("could not open output file %s\n", filename.c_str());
+ return false;
+ }
+ auto mapEntry = fBmhParser.fClassMap.find(className);
+ if (fBmhParser.fClassMap.end() == mapEntry) {
+ remove(filename.c_str());
+ return true;
+ }
+ const Definition* classMarkup = &mapEntry->second;
+ const Definition* root = classMarkup->fParent;
+ SkASSERT(root);
+ SkASSERT(root->fTerminator);
+ SkASSERT('\n' == root->fTerminator[0]);
+ SkASSERT(!root->fParent);
+ fStart = root->fStart;
+ fChar = fStart;
+ 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());
+ } else {
+ remove(filename.c_str());
+ }
+ return true;
+}
+
+// returns true if topic has method
+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);
+ }
+ if (MarkType::kMethod != child->fMarkType) {
+ continue;
+ }
+ 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;
+ }
+}
diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp
index 2fdd7ae5da..5662bcfdbc 100644
--- a/tools/bookmaker/includeParser.cpp
+++ b/tools/bookmaker/includeParser.cpp
@@ -5,10 +5,12 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
+#include "bmhParser.h"
+#include "includeParser.h"
+
const IncludeKey kKeyWords[] = {
{ "", KeyWord::kNone, KeyProperty::kNone },
{ "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
diff --git a/tools/bookmaker/includeParser.h b/tools/bookmaker/includeParser.h
new file mode 100644
index 0000000000..cd01687f5d
--- /dev/null
+++ b/tools/bookmaker/includeParser.h
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef includeParser_DEFINED
+#define includeParser_DEFINED
+
+#include "SkString.h"
+
+#include "parserCommon.h"
+
+class BmhParser;
+
+struct IClassDefinition : public Definition {
+ unordered_map<string, Definition*> fConsts;
+ unordered_map<string, Definition*> fDefines;
+ unordered_map<string, Definition*> fEnums;
+ unordered_map<string, Definition*> fMembers;
+ unordered_map<string, Definition*> fMethods;
+ unordered_map<string, Definition*> fStructs;
+ unordered_map<string, Definition*> fTypedefs;
+};
+
+class IncludeParser : public ParserCommon {
+public:
+ enum class IsStruct {
+ kNo,
+ kYes,
+ };
+
+ enum class Elided {
+ kNo,
+ kYes,
+ };
+
+ struct CheckCode {
+ enum class State {
+ kNone,
+ kClassDeclaration,
+ kConstructor,
+ kForwardDeclaration,
+ kMethod,
+ };
+
+ void reset() {
+ fInDebugCode = nullptr;
+ fPrivateBrace = 0;
+ fBraceCount = 0;
+ fIndent = 0;
+ fDoubleReturn = 0;
+ fState = State::kNone;
+ fPrivateProtected = false;
+ fTypedefReturn = false;
+ fSkipAPI = false;
+ fSkipInline = false;
+ fSkipWarnUnused = false;
+ fWriteReturn = false;
+ }
+
+ const char* fInDebugCode;
+ int fPrivateBrace;
+ int fBraceCount;
+ int fIndent;
+ int fDoubleReturn;
+ State fState;
+ bool fPrivateProtected;
+ bool fTypedefReturn;
+ bool fSkipAPI;
+ bool fSkipInline;
+ bool fSkipWarnUnused;
+ bool fWriteReturn;
+ };
+
+ IncludeParser() : ParserCommon()
+ , fMaps {
+ { &fIConstMap, MarkType::kConst }
+ , { &fIDefineMap, MarkType::kDefine }
+ , { &fIEnumMap, MarkType::kEnum }
+ , { &fIEnumMap, MarkType::kEnumClass }
+ , { &fIStructMap, MarkType::kStruct }
+ , { &fITemplateMap, MarkType::kTemplate }
+ , { &fITypedefMap, MarkType::kTypedef }
+ , { &fIUnionMap, MarkType::kUnion }
+ }
+ {
+ this->reset();
+ }
+
+ ~IncludeParser() override {}
+
+ void addKeyword(KeyWord keyWord);
+
+ void addPunctuation(Punctuation punctuation) {
+ fParent->fTokens.emplace_back(punctuation, fChar, fLineCount, fParent, '\0');
+ }
+
+ void addWord() {
+ fParent->fTokens.emplace_back(fIncludeWord, fChar, fLineCount, fParent, '\0');
+ fIncludeWord = nullptr;
+ }
+
+ bool advanceInclude(TextParser& i);
+ bool inAlignAs() const;
+ void checkForMissingParams(const vector<string>& methodParams,
+ const vector<string>& foundParams);
+ bool checkForWord();
+ string className() const;
+
+ string codeBlock(const Definition& def, bool inProgress) const {
+ return codeBlock(def.fMarkType, def.fName, inProgress);
+ }
+
+ string codeBlock(MarkType markType, string name, bool inProgress) const {
+ if (MarkType::kClass == markType || MarkType::kStruct == markType) {
+ auto map = fIClassMap.find(name);
+ SkASSERT(fIClassMap.end() != map || inProgress);
+ return fIClassMap.end() != map ? map->second.fCode : "";
+ }
+ if (MarkType::kConst == markType) {
+ auto map = fIConstMap.find(name);
+ SkASSERT(fIConstMap.end() != map);
+ return map->second->fCode;
+ }
+ if (MarkType::kDefine == markType) {
+ auto map = fIDefineMap.find(name);
+ SkASSERT(fIDefineMap.end() != map);
+ return map->second->fCode;
+ }
+ if (MarkType::kEnum == markType || MarkType::kEnumClass == markType) {
+ auto map = fIEnumMap.find(name);
+ SkASSERT(fIEnumMap.end() != map);
+ return map->second->fCode;
+ }
+ if (MarkType::kTypedef == markType) {
+ auto map = fITypedefMap.find(name);
+ SkASSERT(fITypedefMap.end() != map);
+ return map->second->fCode;
+ }
+ SkASSERT(0);
+ return "";
+ }
+
+ void codeBlockAppend(string& result, char ch) const;
+ void codeBlockSpaces(string& result, int indent) const;
+
+ bool crossCheck(BmhParser& );
+ IClassDefinition* defineClass(const Definition& includeDef, string className);
+ void dumpClassTokens(IClassDefinition& classDef);
+ void dumpComment(const Definition& );
+ void dumpCommonTail(const Definition& );
+ void dumpConst(const Definition& , string className);
+ void dumpDefine(const Definition& );
+ void dumpEnum(const Definition& , string name);
+ bool dumpGlobals(string* globalFileName, long int* globalTell);
+ void dumpMethod(const Definition& , string className);
+ void dumpMember(const Definition& );
+ bool dumpTokens();
+ bool dumpTokens(string skClassName, string globalFileName, long int* globalTell);
+ void dumpTypedef(const Definition& , string className);
+
+ string elidedCodeBlock(const Definition& );
+ string filteredBlock(string inContents, string filterContents);
+ bool findComments(const Definition& includeDef, Definition* markupDef);
+ Definition* findIncludeObject(const Definition& includeDef, MarkType markType,
+ string typeName);
+ static KeyWord FindKey(const char* start, const char* end);
+ Definition* findMethod(const Definition& bmhDef);
+ Bracket grandParentBracket() const;
+ const Definition* include(string ) const;
+ bool isClone(const Definition& token);
+ bool isConstructor(const Definition& token, string className);
+ bool isInternalName(const Definition& token);
+ bool isMember(const Definition& token) const;
+ bool isOperator(const Definition& token);
+ Definition* parentBracket(Definition* parent) const;
+ bool parseChar();
+ bool parseComment(string filename, const char* start, const char* end, int lineCount,
+ Definition* markupDef);
+ bool parseClass(Definition* def, IsStruct);
+ bool parseConst(Definition* child, Definition* markupDef);
+ bool parseDefine(Definition* child, Definition* markupDef);
+ bool parseEnum(Definition* child, Definition* markupDef);
+
+ bool parseFromFile(const char* path) override {
+ this->reset();
+ if (!INHERITED::parseSetup(path)) {
+ return false;
+ }
+ string name(path);
+ return this->parseInclude(name);
+ }
+
+ bool parseInclude(string name);
+ bool parseMember(Definition* child, Definition* markupDef);
+ bool parseMethod(Definition* child, Definition* markupDef);
+ bool parseObject(Definition* child, Definition* markupDef);
+ bool parseObjects(Definition* parent, Definition* markupDef);
+ bool parseTemplate(Definition* child, Definition* markupDef);
+ bool parseTypedef(Definition* child, Definition* markupDef);
+ bool parseUsing();
+ bool parseUnion();
+
+ void popBracket() {
+ if (Definition::Type::kKeyWord == fParent->fType
+ && KeyWord::kTypename == fParent->fKeyWord) {
+ this->popObject();
+ }
+ SkASSERT(Definition::Type::kBracket == fParent->fType);
+ this->popObject();
+ Bracket bracket = this->topBracket();
+ this->setBracketShortCuts(bracket);
+ }
+
+ void pushBracket(Bracket bracket) {
+ this->setBracketShortCuts(bracket);
+ fParent->fTokens.emplace_back(bracket, fChar, fLineCount, fParent, '\0');
+ Definition* container = &fParent->fTokens.back();
+ this->addDefinition(container);
+ }
+
+ bool references(const SkString& file) const;
+
+ static void RemoveFile(const char* docs, const char* includes);
+ static void RemoveOneFile(const char* docs, const char* includesFileOrPath);
+
+ void reset() override {
+ INHERITED::resetCommon();
+ fRootTopic = nullptr;
+ fConstExpr = nullptr;
+ fInBrace = nullptr;
+ fIncludeWord = nullptr;
+ fLastObject = nullptr;
+ fPriorEnum = nullptr;
+ fPriorObject = nullptr;
+ fPrev = '\0';
+ fInChar = false;
+ fInCharCommentString = false;
+ fInComment = false;
+ fInDefine = false;
+ fInEnum = false;
+ fInFunction = false;
+ fInString = false;
+ fFailed = false;
+ }
+
+ void setBracketShortCuts(Bracket bracket) {
+ fInComment = Bracket::kSlashSlash == bracket || Bracket::kSlashStar == bracket;
+ fInString = Bracket::kString == bracket;
+ fInChar = Bracket::kChar == bracket;
+ fInCharCommentString = fInChar || fInComment || fInString;
+ }
+
+ Bracket topBracket() const;
+
+ template <typename T>
+ string uniqueName(const unordered_map<string, T>& m, string typeName) {
+ string base(typeName.size() > 0 ? typeName : "_anonymous");
+ string name(base);
+ int anonCount = 1;
+ do {
+ auto iter = m.find(name);
+ if (iter == m.end()) {
+ return name;
+ }
+ name = base + '_';
+ name += to_string(++anonCount);
+ } while (true);
+ // should never get here
+ return string();
+ }
+
+ void validate() const;
+ void writeCodeBlock();
+ string writeCodeBlock(const Definition&, MarkType );
+ string writeCodeBlock(TextParser& i, MarkType , int indent);
+
+ void writeDefinition(const Definition& def) {
+ if (def.length() > 1) {
+ this->writeBlock((int) (def.fContentEnd - def.fContentStart), def.fContentStart);
+ this->lf(1);
+ }
+ }
+
+ void writeDefinition(const Definition& def, string name, int spaces) {
+ this->writeBlock((int) (def.fContentEnd - def.fContentStart), def.fContentStart);
+ this->writeSpace(spaces);
+ this->writeString(name);
+ this->lf(1);
+ }
+
+ void writeEndTag() {
+ this->lf(1);
+ this->writeString("##");
+ this->lf(1);
+ }
+
+ void writeEndTag(const char* tagType) {
+ this->lf(1);
+ this->writeString(string("#") + tagType + " ##");
+ this->lf(1);
+ }
+
+ void writeEndTag(const char* tagType, const char* tagID, int spaces = 1) {
+ this->lf(1);
+ this->writeString(string("#") + tagType + " " + tagID);
+ this->writeSpace(spaces);
+ this->writeString("##");
+ this->lf(1);
+ }
+
+ void writeEndTag(const char* tagType, string tagID, int spaces = 1) {
+ this->writeEndTag(tagType, tagID.c_str(), spaces);
+ }
+
+ void writeIncompleteTag(const char* tagType, string tagID, int spaces = 1) {
+ this->writeString(string("#") + tagType + " " + tagID);
+ this->writeSpace(spaces);
+ this->writeString("incomplete");
+ this->writeSpace();
+ this->writeString("##");
+ this->lf(1);
+ }
+
+ void writeIncompleteTag(const char* tagType) {
+ this->writeString(string("#") + tagType + " incomplete ##");
+ this->lf(1);
+ }
+
+ void writeTableHeader(const char* col1, size_t pad, const char* col2) {
+ this->lf(1);
+ this->writeString("#Table");
+ this->lf(1);
+ this->writeString("#Legend");
+ this->lf(1);
+ string legend = "# ";
+ legend += col1;
+ if (pad > strlen(col1)) {
+ legend += string(pad - strlen(col1), ' ');
+ }
+ legend += " # ";
+ legend += col2;
+ legend += " ##";
+ this->writeString(legend);
+ this->lf(1);
+ this->writeString("#Legend ##");
+ this->lf(1);
+ }
+
+ void writeTableRow(size_t pad, string col1) {
+ this->lf(1);
+ string row = "# " + col1 + string(pad - col1.length(), ' ') + " # ##";
+ this->writeString(row);
+ this->lf(1);
+ }
+
+ void writeTableRow(size_t pad1, string col1, size_t pad2, string col2) {
+ this->lf(1);
+ string row = "# " + col1 + string(pad1 - col1.length(), ' ') + " # " +
+ col2 + string(pad2 - col2.length(), ' ') + " ##";
+ this->writeString(row);
+ this->lf(1);
+ }
+
+ void writeTableTrailer() {
+ this->lf(1);
+ this->writeString("#Table ##");
+ this->lf(1);
+ }
+
+ void writeTag(const char* tagType) {
+ this->lf(1);
+ this->writeString("#");
+ this->writeString(tagType);
+ }
+
+ void writeTagNoLF(const char* tagType, const char* tagID) {
+ this->writeString("#");
+ this->writeString(tagType);
+ this->writeSpace();
+ this->writeString(tagID);
+ }
+
+ void writeTagNoLF(const char* tagType, string tagID) {
+ this->writeTagNoLF(tagType, tagID.c_str());
+ }
+
+ void writeTag(const char* tagType, const char* tagID) {
+ this->lf(1);
+ this->writeTagNoLF(tagType, tagID);
+ }
+
+ void writeTag(const char* tagType, string tagID) {
+ this->writeTag(tagType, tagID.c_str());
+ }
+
+ void writeTagTable(string tagType, string body) {
+ this->writeTag(tagType.c_str());
+ this->writeSpace(1);
+ this->writeString("#");
+ this->writeSpace(1);
+ this->writeString(body);
+ this->writeSpace(1);
+ this->writeString("##");
+ }
+
+protected:
+ static void ValidateKeyWords();
+
+ struct DefinitionMap {
+ unordered_map<string, Definition*>* fInclude;
+ MarkType fMarkType;
+ };
+
+ vector<DefinitionMap> fMaps;
+ unordered_map<string, Definition> fIncludeMap;
+ list<Definition> fGlobals;
+ unordered_map<string, IClassDefinition> fIClassMap;
+ unordered_map<string, Definition*> fIConstMap;
+ unordered_map<string, Definition*> fIDefineMap;
+ unordered_map<string, Definition*> fIEnumMap;
+ unordered_map<string, Definition*> fIFunctionMap;
+ unordered_map<string, Definition*> fIStructMap;
+ unordered_map<string, Definition*> fITemplateMap;
+ unordered_map<string, Definition*> fITypedefMap;
+ unordered_map<string, Definition*> fIUnionMap;
+ CheckCode fCheck;
+ Definition* fRootTopic;
+ Definition* fConstExpr;
+ Definition* fInBrace;
+ Definition* fLastObject;
+ Definition* fPriorEnum;
+ Definition* fPriorObject;
+ int fPriorIndex;
+ const char* fIncludeWord;
+ Elided fElided;
+ char fPrev;
+ bool fInChar;
+ bool fInCharCommentString;
+ bool fInComment;
+ bool fInDefine;
+ bool fInEnum;
+ bool fInFunction;
+ bool fInString;
+ bool fFailed;
+
+ typedef ParserCommon INHERITED;
+};
+
+#endif
diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp
index ef854a72aa..17336bf772 100644
--- a/tools/bookmaker/includeWriter.cpp
+++ b/tools/bookmaker/includeWriter.cpp
@@ -5,10 +5,11 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
#include <chrono>
#include <ctime>
-#include <string>
+
+#include "bmhParser.h"
+#include "includeWriter.h"
bool IncludeWriter::checkChildCommentLength(const Definition* parent, MarkType childType) const {
bool oneMember = false;
diff --git a/tools/bookmaker/includeWriter.h b/tools/bookmaker/includeWriter.h
new file mode 100644
index 0000000000..6271ac7425
--- /dev/null
+++ b/tools/bookmaker/includeWriter.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef includeWriter_DEFINED
+#define includeWriter_DEFINED
+
+#include "includeParser.h"
+
+class IncludeWriter : public IncludeParser {
+public:
+ enum class Word {
+ kStart,
+ kCap,
+ kFirst,
+ kUnderline,
+ kMixed,
+ };
+
+ enum class Phrase {
+ kNo,
+ kYes,
+ };
+
+ enum class PunctuationState {
+ kStart,
+ kDelimiter,
+ kParen, // treated as a delimiter unless following a space, and followed by word
+ kPeriod,
+ kSpace,
+ };
+
+ enum class RefType {
+ kUndefined,
+ kNormal,
+ kExternal,
+ };
+
+ enum class SkipFirstLine {
+ kNo,
+ kYes,
+ };
+
+ enum class Wrote {
+ kNone,
+ kLF,
+ kChars,
+ };
+
+ enum class MemberPass {
+ kCount,
+ kOut,
+ };
+
+ enum class ItemState {
+ kNone,
+ kName,
+ kValue,
+ kComment,
+ };
+
+ struct IterState {
+ IterState (list<Definition>::iterator tIter, list<Definition>::iterator tIterEnd)
+ : fDefIter(tIter)
+ , fDefEnd(tIterEnd) {
+ }
+ list<Definition>::iterator fDefIter;
+ list<Definition>::iterator fDefEnd;
+ };
+
+ struct ParentPair {
+ const Definition* fParent;
+ const ParentPair* fPrev;
+ };
+
+ struct Preprocessor {
+ Preprocessor() {
+ reset();
+ }
+
+ void reset() {
+ fDefinition = nullptr;
+ fStart = nullptr;
+ fEnd = nullptr;
+ fWord = false;
+ }
+
+ const Definition* fDefinition;
+ const char* fStart;
+ const char* fEnd;
+ bool fWord;
+ };
+
+ struct Item {
+ void reset() {
+ fName = "";
+ fValue = "";
+ }
+
+ string fName;
+ string fValue;
+ };
+
+ struct LastItem {
+ const char* fStart;
+ const char* fEnd;
+ };
+
+ struct ItemLength {
+ int fCurName;
+ int fCurValue;
+ int fLongestName;
+ int fLongestValue;
+ };
+
+ IncludeWriter() : IncludeParser() {
+ this->reset();
+ }
+
+ ~IncludeWriter() override {}
+
+ bool contentFree(int size, const char* data) const {
+ while (size > 0 && data[0] <= ' ') {
+ --size;
+ ++data;
+ }
+ while (size > 0 && data[size - 1] <= ' ') {
+ --size;
+ }
+ return 0 == size;
+ }
+
+ 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* bmhConst);
+ void constSizeMembers(const RootDefinition* root);
+ bool defineOut(const Definition& );
+ 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;
+ ItemState enumMemberName(const Definition& child,
+ const Definition* token, Item* , LastItem* , const Definition** enumItem);
+ void enumMemberOut(const Definition* currentEnumItem, const Definition& child,
+ const Item& , Preprocessor& );
+ void enumMembersOut(Definition& child);
+ bool enumPreprocessor(Definition* token, MemberPass pass,
+ vector<IterState>& iterStack, IterState** iterState, Preprocessor* );
+ void enumSizeItems(const Definition& child);
+ bool findEnumSubtopic(string undername, const Definition** ) const;
+ void firstBlock(int size, const char* data);
+ bool firstBlockTrim(int size, const char* data);
+ Definition* findMemberCommentBlock(const vector<Definition*>& bmhChildren, string name) const;
+ Definition* findMethod(string name, RootDefinition* ) const;
+
+ void indentDeferred(IndentKind kind) {
+ if (fIndentNext) {
+ this->indentIn(kind);
+ fIndentNext = false;
+ }
+ }
+
+ int lookupMethod(const PunctuationState punctuation, const Word word,
+ const int start, const int run, int lastWrite,
+ const char* data, bool hasIndirection);
+ int lookupReference(const PunctuationState punctuation, const Word word,
+ const int start, const int run, int lastWrite, const char last,
+ const char* data);
+ const Definition* matchMemberName(string matchName, const Definition& child) const;
+ void methodOut(Definition* method, const Definition& child);
+ bool populate(Definition* def, ParentPair* parentPair, RootDefinition* root);
+ bool populate(BmhParser& bmhParser);
+
+ void reset() override {
+ INHERITED::resetCommon();
+ fBmhParser = nullptr;
+ fDeferComment = nullptr;
+ fBmhMethod = nullptr;
+ fEnumDef = nullptr;
+ fMethodDef = nullptr;
+ fBmhConst = nullptr;
+ fConstDef = nullptr;
+ fLastDescription = nullptr;
+ fStartSetter = nullptr;
+ fBmhStructDef = nullptr;
+ fContinuation = nullptr;
+ fInStruct = false;
+ fWroteMethod = false;
+ fIndentNext = false;
+ fPendingMethod = false;
+ fFirstWrite = false;
+ fStructEnded = false;
+ fWritingIncludes = true;
+ }
+
+ 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);
+ void setStart(const char* start, const Definition * );
+ void setStartBack(const char* start, const Definition * );
+ Definition* structMemberOut(const Definition* memberStart, const Definition& child);
+ void structOut(const Definition* root, const Definition& child,
+ const char* commentStart, const char* commentEnd);
+ void structSizeMembers(const Definition& child);
+ bool writeHeader(std::pair<const string, Definition>& );
+private:
+ vector<const Definition* > fICSStack;
+ BmhParser* fBmhParser;
+ Definition* fDeferComment;
+ const Definition* fBmhMethod;
+ const Definition* fEnumDef;
+ const Definition* fMethodDef;
+ const Definition* fBmhConst;
+ const Definition* fConstDef;
+ const Definition* fLastDescription;
+ const Definition* fStartSetter;
+ Definition* fBmhStructDef;
+ const char* fContinuation; // used to construct paren-qualified method name
+ int fAnonymousEnumCount;
+ int fEnumItemValueTab;
+ int fEnumItemCommentTab;
+ int fStructMemberTab;
+ int fStructValueTab;
+ int fStructCommentTab;
+ int fStructMemberLength;
+ int fConstValueTab;
+ int fConstCommentTab;
+ int fConstLength;
+ bool fInStruct; // set if struct is inside class
+ bool fWroteMethod;
+ bool fIndentNext;
+ bool fPendingMethod;
+ bool fFirstWrite; // set to write file information just after includes
+ bool fStructEnded; // allow resetting indent after struct is complete
+
+ typedef IncludeParser INHERITED;
+};
+
+#endif
diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp
index 22b830b69d..49f8b86ecf 100644
--- a/tools/bookmaker/mdOut.cpp
+++ b/tools/bookmaker/mdOut.cpp
@@ -5,11 +5,30 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
+#include "bmhParser.h"
+#include "includeParser.h"
+#include "mdOut.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
+class SubtopicKeys {
+public:
+ static constexpr const char* kClasses = "Classes";
+ static constexpr const char* kConstants = "Constants";
+ static constexpr const char* kConstructors = "Constructors";
+ static constexpr const char* kDefines = "Defines";
+ static constexpr const char* kMemberFunctions = "Member_Functions";
+ static constexpr const char* kMembers = "Members";
+ static constexpr const char* kOperators = "Operators";
+ static constexpr const char* kOverview = "Overview";
+ static constexpr const char* kRelatedFunctions = "Related_Functions";
+ static constexpr const char* kStructs = "Structs";
+ static constexpr const char* kTypedefs = "Typedefs";
+
+ static const char* kGeneratedSubtopics[];
+};
+
const char* SubtopicKeys::kGeneratedSubtopics[] = {
kConstants, kDefines, kTypedefs, kMembers, kClasses, kStructs, kConstructors,
kOperators, kMemberFunctions, kRelatedFunctions
@@ -257,9 +276,9 @@ Definition* MdOut::checkParentsForMatch(Definition* test, string ref) const {
return nullptr;
}
-static bool formula_or_code(BmhParser::Resolvable resolvable) {
- return BmhParser::Resolvable::kFormula == resolvable
- || BmhParser::Resolvable::kCode == resolvable;
+static bool formula_or_code(Resolvable resolvable) {
+ return Resolvable::kFormula == resolvable
+ || Resolvable::kCode == resolvable;
}
static void fixup_const_function_name(string* ref) {
@@ -507,8 +526,8 @@ string MdOut::addIncludeReferences(const char* refStart, const char* refEnd) {
// 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
+ Resolvable resolvable) {
+ if (Resolvable::kInclude == resolvable) { // test include resolving
return this->addIncludeReferences(refStart, refEnd);
}
string result;
@@ -522,7 +541,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
vector<BraceState> braceStack;
KeyWord lastKeyWord = KeyWord::kNone;
KeyWord keyWord = KeyWord::kNone;
- if (BmhParser::Resolvable::kCode == resolvable) {
+ if (Resolvable::kCode == resolvable) {
braceStack.push_back({fRoot, fRoot->fName, t.fChar, lastKeyWord, keyWord, 0});
}
do {
@@ -536,7 +555,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
t.next();
continue;
}
- if (BmhParser::Resolvable::kCode == resolvable) {
+ if (Resolvable::kCode == resolvable) {
int priorBrace = braceStack.back().fBraceCount;
int braceCount = priorBrace + t.skipToMethodStart();
if (braceCount > priorBrace) {
@@ -590,7 +609,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
wordStart = base;
}
string nonWord = string(wordStart, start - wordStart);
- if (BmhParser::Resolvable::kFormula == resolvable) {
+ if (Resolvable::kFormula == resolvable) {
string unbreakable;
bool comma = false;
for (char c : nonWord) {
@@ -642,7 +661,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
result += ref;
continue;
}
- if (BmhParser::Resolvable::kCode == resolvable) {
+ if (Resolvable::kCode == resolvable) {
fixup_const_function_name(&ref);
}
if (Definition* def = this->isDefined(t, ref, resolvable)) {
@@ -652,8 +671,8 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
continue;
}
SkASSERT(def->fFiddle.length());
- if (BmhParser::Resolvable::kSimple != resolvable
- && BmhParser::Resolvable::kInclude != resolvable
+ if (Resolvable::kSimple != resolvable
+ && Resolvable::kInclude != resolvable
&& !t.eof() && '(' == t.peek() && t.strnchr(')', t.fEnd)) {
TextParserSave tSave(&t);
if (!t.skipToBalancedEndBracket('(', ')')) {
@@ -680,7 +699,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
}
string altTest = ref + '_';
altTest += suffix++;
- altDef = this->isDefined(t, altTest, BmhParser::Resolvable::kOut);
+ altDef = this->isDefined(t, altTest, Resolvable::kOut);
}
if (suffix > '9') {
t.reportError("too many alts");
@@ -705,7 +724,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
}
ref = ref.substr(0, ref.find('('));
tSave.restore();
- } else if (BmhParser::Resolvable::kClone != resolvable &&
+ } else if (Resolvable::kClone != resolvable &&
all_lower(ref) && (t.eof() || '(' != t.peek())) {
add_ref(leadingSpaces, ref, &result);
continue;
@@ -717,7 +736,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
}
result += linkRef(leadingSpaces, def, ref, resolvable);
if (!t.eof() && '(' == t.peek()) {
- if (BmhParser::Resolvable::kInclude == resolvable
+ if (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
@@ -735,7 +754,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
}
t.next();
ref = string(start, t.fChar - start);
- if (Definition* def = this->isDefined(t, ref, BmhParser::Resolvable::kYes)) {
+ if (Definition* def = this->isDefined(t, ref, Resolvable::kYes)) {
SkASSERT(def->fFiddle.length());
result += linkRef(leadingSpaces, def, ref, resolvable);
continue;
@@ -749,14 +768,14 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
// look for Sk / sk / SK ..
if (!ref.compare(0, 2, "Sk") && ref != "Skew" && ref != "Skews" && ref != "Skewing"
&& ref != "Skip" && ref != "Skips") {
- if (BmhParser::Resolvable::kOut != resolvable && !formula_or_code(resolvable)) {
+ if (Resolvable::kOut != resolvable && !formula_or_code(resolvable)) {
t.reportError("missed Sk prefixed");
fAddRefFailed = true;
return result;
}
}
if (!ref.compare(0, 2, "SK")) {
- if (BmhParser::Resolvable::kOut != resolvable && !formula_or_code(resolvable)) {
+ if (Resolvable::kOut != resolvable && !formula_or_code(resolvable)) {
t.reportError("missed SK prefixed");
fAddRefFailed = true;
return result;
@@ -788,9 +807,9 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
}
}
}
- if (BmhParser::Resolvable::kSimple != resolvable
- && BmhParser::Resolvable::kInclude != resolvable
- && BmhParser::Resolvable::kOut != resolvable
+ if (Resolvable::kSimple != resolvable
+ && Resolvable::kInclude != resolvable
+ && Resolvable::kOut != resolvable
&& !formula_or_code(resolvable)) {
t.reportError("missed camelCase");
fAddRefFailed = true;
@@ -804,7 +823,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
&& KeyWord::kPublic != newKeyWord) {
lastKeyWord = keyWord;
keyWord = newKeyWord;
- } else if (BmhParser::Resolvable::kCode == resolvable && ':' != t.peek()) {
+ } else if (Resolvable::kCode == resolvable && ':' != t.peek()) {
lastLine = t.fLine;
lastKeyWord = keyWord;
keyWord = newKeyWord;
@@ -829,7 +848,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
if (isupper(t.fChar[1]) && startsSentence) {
TextParser next(t.fFileName, &t.fChar[1], t.fEnd, t.fLineCount);
string nextWord(next.fChar, next.wordEnd() - next.fChar);
- if (this->isDefined(t, nextWord, BmhParser::Resolvable::kYes)) {
+ if (this->isDefined(t, nextWord, Resolvable::kYes)) {
add_ref(leadingSpaces, ref, &result);
continue;
}
@@ -842,7 +861,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
result += this->linkRef(leadingSpaces, def, ref, resolvable);
continue;
}
- if (BmhParser::Resolvable::kOut != resolvable &&
+ if (Resolvable::kOut != resolvable &&
!formula_or_code(resolvable)) {
t.reportError("undefined reference");
fAddRefFailed = true;
@@ -1063,7 +1082,7 @@ bool MdOut::checkParamReturnBody(const Definition* def) {
if (!islower(descriptionStart[0]) && !isdigit(descriptionStart[0])) {
paramBody.skipToNonName();
string ref = string(descriptionStart, paramBody.fChar - descriptionStart);
- if (!this->isDefined(paramBody, ref, BmhParser::Resolvable::kYes)) {
+ if (!this->isDefined(paramBody, ref, Resolvable::kYes)) {
string errorStr = MarkType::kReturn == def->fMarkType ? "return" : "param";
errorStr += " description must start with lower case";
paramBody.reportError(errorStr.c_str());
@@ -1088,20 +1107,20 @@ void MdOut::childrenOut(Definition* def, const char* start) {
if (MarkType::kEnumClass == def->fMarkType) {
fEnumClass = def;
}
- BmhParser::Resolvable resolvable = this->resolvable(def);
+ Resolvable resolvable = this->resolvable(def);
const Definition* prior = nullptr;
for (auto& child : def->fChildren) {
if (MarkType::kPhraseParam == child->fMarkType) {
continue;
}
end = child->fStart;
- if (BmhParser::Resolvable::kNo != resolvable) {
+ if (Resolvable::kNo != resolvable) {
this->resolveOut(start, end, resolvable);
}
this->markTypeOut(child, &prior);
start = child->fTerminator;
}
- if (BmhParser::Resolvable::kNo != resolvable) {
+ if (Resolvable::kNo != resolvable) {
end = def->fContentEnd;
if (MarkType::kFormula == def->fMarkType && ' ' == start[0]) {
this->writeSpace();
@@ -1226,7 +1245,7 @@ Definition* MdOut::findParamType() {
string name = string(word, parser.fChar - word);
if (fLastParam->fName == name) {
Definition* paramType = this->isDefined(parser, lastFull,
- BmhParser::Resolvable::kOut);
+ Resolvable::kOut);
return paramType;
}
if (isupper(name[0])) {
@@ -1337,7 +1356,7 @@ Definition* MdOut::isDefinedByParent(RootDefinition* root, string ref) {
}
Definition* MdOut::isDefined(const TextParser& parser, string ref,
- BmhParser::Resolvable resolvable) {
+ Resolvable resolvable) {
auto rootIter = fBmhParser.fClassMap.find(ref);
if (rootIter != fBmhParser.fClassMap.end()) {
return &rootIter->second;
@@ -1423,7 +1442,7 @@ Definition* MdOut::isDefined(const TextParser& parser, string ref,
return nullptr;
}
} else {
- if (BmhParser::Resolvable::kOut != resolvable &&
+ if (Resolvable::kOut != resolvable &&
!formula_or_code(resolvable)) {
parser.reportError("SK undefined");
fAddRefFailed = true;
@@ -1454,7 +1473,7 @@ Definition* MdOut::isDefined(const TextParser& parser, string ref,
return definition;
}
}
- if (BmhParser::Resolvable::kOut != resolvable &&
+ if (Resolvable::kOut != resolvable &&
!formula_or_code(resolvable)) {
parser.reportError("_ undefined");
fAddRefFailed = true;
@@ -1483,8 +1502,8 @@ string MdOut::linkName(const Definition* ref) const {
// for now, hard-code to html links
// 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);
+ string ref, Resolvable resolvable) {
+ bool trimRef = Resolvable::kInclude == resolvable && "Sk" == ref.substr(0, 2);
if (trimRef) {
#ifdef SK_DEBUG
for (auto c : ref) {
@@ -1576,15 +1595,15 @@ string MdOut::linkRef(string leadingSpaces, Definition* def,
buildup = '#' + parent->fFiddle + '_' + ref;
}
string refOut(ref);
- if (!globalEnumMember && BmhParser::Resolvable::kCode != resolvable) {
+ if (!globalEnumMember && Resolvable::kCode != resolvable) {
std::replace(refOut.begin(), refOut.end(), '_', ' ');
}
if (ref.length() > 2 && islower(ref[0]) && "()" == ref.substr(ref.length() - 2)
- && BmhParser::Resolvable::kCode != resolvable) {
+ && Resolvable::kCode != resolvable) {
refOut = refOut.substr(0, refOut.length() - 2);
}
string result = leadingSpaces + this->anchorRef(buildup, refOut);
- if (BmhParser::Resolvable::kClone == resolvable && MarkType::kMethod == def->fMarkType &&
+ if (Resolvable::kClone == resolvable && MarkType::kMethod == def->fMarkType &&
def->fCloned && !def->fClone) {
bool found = false;
string match = def->fName;
@@ -2015,7 +2034,7 @@ void MdOut::markTypeOut(Definition* def, const Definition** prior) {
string formattedStr = def->formatFunction(Definition::Format::kIncludeReturn);
string preformattedStr = preformat(formattedStr);
string references = this->addReferences(&preformattedStr.front(),
- &preformattedStr.back() + 1, BmhParser::Resolvable::kSimple);
+ &preformattedStr.back() + 1, Resolvable::kSimple);
preformattedStr = references;
this->htmlOut("<pre style=\"padding: 1em 1em 1em 1em; width: 62.5em;"
"background-color: #f0f0f0\">\n" + preformattedStr + "\n" + "</pre>");
@@ -2208,7 +2227,7 @@ void MdOut::markTypeOut(Definition* def, const Definition** prior) {
if (parser.skipExact("@param ")) { // write parameters, if any
this->parameterHeaderOut(parser, prior, def);
this->resolveOut(parser.fChar, parser.fEnd,
- BmhParser::Resolvable::kInclude);
+ Resolvable::kInclude);
this->parameterTrailerOut();
wroteParam = true;
continue;
@@ -2223,7 +2242,7 @@ void MdOut::markTypeOut(Definition* def, const Definition** prior) {
if (parser.skipExact("@return ")) { // write return, if any
this->returnHeaderOut(prior, def);
this->resolveOut(parser.fChar, parser.fEnd,
- BmhParser::Resolvable::kInclude);
+ Resolvable::kInclude);
this->lf(2);
continue;
}
@@ -2240,7 +2259,7 @@ void MdOut::markTypeOut(Definition* def, const Definition** prior) {
this->lf(2);
}
this->resolveOut(entry.fContentStart, entry.fContentEnd,
- BmhParser::Resolvable::kInclude); // write description
+ Resolvable::kInclude); // write description
this->lf(1);
}
fMethod = nullptr;
@@ -2332,7 +2351,7 @@ void MdOut::markTypeOut(Definition* def, const Definition** prior) {
this->writePending();
this->htmlOut("<code>");
this->resolveOut(def->fContentStart, def->fContentEnd,
- BmhParser::Resolvable::kFormula);
+ Resolvable::kFormula);
this->htmlOut("</code>");
}
break;
@@ -2665,8 +2684,8 @@ void MdOut::populateTables(const Definition* def, RootDefinition* root) {
}
}
-void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable resolvable) {
- if ((BmhParser::Resolvable::kLiteral == resolvable || fLiteralAndIndent ||
+void MdOut::resolveOut(const char* start, const char* end, Resolvable resolvable) {
+ if ((Resolvable::kLiteral == resolvable || fLiteralAndIndent ||
fResolveAndIndent) && end > start) {
int linefeeds = 0;
while ('\n' == *start) {
@@ -2684,7 +2703,7 @@ void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable
fIndent = start - spaceStart;
}
}
- if (BmhParser::Resolvable::kLiteral == resolvable || fLiteralAndIndent) {
+ if (Resolvable::kLiteral == resolvable || fLiteralAndIndent) {
this->writeBlockTrim(end - start, start);
if ('\n' == end[-1]) {
this->lf(1);
@@ -2784,12 +2803,12 @@ void MdOut::rowOut(const char* name, string description, bool literalName) {
this->writeString(name);
}
} else {
- this->resolveOut(name, name + strlen(name), BmhParser::Resolvable::kYes);
+ this->resolveOut(name, name + strlen(name), Resolvable::kYes);
}
FPRINTF("</td>");
this->lfAlways(1);
FPRINTF("%s", kTD_Left.c_str());
- this->resolveOut(&description.front(), &description.back() + 1, BmhParser::Resolvable::kYes);
+ this->resolveOut(&description.front(), &description.back() + 1, Resolvable::kYes);
FPRINTF("</td>");
this->lfAlways(1);
FPRINTF(" </tr>");
@@ -2943,7 +2962,7 @@ bool MdOut::subtopicRowOut(string keyName, const Definition* entry) {
return parser.reportError<bool>("missing #Line");
}
TextParser dummy(entry); // for reporting errors, which we won't do
- if (!this->isDefined(dummy, keyName, BmhParser::Resolvable::kOut)) {
+ if (!this->isDefined(dummy, keyName, Resolvable::kOut)) {
keyName = entry->fName;
size_t doubleColon = keyName.find("::");
SkASSERT(string::npos != doubleColon);
diff --git a/tools/bookmaker/mdOut.h b/tools/bookmaker/mdOut.h
new file mode 100644
index 0000000000..30b48d8e55
--- /dev/null
+++ b/tools/bookmaker/mdOut.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef mdOut_DEFINED
+#define mdOut_DEFINED
+
+#include "parserCommon.h"
+
+class IncludeParser;
+
+class MdOut : public ParserCommon {
+public:
+ struct SubtopicDescriptions {
+ string fSingular;
+ string fPlural;
+ string fOneLiner;
+ string fDetails;
+ };
+
+ MdOut(BmhParser& bmh, IncludeParser& inc) : ParserCommon()
+ , fBmhParser(bmh)
+ , fIncludeParser(inc) {
+ this->reset();
+ this->addPopulators();
+ fBmhParser.setUpGlobalSubstitutes();
+ }
+
+ bool buildReferences(const char* docDir, const char* mdOutDirOrFile);
+ bool buildStatus(const char* docDir, const char* mdOutDir);
+ void checkAnchors();
+
+private:
+ enum class TableState {
+ kNone,
+ kRow,
+ kColumn,
+ };
+
+ struct AnchorDef {
+ string fDef;
+ MarkType fMarkType;
+ };
+
+ 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, 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, Resolvable );
+ Definition* isDefinedByParent(RootDefinition* root, string ref);
+ string linkName(const Definition* ) const;
+ string linkRef(string leadingSpaces, Definition*, string ref, Resolvable );
+ void markTypeOut(Definition* , const Definition** prior);
+ void mdHeaderOut(int depth) { mdHeaderOutLF(depth, 2); }
+ void mdHeaderOutLF(int depth, int lf);
+ 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* );
+
+ SubtopicDescriptions& populator(string key) {
+ auto entry = fPopulators.find(key);
+ // FIXME: this should have been detected earlier
+ SkASSERT(fPopulators.end() != entry);
+ return entry->second;
+ }
+
+ void reset() override {
+ INHERITED::resetCommon();
+ fEnumClass = nullptr;
+ fMethod = nullptr;
+ fRoot = nullptr;
+ fSubtopic = nullptr;
+ fLastParam = nullptr;
+ fTableState = TableState::kNone;
+ fAddRefFailed = false;
+ fHasFiddle = false;
+ fInDescription = false;
+ fInList = false;
+ fResolveAndIndent = false;
+ fLiteralAndIndent = false;
+ fLastDef = nullptr;
+ fParamEnd = nullptr;
+ fInProgress = false;
+ }
+
+ Resolvable resolvable(const Definition* definition) const {
+ MarkType markType = definition->fMarkType;
+ if (MarkType::kCode == markType) {
+ for (auto child : definition->fChildren) {
+ if (MarkType::kLiteral == child->fMarkType) {
+ return Resolvable::kLiteral;
+ }
+ }
+ }
+ if ((MarkType::kExample == markType
+ || MarkType::kFunction == markType) && fHasFiddle) {
+ return Resolvable::kNo;
+ }
+ return BmhParser::kMarkProps[(int) markType].fResolve;
+ }
+
+ void resolveOut(const char* start, const char* end, Resolvable );
+ void returnHeaderOut(const Definition** prior, Definition* def);
+ void rowOut(string col1, const Definition* col2);
+ void rowOut(const char * name, string description, bool literalName);
+
+ void subtopicOut(string name);
+ void subtopicsOut(Definition* def);
+ void subtopicOut(string key, const vector<Definition*>& data, const Definition* csParent,
+ const Definition* topicParent, bool showClones);
+ bool subtopicRowOut(string keyName, const Definition* entry);
+ void summaryOut(const Definition* def, MarkType , string name);
+ string tableDataCodeDef(const Definition* def);
+ string tableDataCodeDef(string def, string name);
+ string tableDataCodeLocalRef(string name);
+ string tableDataCodeLocalRef(string ref, string name);
+ string tableDataCodeRef(const Definition* ref);
+ string tableDataCodeRef(string ref, string name);
+ void writeSubtopicTableHeader(string key);
+
+ 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;
+ const Definition* fLastDef;
+ Definition* fMethod;
+ RootDefinition* fRoot; // used in generating populated tables; always struct or class
+ RootDefinition* fSubtopic; // used in resolving symbols
+ const Definition* fLastParam;
+ TableState fTableState;
+ unordered_map<string, SubtopicDescriptions> fPopulators;
+ unordered_map<string, string> fPhraseParams;
+ const char* fParamEnd;
+ bool fAddRefFailed;
+ bool fHasFiddle;
+ bool fInDescription; // FIXME: for now, ignore unfound camelCase in description since it may
+ // be defined in example which at present cannot be linked to
+ bool fInList;
+ bool fLiteralAndIndent;
+ bool fResolveAndIndent;
+ bool fOddRow;
+ bool fHasDetails;
+ bool fInProgress;
+ typedef ParserCommon INHERITED;
+};
+
+#endif
diff --git a/tools/bookmaker/parserCommon.cpp b/tools/bookmaker/parserCommon.cpp
index c3bab1a4da..271551bd6f 100644
--- a/tools/bookmaker/parserCommon.cpp
+++ b/tools/bookmaker/parserCommon.cpp
@@ -5,10 +5,11 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
+#include "parserCommon.h"
+
void ParserCommon::checkLineLength(size_t len, const char* str) {
if (!fWritingIncludes) {
return;
diff --git a/tools/bookmaker/parserCommon.h b/tools/bookmaker/parserCommon.h
new file mode 100644
index 0000000000..f31cbd63cd
--- /dev/null
+++ b/tools/bookmaker/parserCommon.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef parserCommon_DEFINED
+#define parserCommon_DEFINED
+
+#include "SkData.h"
+#include "SkJSONCPP.h"
+
+#include "definition.h"
+#include "textParser.h"
+
+enum class StatusFilter {
+ kCompleted,
+ kInProgress,
+ kUnknown,
+};
+
+class ParserCommon : public TextParser {
+public:
+ enum class OneFile {
+ kNo,
+ kYes,
+ };
+
+ enum class OneLine {
+ kNo,
+ kYes,
+ };
+
+ enum class IndentKind {
+ kConstOut,
+ kEnumChild,
+ kEnumChild2,
+ kEnumHeader,
+ kEnumHeader2,
+ kMethodOut,
+ kStructMember,
+ };
+
+ struct IndentState {
+ IndentState(IndentKind kind, int indent)
+ : fKind(kind)
+ , fIndent(indent) {
+ }
+
+ IndentKind fKind;
+ int fIndent;
+ };
+
+ ParserCommon() : TextParser()
+ , fParent(nullptr)
+ , fDebugOut(false)
+ , fValidate(false)
+ , fReturnOnWrite(false)
+ {
+ }
+
+ ~ParserCommon() override {
+ }
+
+ void addDefinition(Definition* def) {
+ fParent->fChildren.push_back(def);
+ fParent = def;
+ }
+
+ 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;
+ fIndentStack.emplace_back(kind, fIndent);
+ }
+
+ void indentOut() {
+ SkASSERT(fIndent >= 4);
+ SkASSERT(fIndentStack.back().fIndent == fIndent);
+ fIndent -= 4;
+ fIndentStack.pop_back();
+ }
+
+ void indentToColumn(int column) {
+ SkASSERT(column >= fColumn);
+ SkASSERT(!fReturnOnWrite);
+ SkASSERT(column < 80);
+ FPRINTF("%*s", column - fColumn, "");
+ fColumn = column;
+ fSpaces += column - fColumn;
+ }
+
+ bool leadingPunctuation(const char* str, size_t len) const {
+ if (!fPendingSpace) {
+ return false;
+ }
+ if (len < 2) {
+ return false;
+ }
+ if ('.' != str[0] && ',' != str[0] && ';' != str[0] && ':' != str[0]) {
+ return false;
+ }
+ return ' ' >= str[1];
+ }
+
+ void lf(int count) {
+ fPendingLF = SkTMax(fPendingLF, count);
+ this->nl();
+ }
+
+ void lfAlways(int count) {
+ this->lf(count);
+ this->writePending();
+ }
+
+ void lfcr() {
+ this->lf(1);
+ }
+
+ void nl() {
+ SkASSERT(!fReturnOnWrite);
+ fLinefeeds = 0;
+ fSpaces = 0;
+ fColumn = 0;
+ fPendingSpace = 0;
+ }
+
+ bool parseFile(const char* file, const char* suffix, OneFile );
+ bool parseStatus(const char* file, const char* suffix, StatusFilter filter);
+ virtual bool parseFromFile(const char* path) = 0;
+ bool parseSetup(const char* path);
+
+ void popObject() {
+ fParent->fContentEnd = fParent->fTerminator = fChar;
+ fParent = fParent->fParent;
+ }
+
+ static char* ReadToBuffer(string filename, int* size);
+
+ virtual void reset() = 0;
+
+ void resetCommon() {
+ fLine = fChar = fStart;
+ fLineCount = 0;
+ fLinesWritten = 1;
+ fParent = nullptr;
+ fIndent = 0;
+ fOut = nullptr;
+ fMaxLF = 2;
+ fPendingLF = 0;
+ fPendingSpace = 0;
+ fOutdentNext = false;
+ fWritingIncludes = false;
+ fDebugWriteCodeBlock = false;
+ nl();
+ }
+
+ void setAsParent(Definition* definition) {
+ if (fParent) {
+ fParent->fChildren.push_back(definition);
+ definition->fParent = fParent;
+ }
+ fParent = definition;
+ }
+
+ void singleLF() {
+ fMaxLF = 1;
+ }
+
+ void stringAppend(string& result, char ch) const;
+ void stringAppend(string& result, string str) const;
+ void stringAppend(string& result, const Definition* ) const;
+
+ void writeBlock(int size, const char* data) {
+ SkAssertResult(writeBlockTrim(size, data));
+ }
+
+ bool writeBlockIndent(int size, const char* data, bool ignoreIndent);
+
+ void writeBlockSeparator() {
+ this->writeString(
+ "# ------------------------------------------------------------------------------");
+ this->lf(2);
+ }
+
+ bool writeBlockTrim(int size, const char* data);
+
+ void writeCommentHeader() {
+ this->lf(2);
+ this->writeString("/**");
+ this->writeSpace();
+ }
+
+ void writeCommentTrailer(OneLine oneLine) {
+ if (OneLine::kNo == oneLine) {
+ this->lf(1);
+ } else {
+ this->writeSpace();
+ }
+ this->writeString("*/");
+ this->lfcr();
+ }
+
+ void writePending();
+
+ // write a pending space, so that two consecutive calls
+ // don't double write, and trailing spaces on lines aren't written
+ void writeSpace(int count = 1) {
+ SkASSERT(!fReturnOnWrite);
+ SkASSERT(!fPendingLF);
+ SkASSERT(!fLinefeeds);
+ SkASSERT(fColumn > 0);
+ SkASSERT(!fSpaces);
+ fPendingSpace = count;
+ }
+
+ void writeString(const char* str);
+
+ void writeString(string str) {
+ this->writeString(str.c_str());
+ }
+
+ static bool WrittenFileDiffers(string filename, string readname);
+
+ unordered_map<string, sk_sp<SkData>> fRawData;
+ unordered_map<string, vector<char>> fLFOnly;
+ 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)
+ int fSpaces; // number of spaces (indent) last written, zeroed on non-space
+ int fColumn; // current column; number of chars past last linefeed
+ int fIndent; // desired indention
+ int fPendingSpace; // one or two spaces should preceed the next string or block
+ size_t fLinesWritten; // as opposed to fLineCount, number of lines read
+ char fLastChar; // last written
+ bool fDebugOut; // set true to write to std out
+ bool fValidate; // set true to check anchor defs and refs
+ bool fOutdentNext; // set at end of embedded struct to prevent premature outdent
+ bool fWroteSomething; // used to detect empty content; an alternative source is preferable
+ bool fReturnOnWrite; // used to detect non-empty content; allowing early return
+ bool fWritingIncludes; // set true when writing includes to check >100 columns
+ mutable bool fDebugWriteCodeBlock;
+
+private:
+ typedef TextParser INHERITED;
+};
+
+struct JsonStatus {
+ const Json::Value& fObject;
+ Json::Value::iterator fIter;
+ string fName;
+ StatusFilter fStatusFilter;
+};
+
+class JsonCommon : public ParserCommon {
+public:
+ bool empty() { return fStack.empty(); }
+ bool parseFromFile(const char* path) override;
+
+ void reset() override {
+ fStack.clear();
+ INHERITED::resetCommon();
+ }
+
+ vector<JsonStatus> fStack;
+ Json::Value fRoot;
+private:
+ typedef ParserCommon INHERITED;
+};
+
+class StatusIter : public JsonCommon {
+public:
+ StatusIter(const char* statusFile, const char* suffix, StatusFilter);
+ ~StatusIter() override {}
+ string baseDir();
+ bool next(string* file, StatusFilter* filter);
+private:
+ const char* fSuffix;
+ StatusFilter fFilter;
+};
+
+class HackParser : public ParserCommon {
+public:
+ HackParser(const BmhParser& bmhParser)
+ : ParserCommon()
+ , fBmhParser(bmhParser) {
+ this->reset();
+ }
+
+ bool parseFromFile(const char* path) override {
+ if (!INHERITED::parseSetup(path)) {
+ return false;
+ }
+ return hackFiles();
+ }
+
+ void reset() override {
+ INHERITED::resetCommon();
+ }
+
+ void replaceWithPop(const Definition* );
+
+private:
+ const BmhParser& fBmhParser;
+ bool hackFiles();
+
+ typedef ParserCommon INHERITED;
+};
+
+#endif
diff --git a/tools/bookmaker/selfCheck.cpp b/tools/bookmaker/selfCheck.cpp
index 5f1eb38ece..1c822c2d05 100644
--- a/tools/bookmaker/selfCheck.cpp
+++ b/tools/bookmaker/selfCheck.cpp
@@ -5,7 +5,8 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
+#include "bmhParser.h"
+#include "selfCheck.h"
#ifdef SK_BUILD_FOR_WIN
#include <windows.h>
diff --git a/tools/bookmaker/selfCheck.h b/tools/bookmaker/selfCheck.h
new file mode 100644
index 0000000000..6e7bcf038b
--- /dev/null
+++ b/tools/bookmaker/selfCheck.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef selfCheck_DEFINED
+#define selfCheck_DEFINED
+
+class BmhParser;
+
+bool SelfCheck(const BmhParser& );
+
+#endif
diff --git a/tools/bookmaker/spellCheck.cpp b/tools/bookmaker/spellCheck.cpp
index 75a01f78dc..c6cfe5d079 100644
--- a/tools/bookmaker/spellCheck.cpp
+++ b/tools/bookmaker/spellCheck.cpp
@@ -5,8 +5,9 @@
* found in the LICENSE file.
*/
-#include "bookmaker.h"
+#include "bmhParser.h"
+#include "SkCommandLineFlags.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
@@ -369,7 +370,7 @@ bool SpellCheck::check(Definition* def) {
}
bool SpellCheck::checkable(MarkType markType) {
- return BmhParser::Resolvable::kYes == fBmhParser.kMarkProps[(int) markType].fResolve;
+ return Resolvable::kYes == fBmhParser.kMarkProps[(int) markType].fResolve;
}
void SpellCheck::childCheck(Definition* def, const char* start) {
diff --git a/tools/bookmaker/textParser.cpp b/tools/bookmaker/textParser.cpp
new file mode 100644
index 0000000000..6ea4a2485e
--- /dev/null
+++ b/tools/bookmaker/textParser.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "definition.h"
+
+#ifdef SK_BUILD_FOR_WIN
+#include <Windows.h>
+#endif
+
+TextParser::TextParser(const Definition* definition) :
+ TextParser(definition->fFileName, definition->fContentStart, definition->fContentEnd,
+ definition->fLineCount) {
+}
+
+string TextParser::ReportFilename(string file) {
+ string fullName;
+#ifdef SK_BUILD_FOR_WIN
+ TCHAR pathChars[MAX_PATH];
+ DWORD pathLen = GetCurrentDirectory(MAX_PATH, pathChars);
+ for (DWORD index = 0; index < pathLen; ++index) {
+ fullName += pathChars[index] == (char)pathChars[index] ? (char)pathChars[index] : '?';
+ }
+ fullName += '\\';
+#endif
+ fullName += file;
+ return fullName;
+}
+
+void TextParser::reportError(const char* errorStr) const {
+ this->reportWarning(errorStr);
+ SkDebugf(""); // convenient place to set a breakpoint
+}
+
+void TextParser::reportWarning(const char* errorStr) const {
+ const char* lineStart = fLine;
+ if (lineStart >= fEnd) {
+ lineStart = fChar;
+ }
+ SkASSERT(lineStart < fEnd);
+ TextParser err(fFileName, lineStart, fEnd, fLineCount);
+ size_t lineLen = this->lineLength();
+ ptrdiff_t spaces = fChar - lineStart;
+ while (spaces > 0 && (size_t) spaces > lineLen) {
+ ++err.fLineCount;
+ err.fLine += lineLen;
+ spaces -= lineLen;
+ lineLen = err.lineLength();
+ }
+ string fullName = this->ReportFilename(fFileName);
+ SkDebugf("\n%s(%zd): error: %s\n", fullName.c_str(), err.fLineCount, errorStr);
+ if (0 == lineLen) {
+ SkDebugf("[blank line]\n");
+ } else {
+ while (lineLen > 0 && '\n' == err.fLine[lineLen - 1]) {
+ --lineLen;
+ }
+ SkDebugf("%.*s\n", (int) lineLen, err.fLine);
+ SkDebugf("%*s^\n", (int) spaces, "");
+ }
+}
+
+void TextParser::setForErrorReporting(const Definition* definition, const char* str) {
+ fFileName = definition->fFileName;
+ fStart = definition->fContentStart;
+ fLine = str;
+ while (fLine > fStart && fLine[-1] != '\n') {
+ --fLine;
+ }
+ fChar = str;
+ fEnd = definition->fContentEnd;
+ fLineCount = definition->fLineCount;
+ const char* lineInc = fStart;
+ while (lineInc < str) {
+ fLineCount += '\n' == *lineInc++;
+ }
+}
+
+string TextParser::typedefName() {
+ // look for typedef as one of three forms:
+ // typedef return-type (*NAME)(params);
+ // typedef alias NAME;
+ // typedef std::function<alias> NAME;
+ string builder;
+ const char* end = this->doubleLF();
+ if (!end) {
+ end = fEnd;
+ }
+ const char* altEnd = this->strnstr("#Typedef ##", end);
+ if (altEnd) {
+ end = this->strnchr('\n', end);
+ }
+ if (!end) {
+ return this->reportError<string>("missing typedef std::function end bracket >");
+ }
+ bool stdFunction = this->startsWith("std::function");
+ if (stdFunction) {
+ if (!this->skipToEndBracket('>')) {
+ return this->reportError<string>("missing typedef std::function end bracket >");
+ }
+ this->next();
+ this->skipWhiteSpace();
+ builder += string(fChar, end - fChar);
+ } else {
+ const char* paren = this->strnchr('(', end);
+ if (!paren) {
+ const char* lastWord = nullptr;
+ do {
+ this->skipToWhiteSpace();
+ if (fChar < end && isspace(fChar[0])) {
+ const char* whiteStart = fChar;
+ this->skipWhiteSpace();
+ // FIXME: test should be for fMC
+ if ('#' == fChar[0]) {
+ end = whiteStart;
+ break;
+ }
+ lastWord = fChar;
+ } else {
+ break;
+ }
+ } while (true);
+ if (!lastWord) {
+ return this->reportError<string>("missing typedef name");
+ }
+ builder += string(lastWord, end - lastWord);
+ } else {
+ this->skipTo(paren);
+ this->next();
+ if ('*' != this->next()) {
+ return this->reportError<string>("missing typedef function asterisk");
+ }
+ const char* nameStart = fChar;
+ if (!this->skipToEndBracket(')')) {
+ return this->reportError<string>("missing typedef function )");
+ }
+ builder += string(nameStart, fChar - nameStart);
+ if (!this->skipToEndBracket('(')) {
+ return this->reportError<string>("missing typedef params (");
+ }
+ if (! this->skipToEndBracket(')')) {
+ return this->reportError<string>("missing typedef params )");
+ }
+ this->skipTo(end);
+ }
+ }
+ return builder;
+}
+
+void MethodParser::skipToMethodEnd(Resolvable resolvable) {
+ if (this->eof()) {
+ return;
+ }
+ string name = fLocalName.length() ? fLocalName : fClassName;
+ if ('~' == this->peek()) {
+ this->next();
+ if (!this->startsWith(name.c_str())) {
+ --fChar;
+ return;
+ }
+ }
+ if (Resolvable::kSimple != resolvable
+ && Resolvable::kInclude != resolvable
+ && (this->startsWith(name.c_str()) || this->startsWith("operator"))) {
+ const char* ptr = this->anyOf("\n (");
+ if (ptr && '(' == *ptr && strncmp(ptr, "(...", 4)) {
+ this->skipToEndBracket(')');
+ SkAssertResult(')' == this->next());
+ this->skipExact("_const") || (Resolvable::kCode == resolvable
+ && this->skipExact(" const"));
+ return;
+ }
+ }
+ if (this->startsWith("Sk") && this->wordEndsWith(".h")) { // allow include refs
+ this->skipToNonName();
+ } else {
+ this->skipFullName();
+ if (this->endsWith("operator")) {
+ const char* ptr = this->anyOf("\n (");
+ if (ptr && '(' == *ptr) {
+ this->skipToEndBracket(')');
+ SkAssertResult(')' == this->next());
+ this->skipExact("_const");
+ }
+ }
+ }
+}
diff --git a/tools/bookmaker/textParser.h b/tools/bookmaker/textParser.h
new file mode 100644
index 0000000000..eee1f7cfad
--- /dev/null
+++ b/tools/bookmaker/textParser.h
@@ -0,0 +1,733 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef textParser_DEFINED
+#define textParser_DEFINED
+
+#include <functional>
+
+#include "bookmaker.h"
+
+class BmhParser;
+class Definition;
+
+class TextParser : public NonAssignable {
+ TextParser() {} // only for ParserCommon, TextParserSave
+ friend class ParserCommon;
+ friend class TextParserSave;
+public:
+ virtual ~TextParser() {}
+
+ TextParser(string fileName, const char* start, const char* end, int lineCount)
+ : fFileName(fileName)
+ , fStart(start)
+ , fLine(start)
+ , fChar(start)
+ , fEnd(end)
+ , fLineCount(lineCount)
+ {
+ }
+
+ TextParser(const Definition* );
+
+ const char* anyOf(const char* str) const {
+ const char* ptr = fChar;
+ while (ptr < fEnd) {
+ if (strchr(str, ptr[0])) {
+ return ptr;
+ }
+ ++ptr;
+ }
+ return nullptr;
+ }
+
+ const char* anyOf(const char* wordStart, const char* wordList[], size_t wordListCount) const {
+ const char** wordPtr = wordList;
+ const char** wordEnd = wordPtr + wordListCount;
+ const size_t matchLen = fChar - wordStart;
+ while (wordPtr < wordEnd) {
+ const char* word = *wordPtr++;
+ if (strlen(word) == matchLen && !strncmp(wordStart, word, matchLen)) {
+ return word;
+ }
+ }
+ 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;
+ if (start <= fStart) {
+ return '\0';
+ }
+ if (strncmp(start, pattern, len)) {
+ return '\0';
+ }
+ 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) {
+ *loc = result;
+ }
+ 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;
+ const char* doubleStart = nullptr;
+ while (++ptr < fEnd) {
+ if (!doubleStart) {
+ if ('\n' == ptr[0]) {
+ doubleStart = ptr;
+ }
+ continue;
+ }
+ if ('\n' == ptr[0] || '#' == ptr[0]) {
+ return doubleStart;
+ }
+ if (' ' < ptr[0]) {
+ doubleStart = nullptr;
+ }
+ }
+ return nullptr;
+ }
+
+ bool endsWith(const char* match) {
+ int matchLen = strlen(match);
+ if (matchLen > fChar - fLine) {
+ return false;
+ }
+ return !strncmp(fChar - matchLen, match, matchLen);
+ }
+
+ bool eof() const { return fChar >= fEnd; }
+
+ const char* lineEnd() const {
+ const char* ptr = fChar;
+ do {
+ if (ptr >= fEnd) {
+ return ptr;
+ }
+ char test = *ptr++;
+ if (test == '\n' || test == '\0') {
+ break;
+ }
+ } while (true);
+ return ptr;
+ }
+
+ ptrdiff_t lineLength() const {
+ return this->lineEnd() - fLine;
+ }
+
+ bool match(TextParser* );
+
+ char next() {
+ SkASSERT(fChar < fEnd);
+ char result = *fChar++;
+ if ('\n' == result) {
+ ++fLineCount;
+ fLine = fChar;
+ }
+ return result;
+ }
+
+ char peek() const { SkASSERT(fChar < fEnd); return *fChar; }
+
+ void restorePlace(const TextParser& save) {
+ fChar = save.fChar;
+ fLine = save.fLine;
+ fLineCount = save.fLineCount;
+ }
+
+ void savePlace(TextParser* save) {
+ save->fChar = fChar;
+ save->fLine = fLine;
+ save->fLineCount = fLineCount;
+ }
+
+ void reportError(const char* errorStr) const;
+ static string ReportFilename(string file);
+ void reportWarning(const char* errorStr) const;
+
+ template <typename T> T reportError(const char* errorStr) const {
+ this->reportError(errorStr);
+ return T();
+ }
+
+ bool sentenceEnd(const char* check) const {
+ while (check > fStart) {
+ --check;
+ if (' ' < check[0] && '.' != check[0]) {
+ return false;
+ }
+ if ('.' == check[0]) {
+ return ' ' >= check[1];
+ }
+ if ('\n' == check[0] && '\n' == check[1]) {
+ return true;
+ }
+ }
+ return true;
+ }
+
+ void setForErrorReporting(const Definition* , const char* );
+
+ bool skipToBalancedEndBracket(char startB, char endB) {
+ SkASSERT(fChar < fEnd);
+ SkASSERT(startB == fChar[0]);
+ int startCount = 0;
+ do {
+ char test = this->next();
+ startCount += startB == test;
+ startCount -= endB == test;
+ } while (startCount && fChar < fEnd);
+ return !startCount;
+ }
+
+ bool skipToEndBracket(char endBracket, const char* end = nullptr) {
+ if (nullptr == end) {
+ end = fEnd;
+ }
+ while (fChar[0] != endBracket) {
+ if (fChar >= end) {
+ return false;
+ }
+ (void) this->next();
+ }
+ return true;
+ }
+
+ bool skipToEndBracket(const char* endBracket) {
+ size_t len = strlen(endBracket);
+ while (strncmp(fChar, endBracket, len)) {
+ if (fChar >= fEnd) {
+ return false;
+ }
+ (void) this->next();
+ }
+ return true;
+ }
+
+ bool skipLine() {
+ return skipToEndBracket('\n');
+ }
+
+ void skipTo(const char* skip) {
+ while (fChar < skip) {
+ this->next();
+ }
+ }
+
+ void skipToAlpha() {
+ while (fChar < fEnd && !isalpha(fChar[0])) {
+ fChar++;
+ }
+ }
+
+ // returns true if saw close brace
+ bool skipToAlphaNum() {
+ bool sawCloseBrace = false;
+ while (fChar < fEnd && !isalnum(fChar[0])) {
+ sawCloseBrace |= '}' == *fChar++;
+ }
+ return sawCloseBrace;
+ }
+
+ bool skipExact(const char* pattern) {
+ if (!this->startsWith(pattern)) {
+ return false;
+ }
+ this->skipName(pattern);
+ return true;
+ }
+
+ // 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() {
+ 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++;
+ }
+ 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) {
+ int match = 0;
+ while (!this->eof() && '\n' != fChar[0]) {
+ match += open == this->peek();
+ match -= close == this->next();
+ }
+ return match;
+ }
+
+ bool skipToLineStart() {
+ if (!this->skipLine()) {
+ return false;
+ }
+ if (!this->eof()) {
+ return this->skipWhiteSpace();
+ }
+ return true;
+ }
+
+ void skipToLineStart(int* indent, bool* sawReturn) {
+ SkAssertResult(this->skipLine());
+ this->skipWhiteSpace(indent, sawReturn);
+ }
+
+ void skipLower() {
+ while (fChar < fEnd && (islower(fChar[0]) || '_' == fChar[0])) {
+ fChar++;
+ }
+ }
+
+ void skipToNonAlphaNum() {
+ while (fChar < fEnd && (isalnum(fChar[0]) || '_' == fChar[0])) {
+ fChar++;
+ }
+ }
+
+ void skipToNonName() {
+ while (fChar < fEnd && (isalnum(fChar[0])
+ || '_' == fChar[0] || '-' == fChar[0]
+ || (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1])
+ || ('.' == fChar[0] && fChar + 1 < fEnd && isalpha(fChar[1])))) {
+ if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
+ fChar++;
+ }
+ fChar++;
+ }
+ }
+
+ void skipPhraseName() {
+ while (fChar < fEnd && (islower(fChar[0]) || '_' == fChar[0])) {
+ fChar++;
+ }
+ }
+
+ void skipToSpace() {
+ while (fChar < fEnd && ' ' != fChar[0]) {
+ fChar++;
+ }
+ }
+
+ void skipToWhiteSpace() {
+ while (fChar < fEnd && ' ' < fChar[0]) {
+ fChar++;
+ }
+ }
+
+ bool skipName(const char* word) {
+ size_t len = strlen(word);
+ if (len <= (size_t) (fEnd - fChar) && !strncmp(word, fChar, len)) {
+ for (size_t i = 0; i < len; ++i) {
+ this->next();
+ }
+ }
+ return this->eof() || ' ' >= fChar[0];
+ }
+
+ bool skipSpace() {
+ while (' ' == this->peek()) {
+ (void) this->next();
+ if (fChar >= fEnd) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool skipWord(const char* word) {
+ if (!this->skipWhiteSpace()) {
+ return false;
+ }
+ const char* save = fChar;
+ if (!this->skipName(word)) {
+ fChar = save;
+ return false;
+ }
+ if (!this->skipWhiteSpace()) {
+ return false;
+ }
+ return true;
+ }
+
+ bool skipWhiteSpace() {
+ while (' ' >= this->peek()) {
+ (void) this->next();
+ if (fChar >= fEnd) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void skipWhiteSpace(int* indent, bool* skippedReturn) {
+ while (' ' >= this->peek()) {
+ *indent = *skippedReturn ? *indent + 1 : 1;
+ if ('\n' == this->peek()) {
+ *skippedReturn |= true;
+ *indent = 0;
+ }
+ (void) this->next();
+ SkASSERT(fChar < fEnd);
+ }
+ }
+
+ bool startsWith(const char* str) const {
+ size_t len = strlen(str);
+ ptrdiff_t lineLen = fEnd - fChar;
+ return len <= (size_t) lineLen && 0 == strncmp(str, fChar, len);
+ }
+
+ // ignores minor white space differences
+ bool startsWith(const char* str, size_t oLen) const {
+ size_t tIndex = 0;
+ size_t tLen = fEnd - fChar;
+ size_t oIndex = 0;
+ while (oIndex < oLen && tIndex < tLen) {
+ bool tSpace = ' ' >= fChar[tIndex];
+ bool oSpace = ' ' >= str[oIndex];
+ if (tSpace != oSpace) {
+ break;
+ }
+ if (tSpace) {
+ do {
+ ++tIndex;
+ } while (tIndex < tLen && ' ' >= fChar[tIndex]);
+ do {
+ ++oIndex;
+ } while (oIndex < oLen && ' ' >= str[oIndex]);
+ continue;
+ }
+ if (fChar[tIndex] != str[oIndex]) {
+ break;
+ }
+ ++tIndex;
+ ++oIndex;
+ }
+ return oIndex >= oLen;
+ }
+
+ const char* strnchr(char ch, const char* end) const {
+ const char* ptr = fChar;
+ while (ptr < end) {
+ if (ptr[0] == ch) {
+ return ptr;
+ }
+ ++ptr;
+ }
+ return nullptr;
+ }
+
+ const char* strnstr(const char *match, const char* end) const {
+ size_t matchLen = strlen(match);
+ SkASSERT(matchLen > 0);
+ ptrdiff_t len = end - fChar;
+ SkASSERT(len >= 0);
+ if ((size_t) len < matchLen ) {
+ return nullptr;
+ }
+ size_t count = len - matchLen;
+ for (size_t index = 0; index <= count; index++) {
+ if (0 == strncmp(&fChar[index], match, matchLen)) {
+ return &fChar[index];
+ }
+ }
+ return nullptr;
+ }
+
+ const char* trimmedBracketEnd(const char bracket) const {
+ int max = (int) (this->lineLength());
+ int index = 0;
+ while (index < max && bracket != fChar[index]) {
+ ++index;
+ }
+ SkASSERT(index < max);
+ while (index > 0 && ' ' >= fChar[index - 1]) {
+ --index;
+ }
+ return fChar + index;
+ }
+
+ const char* trimmedBracketEnd(string bracket) const {
+ size_t max = (size_t) (this->lineLength());
+ string line(fChar, max);
+ size_t index = line.find(bracket);
+ SkASSERT(index < max);
+ while (index > 0 && ' ' >= fChar[index - 1]) {
+ --index;
+ }
+ return fChar + index;
+ }
+
+ const char* trimmedBracketNoEnd(const char bracket) const {
+ int max = (int) (fEnd - fChar);
+ int index = 0;
+ while (index < max && bracket != fChar[index]) {
+ ++index;
+ }
+ SkASSERT(index < max);
+ while (index > 0 && ' ' >= fChar[index - 1]) {
+ --index;
+ }
+ return fChar + index;
+ }
+
+ const char* trimmedLineEnd() const {
+ const char* result = this->lineEnd();
+ while (result > fChar && ' ' >= result[-1]) {
+ --result;
+ }
+ return result;
+ }
+
+ void trimEnd() {
+ while (fEnd > fStart && ' ' >= fEnd[-1]) {
+ --fEnd;
+ }
+ }
+
+ // FIXME: nothing else in TextParser knows from C++ --
+ // there could be a class between TextParser and ParserCommon
+ virtual string typedefName();
+
+ const char* wordEnd() const {
+ const char* end = fChar;
+ while (isalnum(end[0]) || '_' == end[0] || '-' == end[0]) {
+ ++end;
+ }
+ return end;
+ }
+
+ string fFileName;
+ const char* fStart;
+ const char* fLine;
+ const char* fChar;
+ const char* fEnd;
+ size_t fLineCount;
+};
+
+class TextParserSave {
+public:
+ TextParserSave(TextParser* parser) {
+ fParser = parser;
+ fSave.fFileName = parser->fFileName;
+ fSave.fStart = parser->fStart;
+ fSave.fLine = parser->fLine;
+ fSave.fChar = parser->fChar;
+ fSave.fEnd = parser->fEnd;
+ fSave.fLineCount = parser->fLineCount;
+ }
+
+ void restore() const {
+ fParser->fFileName = fSave.fFileName;
+ fParser->fStart = fSave.fStart;
+ fParser->fLine = fSave.fLine;
+ fParser->fChar = fSave.fChar;
+ fParser->fEnd = fSave.fEnd;
+ fParser->fLineCount = fSave.fLineCount;
+ }
+
+private:
+ TextParser* fParser;
+ TextParser fSave;
+};
+
+static inline bool has_nonwhitespace(string s) {
+ bool nonwhite = false;
+ for (const char& c : s) {
+ if (' ' < c) {
+ nonwhite = true;
+ break;
+ }
+ }
+ return nonwhite;
+}
+
+static inline void trim_end(string &s) {
+ s.erase(std::find_if(s.rbegin(), s.rend(),
+ std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
+}
+
+static inline void trim_end_spaces(string &s) {
+ while (s.length() > 0 && ' ' == s.back()) {
+ s.pop_back();
+ }
+}
+
+static inline void trim_start(string &s) {
+ s.erase(s.begin(), std::find_if(s.begin(), s.end(),
+ std::not1(std::ptr_fun<int, int>(std::isspace))));
+}
+
+static inline void trim_start_end(string& s) {
+ trim_start(s);
+ trim_end(s);
+}
+
+class EscapeParser : public TextParser {
+public:
+ EscapeParser(const char* start, const char* end) :
+ TextParser("", start, end, 0) {
+ const char* reader = fStart;
+ fStorage = new char[end - start];
+ char* writer = fStorage;
+ while (reader < fEnd) {
+ char ch = *reader++;
+ if (ch != '\\') {
+ *writer++ = ch;
+ } else {
+ char ctrl = *reader++;
+ if (ctrl == 'u') {
+ unsigned unicode = 0;
+ for (int i = 0; i < 4; ++i) {
+ unicode <<= 4;
+ SkASSERT((reader[0] >= '0' && reader[0] <= '9') ||
+ (reader[0] >= 'A' && reader[0] <= 'F') ||
+ (reader[0] >= 'a' && reader[0] <= 'f'));
+ int nibble = *reader++ - '0';
+ if (nibble > 9) {
+ nibble = (nibble & ~('a' - 'A')) - 'A' + '9' + 1;
+ }
+ unicode |= nibble;
+ }
+ SkASSERT(unicode < 256);
+ *writer++ = (unsigned char) unicode;
+ } else {
+ SkASSERT(ctrl == 'n');
+ *writer++ = '\n';
+ }
+ }
+ }
+ fStart = fLine = fChar = fStorage;
+ fEnd = writer;
+ }
+
+ ~EscapeParser() override {
+ delete fStorage;
+ }
+private:
+ char* fStorage;
+};
+
+// some methods cannot be trivially parsed; look for class-name / ~ / operator
+class MethodParser : public TextParser {
+public:
+ MethodParser(string className, string fileName,
+ const char* start, const char* end, int lineCount)
+ : TextParser(fileName, start, end, lineCount)
+ , fClassName(className) {
+ size_t doubleColons = className.find_last_of("::");
+ if (string::npos != doubleColons) {
+ fLocalName = className.substr(doubleColons + 1);
+ SkASSERT(fLocalName.length() > 0);
+ }
+ }
+
+ ~MethodParser() override {}
+
+ string localName() const {
+ return fLocalName;
+ }
+
+ void setLocalName(string name) {
+ if (name == fClassName) {
+ fLocalName = "";
+ } else {
+ fLocalName = name;
+ }
+ }
+
+ // returns true if close brace was skipped
+ int skipToMethodStart() {
+ if (!fClassName.length()) {
+ return this->skipToAlphaNum();
+ }
+ int braceCount = 0;
+ while (!this->eof() && !isalnum(this->peek()) && '~' != this->peek()) {
+ braceCount += '{' == this->peek();
+ braceCount -= '}' == this->peek();
+ this->next();
+ }
+ return braceCount;
+ }
+
+ void skipToMethodEnd(Resolvable resolvable);
+
+ bool wordEndsWith(const char* str) const {
+ const char* space = this->strnchr(' ', fEnd);
+ if (!space) {
+ return false;
+ }
+ size_t len = strlen(str);
+ if (space < fChar + len) {
+ return false;
+ }
+ return !strncmp(str, space - len, len);
+ }
+
+private:
+ string fClassName;
+ string fLocalName;
+ typedef TextParser INHERITED;
+};
+
+#endif