document SkColor.h

SkColor.h uses #define liberally, and has many global symbols,
two things bookmaker hasn't seen in other includes.

Revised .h -> .bmh converter to work with SkColor.h as well
as updating how .bmh indices are built.

Generated SkColor_Reference.bmh for globals, and
SkColor4f_Reference.bmh for class.

Other than the existing comments, this doesn't update the
documentation or add new examples.

Docs-Preview: https://skia.org/?cl=118985
TBR=caryclark@google.com
Bug: skia:6898
Change-Id: I5978257ee0e51319823efbe8dfc467a08c99ffe0
Reviewed-on: https://skia-review.googlesource.com/118985
Commit-Queue: Cary Clark <caryclark@skia.org>
Reviewed-by: Cary Clark <caryclark@skia.org>
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp
index 223c5b5..49e347e 100644
--- a/tools/bookmaker/bookmaker.cpp
+++ b/tools/bookmaker/bookmaker.cpp
@@ -64,6 +64,112 @@
      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_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_F Resolvable::kFormula
+#define R_C Resolvable::kClone
+
+#define E_Y Exemplary::kYes
+#define E_N Exemplary::kNo
+#define E_O Exemplary::kOptional
+
+BmhParser::MarkProps BmhParser::kMarkProps[] = {
+// names without formal definitions (e.g. Column) aren't included
+// fill in other names once they're actually used
+  { "",             MarkType::kNone,         R_Y, E_N, 0 }
+, { "A",            MarkType::kAnchor,       R_N, E_N, 0 }
+, { "Alias",        MarkType::kAlias,        R_N, E_N, 0 }
+, { "Bug",          MarkType::kBug,          R_N, E_N, 0 }
+, { "Class",        MarkType::kClass,        R_Y, E_O, M_CSST | M(Root) }
+, { "Code",         MarkType::kCode,         R_F, E_N, M_CSST | M_E | M(Method) | M(Define) | 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_ST  }
+, { "Define",       MarkType::kDefine,       R_O, E_Y, M_ST }
+, { "DefinedBy",    MarkType::kDefinedBy,    R_N, E_N, M(Method) }
+, { "Deprecated",   MarkType::kDeprecated,   R_Y, E_N, 0 }
+, { "Description",  MarkType::kDescription,  R_Y, E_N, M(Example) | M(NoExample) }
+, { "Doxygen",      MarkType::kDoxygen,      R_Y, E_N, 0 }
+, { "Duration",     MarkType::kDuration,     R_N, E_N, M(Example) | M(NoExample) }
+, { "Enum",         MarkType::kEnum,         R_Y, E_O, M_CSST | M(Root) }
+, { "EnumClass",    MarkType::kEnumClass,    R_Y, E_O, M_CSST | M(Root) }
+, { "Example",      MarkType::kExample,      R_O, E_N, M_CSST | M_E | M(Method) | M(Const) | M(Define) }
+, { "Experimental", MarkType::kExperimental, R_Y, E_N, 0 }
+, { "External",     MarkType::kExternal,     R_Y, E_N, M(Root) }
+, { "File",         MarkType::kFile,         R_N, E_N, M(Track) }
+, { "Formula",      MarkType::kFormula,      R_F, E_N,
+                                              M(Column) | M_E | M_ST | M(Member) | M(Method) | M_D }
+, { "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(Subtopic) }
+, { "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) }
+, { "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_N, M_CSST }
+, { "Method",       MarkType::kMethod,       R_Y, E_Y, M_CSST }
+, { "NoExample",    MarkType::kNoExample,    R_N, E_N, M_CSST | M_E | M(Method) | M(Const) | M(Define) }
+, { "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(Subtopic) }
+, { "",             MarkType::kPhraseRef,    R_Y, E_N, 0 }
+, { "Platform",     MarkType::kPlatform,     R_N, E_N, M(Example) | M(NoExample) }
+, { "Populate",     MarkType::kPopulate,     R_N, E_N, M(Subtopic) }
+, { "Private",      MarkType::kPrivate,      R_N, E_N, 0 }
+, { "Return",       MarkType::kReturn,       R_Y, E_N, M(Method) }
+, { "",             MarkType::kRoot,         R_Y, E_N, 0 }
+, { "",             MarkType::kRow,          R_Y, E_N, M(Table) | M(List) }
+, { "SeeAlso",      MarkType::kSeeAlso,      R_C, E_N, M_CSST | M_E | M(Method) | M(Define) | 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(Root) | M_ST }
+, { "Substitute",   MarkType::kSubstitute,   R_N, E_N, M_ST }
+, { "Subtopic",     MarkType::kSubtopic,     R_Y, E_Y, M_CSST }
+, { "Table",        MarkType::kTable,        R_Y, E_N, M(Method) | M_CSST | M_E }
+, { "Template",     MarkType::kTemplate,     R_Y, E_N, 0 }
+, { "",             MarkType::kText,         R_N, E_N, 0 }
+, { "Time",         MarkType::kTime,         R_Y, E_N, M(Track) }
+, { "ToDo",         MarkType::kToDo,         R_N, E_N, 0 }
+, { "Topic",        MarkType::kTopic,        R_Y, E_Y, M_CS | M(Root) | M(Topic) }
+, { "Track",        MarkType::kTrack,        R_Y, E_N, M_E | M_ST }
+, { "Typedef",      MarkType::kTypedef,      R_Y, E_N, M(Class) | M_ST }
+, { "",             MarkType::kUnion,        R_Y, E_N, 0 }
+, { "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_F
+#undef R_C
+
+#undef M_E
+#undef M_CSST
+#undef M_ST
+#undef M_CS
+#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;
@@ -77,6 +183,7 @@
         case MarkType::kClass:
         case MarkType::kStruct:
         case MarkType::kConst:
+        case MarkType::kDefine:
         case MarkType::kEnum:
         case MarkType::kEnumClass:
         case MarkType::kMember:
@@ -88,7 +195,7 @@
             if (typeNameBuilder.size() > 1) {
                 return this->reportError<bool>("expected one name only");
             }
-            const string& name = typeNameBuilder[0];
+            string name = typeNameBuilder[0];
             if (nullptr == fRoot) {
                 fRoot = this->findBmhObject(markType, name);
                 fRoot->fFileName = fFileName;
@@ -134,8 +241,8 @@
                             || MarkType::kExperimental == child->fMarkType
                             || MarkType::kNoExample == child->fMarkType;
                 }
-                if (fMaps[(int) markType].fExemplary != hasExample
-                        && fMaps[(int) markType].fExemplary != Exemplary::kOptional) {
+                if (kMarkProps[(int) markType].fExemplary != hasExample
+                        && kMarkProps[(int) markType].fExemplary != Exemplary::kOptional) {
                     if (string::npos == fFileName.find("undocumented")
                             && !hasExcluder) {
                         hasExample == Exemplary::kNo ?
@@ -394,7 +501,6 @@
         case MarkType::kAlias:
         case MarkType::kAnchor:
         case MarkType::kBug:
-        case MarkType::kDefine:
         case MarkType::kDeprecated:
         case MarkType::kDuration:
         case MarkType::kFile:
@@ -445,7 +551,7 @@
             } else if (MarkType::kAlias == markType) {
                 this->skipWhiteSpace();
                 const char* start = fChar;
-                this->skipToNonAlphaNum();
+                this->skipToNonName();
                 string alias(start, fChar - start);
                 if (fAliasMap.end() != fAliasMap.find(alias)) {
                     return this->reportError<bool>("duplicate alias");
@@ -493,7 +599,7 @@
     return true;
 }
 
-void BmhParser::reportDuplicates(const Definition& def, const string& dup) const {
+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");
@@ -546,14 +652,14 @@
         return this->reportError<bool>("mismatched end marker expect #");
     }
     const char* nameStart = tp.fChar;
-    tp.skipToNonAlphaNum();
+    tp.skipToNonName();
     string markName(nameStart, tp.fChar - nameStart);
-    if (fMaps[(int) markType].fName != markName) {
+    if (kMarkProps[(int) markType].fName != markName) {
         return this->reportError<bool>("expected #XXX ## to match");
     }
     tp.skipSpace();
     nameStart = tp.fChar;
-    tp.skipToNonAlphaNum();
+    tp.skipToNonName();
     markName = string(nameStart, tp.fChar - nameStart);
     if ("" == markName) {
         if (fMC != tp.next() || fMC != tp.next()) {
@@ -619,7 +725,7 @@
 bool BmhParser::childOf(MarkType markType) const {
     auto childError = [this](MarkType markType) -> bool {
         string errStr = "expected ";
-        errStr += fMaps[(int) markType].fName;
+        errStr += kMarkProps[(int) markType].fName;
         errStr += " parent";
         return this->reportError<bool>(errStr.c_str());
     };
@@ -645,7 +751,7 @@
     TextParserSave savePlace(this);
     this->skipSpace();
     const char* wordStart = fChar;
-    this->skipToNonAlphaNum();
+    this->skipToNonName();
     const char* wordEnd = fChar;
     classID = string(wordStart, wordEnd - wordStart);
     if (!mc) {
@@ -705,14 +811,14 @@
                 this->skipLine();
                 continue;
             }
-            if (this->startsWith(fMaps[(int) MarkType::kExternal].fName)) {
-                this->skipToNonAlphaNum();
+            if (this->startsWith(kMarkProps[(int) MarkType::kExternal].fName)) {
+                this->skipToNonName();
                 continue;
             }
         }
         this->skipToAlpha();
         const char* wordStart = fChar;
-        this->skipToNonAlphaNum();
+        this->skipToNonName();
         if (fChar - wordStart > 0) {
             fExternals.emplace_front(MarkType::kExternal, wordStart, fChar, fLineCount, fParent,
                     fMC);
@@ -792,7 +898,7 @@
     return true;
 }
 
-static size_t count_indent(const string& text, size_t test, size_t end) {
+static size_t count_indent(string text, size_t test, size_t end) {
     size_t result = test;
     while (test < end) {
         if (' ' != text[test]) {
@@ -803,7 +909,7 @@
     return test - result;
 }
 
-static void add_code(const string& text, int pos, int end,
+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
@@ -935,9 +1041,8 @@
         code += "}";
     }
     string example = "\"" + normalizedName + "\": {\n";
-    size_t nameStart = def->fFileName.find(SkOSPath::SEPARATOR, 0);
-    SkASSERT(string::npos != nameStart);
-    string baseFile = def->fFileName.substr(nameStart + 1, def->fFileName.length() - nameStart - 5);
+    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";
@@ -1107,12 +1212,12 @@
                 bool hasEnd = this->hasEndToken();
                 if (!hasEnd) {
                     MarkType parentType = fParent ? fParent->fMarkType : MarkType::kRoot;
-                    uint64_t parentMask = fMaps[(int) markType].fParentMask;
+                    uint64_t parentMask = kMarkProps[(int) markType].fParentMask;
                     if (parentMask && !(parentMask & (1LL << (int) parentType))) {
                         return this->reportError<bool>("invalid parent");
                     }
                 }
-                if (!this->skipName(fMaps[(int) markType].fName)) {
+                if (!this->skipName(kMarkProps[(int) markType].fName)) {
                     return this->reportError<bool>("illegal markup character");
                 }
                 if (!this->skipSpace()) {
@@ -1226,14 +1331,14 @@
 
 MarkType BmhParser::getMarkType(MarkLookup lookup) const {
     for (int index = 0; index <= Last_MarkType; ++index) {
-        int typeLen = strlen(fMaps[index].fName);
+        int typeLen = strlen(kMarkProps[index].fName);
         if (typeLen == 0) {
             continue;
         }
         if (fChar + typeLen >= fEnd || fChar[typeLen] > ' ') {
             continue;
         }
-        int chCompare = strncmp(fChar, fMaps[index].fName, typeLen);
+        int chCompare = strncmp(fChar, kMarkProps[index].fName, typeLen);
         if (chCompare < 0) {
             goto fail;
         }
@@ -1489,7 +1594,7 @@
     do {
         this->skipSpace();
         wordStart = fChar;
-        this->skipToNonAlphaNum();
+        this->skipToNonName();
     } while (this->anyOf(wordStart, prefixes, SK_ARRAY_COUNT(prefixes)));
     if ('*' == this->peek()) {
         this->next();
@@ -1631,7 +1736,7 @@
     if (parser.eof() || fMC != parser.next()) {
         return end;
     }
-    const char* markName = fMaps[(int) definition->fMarkType].fName;
+    const char* markName = kMarkProps[(int) definition->fMarkType].fName;
     if (!parser.skipExact(markName)) {
         return end;
     }
@@ -1929,6 +2034,7 @@
         builder = fParent->fName;
     }
     switch (markType) {
+        case MarkType::kDefine:
         case MarkType::kEnum:
             // enums may be nameless
         case MarkType::kConst:
@@ -1963,7 +2069,6 @@
         case MarkType::kAlias:
         case MarkType::kAnchor:
         case MarkType::kBug:  // fixme: expect number
-        case MarkType::kDefine:
         case MarkType::kDefinedBy:
         case MarkType::kDeprecated:
         case MarkType::kDuration:
@@ -2046,7 +2151,7 @@
     return uniqueRootName(builder, MarkType::kTypedef);
 }
 
-string BmhParser::uniqueName(const string& base, MarkType markType) {
+string BmhParser::uniqueName(string base, MarkType markType) {
     string builder(base);
     if (!builder.length()) {
         builder = fParent->fName;
@@ -2076,8 +2181,8 @@
     return numBuilder;
 }
 
-string BmhParser::uniqueRootName(const string& base, MarkType markType) {
-    auto checkName = [markType](const Definition& def, const string& numBuilder) -> bool {
+string BmhParser::uniqueRootName(string base, MarkType markType) {
+    auto checkName = [markType](const Definition& def, string numBuilder) -> bool {
         return markType == def.fMarkType && def.fName == numBuilder;
     };
 
@@ -2132,11 +2237,11 @@
 
 void BmhParser::validate() const {
     for (int index = 0; index <= (int) Last_MarkType; ++index) {
-        SkASSERT(fMaps[index].fMarkType == (MarkType) index);
+        SkASSERT(kMarkProps[index].fMarkType == (MarkType) index);
     }
     const char* last = "";
     for (int index = 0; index <= (int) Last_MarkType; ++index) {
-        const char* next = fMaps[index].fName;
+        const char* next = kMarkProps[index].fName;
         if (!last[0]) {
             last = next;
             continue;
@@ -2149,7 +2254,7 @@
     }
 }
 
-string BmhParser::word(const string& prefix, const string& delimiter) {
+string BmhParser::word(string prefix, string delimiter) {
     string builder(prefix);
     this->skipWhiteSpace();
     const char* lineEnd = fLine + this->lineLength();
@@ -2338,7 +2443,7 @@
         }
         if (FLAGS_tokens) {
             includeParser.fDebugOut = FLAGS_stdout;
-            if (includeParser.dumpTokens(FLAGS_bmh[0])) {
+            if (includeParser.dumpTokens()) {
                 bmhParser.fWroteOut = true;
             }
             done = true;
diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h
index bbec6d6..62f2faa 100644
--- a/tools/bookmaker/bookmaker.h
+++ b/tools/bookmaker/bookmaker.h
@@ -198,7 +198,7 @@
 
 extern const IncludeKey kKeyWords[];
 
-static inline bool has_nonwhitespace(const string& s) {
+static inline bool has_nonwhitespace(string s) {
     bool nonwhite = false;
     for (const char& c : s) {
         if (' ' < c) {
@@ -246,7 +246,7 @@
 public:
     virtual ~TextParser() {}
 
-    TextParser(const string& fileName, const char* start, const char* end, int lineCount)
+    TextParser(string fileName, const char* start, const char* end, int lineCount)
         : fFileName(fileName)
         , fStart(start)
         , fLine(start)
@@ -477,6 +477,12 @@
     }
 
     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])
@@ -840,8 +846,7 @@
 
     virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; }
     virtual const RootDefinition* asRoot() const { SkASSERT(0); return nullptr; }
-    bool boilerplateIfDef(Definition* parent);
-    bool boilerplateDef(Definition* parent);
+    bool boilerplateIfDef();
 
     bool boilerplateEndIf() {
         return true;
@@ -864,11 +869,12 @@
     }
 
     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(const string& name) const;
-    const Definition* hasParam(const string& ref) const;
+    bool hasMatch(string name) const;
+    const Definition* hasParam(string ref) const;
     bool isClone() const { return fClone; }
 
     Definition* iRootParent() {
@@ -889,12 +895,12 @@
         return (int) (fContentEnd - fContentStart);
     }
 
-    bool methodHasReturn(const string& name, TextParser* methodParser) 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(const string& fullRef, const string& name) const;
+    bool paramsMatch(string fullRef, string name) const;
     bool parseOperator(size_t doubleColons, string& result);
 
     string printableName() const {
@@ -991,7 +997,7 @@
     const RootDefinition* asRoot() const override { return this; }
     void clearVisited();
     bool dumpUnVisited();
-    const Definition* find(const string& ref, AllowParens ) const;
+    const Definition* find(string ref, AllowParens ) const;
     bool isRoot() const override { return true; }
     RootDefinition* rootParent() override { return fRootParent; }
     const RootDefinition* rootParent() const override { return fRootParent; }
@@ -1004,6 +1010,7 @@
 };
 
 struct IClassDefinition : public Definition {
+    unordered_map<string, Definition*> fDefines;
     unordered_map<string, Definition*> fEnums;
     unordered_map<string, Definition*> fMembers;
     unordered_map<string, Definition*> fMethods;
@@ -1135,6 +1142,13 @@
     }
 
     void writeBlockIndent(int size, const char* data);
+
+    void writeBlockSeparator() {
+            this->writeString(
+              "# ------------------------------------------------------------------------------");
+            this->lf(2);
+    }
+
     bool writeBlockTrim(int size, const char* data);
 
     void writeCommentHeader() {
@@ -1162,7 +1176,7 @@
 
     void writeString(const char* str);
 
-    void writeString(const string& str) {
+    void writeString(string str) {
         this->writeString(str.c_str());
     }
 
@@ -1253,120 +1267,76 @@
         kYes
     };
 
-#define M(mt) (1LL << (int) MarkType::k##mt)
-#define M_D M(Description)
-#define M_CS M(Class) | M(Struct)
-#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_F Resolvable::kFormula
-#define R_C Resolvable::kClone
-
-#define E_Y Exemplary::kYes
-#define E_N Exemplary::kNo
-#define E_O Exemplary::kOptional
-
     BmhParser(bool skip) : ParserCommon()
         , fMaps {
-// names without formal definitions (e.g. Column) aren't included
-// fill in other names once they're actually used
-  { "",            nullptr,      MarkType::kNone,         R_Y, E_N, 0 }
-, { "A",           nullptr,      MarkType::kAnchor,       R_N, E_N, 0 }
-, { "Alias",       nullptr,      MarkType::kAlias,        R_N, E_N, 0 }
-, { "Bug",         nullptr,      MarkType::kBug,          R_N, E_N, 0 }
-, { "Class",       &fClassMap,   MarkType::kClass,        R_Y, E_O, M_CSST | M(Root) }
-, { "Code",        nullptr,      MarkType::kCode,         R_O, E_N, M_CSST | M_E | M(Method) }
-, { "",            nullptr,      MarkType::kColumn,       R_Y, E_N, M(Row) }
-, { "",            nullptr,      MarkType::kComment,      R_N, E_N, 0 }
-, { "Const",       &fConstMap,   MarkType::kConst,        R_Y, E_O, M_E | M_ST  }
-, { "Define",      nullptr,      MarkType::kDefine,       R_O, E_N, M_ST }
-, { "DefinedBy",   nullptr,      MarkType::kDefinedBy,    R_N, E_N, M(Method) }
-, { "Deprecated",  nullptr,      MarkType::kDeprecated,   R_Y, E_N, 0 }
-, { "Description", nullptr,      MarkType::kDescription,  R_Y, E_N, M(Example) | M(NoExample) }
-, { "Doxygen",     nullptr,      MarkType::kDoxygen,      R_Y, E_N, 0 }
-, { "Duration",    nullptr,      MarkType::kDuration,     R_N, E_N, M(Example) | M(NoExample) }
-, { "Enum",        &fEnumMap,    MarkType::kEnum,         R_Y, E_O, M_CSST | M(Root) }
-, { "EnumClass",   &fClassMap,   MarkType::kEnumClass,    R_Y, E_O, M_CSST | M(Root) }
-, { "Example",     nullptr,      MarkType::kExample,
-                                                     R_O, E_N, M_CSST | M_E | M(Method) | M(Const) }
-, { "Experimental", nullptr,     MarkType::kExperimental, R_Y, E_N, 0 }
-, { "External",    nullptr,      MarkType::kExternal,     R_Y, E_N, M(Root) }
-, { "File",        nullptr,      MarkType::kFile,         R_N, E_N, M(Track) }
-, { "Formula",     nullptr,      MarkType::kFormula,      R_F, E_N,
-                                              M(Column) | M_E | M_ST | M(Member) | M(Method) | M_D }
-, { "Function",    nullptr,      MarkType::kFunction,     R_O, E_N, M(Example) | M(NoExample) }
-, { "Height",      nullptr,      MarkType::kHeight,       R_N, E_N, M(Example) | M(NoExample) }
-, { "Illustration", nullptr,     MarkType::kIllustration, R_N, E_N, M(Subtopic) }
-, { "Image",       nullptr,      MarkType::kImage,        R_N, E_N, M(Example) | M(NoExample) }
-, { "In",          nullptr,      MarkType::kIn,           R_N, E_N,
-                                                             M_CSST | M_E | M(Method) | M(Typedef) }
-, { "Legend",      nullptr,      MarkType::kLegend,       R_Y, E_N, M(Table) }
-, { "Line",        nullptr,      MarkType::kLine,         R_N, E_N,
-                                                             M_CSST | M_E | M(Method) | M(Typedef) }
-, { "",            nullptr,      MarkType::kLink,         R_N, E_N, M(Anchor) }
-, { "List",        nullptr,      MarkType::kList,         R_Y, E_N, M(Method) | M_CSST | M_E | M_D }
-, { "Literal",     nullptr,      MarkType::kLiteral,      R_N, E_N, M(Code) }
-, { "",            nullptr,      MarkType::kMarkChar,     R_N, E_N, 0 }
-, { "Member",      nullptr,      MarkType::kMember,       R_Y, E_N, M_CSST }
-, { "Method",      &fMethodMap,  MarkType::kMethod,       R_Y, E_Y, M_CSST }
-, { "NoExample",   nullptr,      MarkType::kNoExample,    R_N, E_N, M_CSST | M_E | M(Method) }
-, { "Outdent",     nullptr,      MarkType::kOutdent,      R_N, E_N, M(Code) }
-, { "Param",       nullptr,      MarkType::kParam,        R_Y, E_N, M(Method) }
-, { "PhraseDef",   nullptr,      MarkType::kPhraseDef,    R_Y, E_N, M(Subtopic) }
-, { "",            nullptr,      MarkType::kPhraseRef,    R_Y, E_N, 0 }
-, { "Platform",    nullptr,      MarkType::kPlatform,     R_N, E_N, M(Example) | M(NoExample) }
-, { "Populate",    nullptr,      MarkType::kPopulate,     R_N, E_N, M(Subtopic) }
-, { "Private",     nullptr,      MarkType::kPrivate,      R_N, E_N, 0 }
-, { "Return",      nullptr,      MarkType::kReturn,       R_Y, E_N, M(Method) }
-, { "",            nullptr,      MarkType::kRoot,         R_Y, E_N, 0 }
-, { "",            nullptr,      MarkType::kRow,          R_Y, E_N, M(Table) | M(List) }
-, { "SeeAlso",     nullptr,      MarkType::kSeeAlso,      R_C, E_N,
-                                                             M_CSST | M_E | M(Method) | M(Typedef) }
-, { "Set",         nullptr,      MarkType::kSet,          R_N, E_N, M(Example) | M(NoExample) }
-, { "StdOut",      nullptr,      MarkType::kStdOut,       R_N, E_N, M(Example) | M(NoExample) }
-, { "Struct",      &fClassMap,   MarkType::kStruct,       R_Y, E_O, M(Class) | M(Root) | M_ST }
-, { "Substitute",  nullptr,      MarkType::kSubstitute,   R_N, E_N, M_ST }
-, { "Subtopic",    nullptr,      MarkType::kSubtopic,     R_Y, E_Y, M_CSST }
-, { "Table",       nullptr,      MarkType::kTable,        R_Y, E_N, M(Method) | M_CSST | M_E }
-, { "Template",    nullptr,      MarkType::kTemplate,     R_Y, E_N, 0 }
-, { "",            nullptr,      MarkType::kText,         R_N, E_N, 0 }
-, { "Time",        nullptr,      MarkType::kTime,         R_Y, E_N, M(Track) }
-, { "ToDo",        nullptr,      MarkType::kToDo,         R_N, E_N, 0 }
-, { "Topic",       nullptr,      MarkType::kTopic,        R_Y, E_Y, M_CS | M(Root) | M(Topic) }
-, { "Track",       nullptr,      MarkType::kTrack,        R_Y, E_N, M_E | M_ST }
-, { "Typedef",     &fTypedefMap, MarkType::kTypedef,      R_Y, E_N, M(Class) | M_ST }
-, { "",            nullptr,      MarkType::kUnion,        R_Y, E_N, 0 }
-, { "Volatile",    nullptr,      MarkType::kVolatile,     R_N, E_N, M(StdOut) }
-, { "Width",       nullptr,      MarkType::kWidth,        R_N, E_N, M(Example) | M(NoExample) } }
-, fSkip(skip)
-        {
+          { nullptr,       MarkType::kNone }
+        , { nullptr,       MarkType::kAnchor }
+        , { nullptr,       MarkType::kAlias }
+        , { nullptr,       MarkType::kBug }
+        , { &fClassMap,    MarkType::kClass }
+        , { nullptr,       MarkType::kCode }
+        , { nullptr,       MarkType::kColumn }
+        , { nullptr,       MarkType::kComment }
+        , { &fConstMap,    MarkType::kConst }
+        , { &fDefineMap,   MarkType::kDefine }
+        , { nullptr,       MarkType::kDefinedBy }
+        , { nullptr,       MarkType::kDeprecated }
+        , { nullptr,       MarkType::kDescription }
+        , { nullptr,       MarkType::kDoxygen }
+        , { nullptr,       MarkType::kDuration }
+        , { &fEnumMap,     MarkType::kEnum }
+        , { &fClassMap,    MarkType::kEnumClass }
+        , { nullptr,       MarkType::kExample }
+        , { nullptr,       MarkType::kExperimental }
+        , { nullptr,       MarkType::kExternal }
+        , { nullptr,       MarkType::kFile }
+        , { nullptr,       MarkType::kFormula }
+        , { nullptr,       MarkType::kFunction }
+        , { nullptr,       MarkType::kHeight }
+        , { nullptr,       MarkType::kIllustration }
+        , { nullptr,       MarkType::kImage }
+        , { nullptr,       MarkType::kIn }
+        , { nullptr,       MarkType::kLegend }
+        , { nullptr,       MarkType::kLine }
+        , { nullptr,       MarkType::kLink }
+        , { nullptr,       MarkType::kList }
+        , { nullptr,       MarkType::kLiteral }
+        , { nullptr,       MarkType::kMarkChar }
+        , { nullptr,       MarkType::kMember }
+        , { &fMethodMap,   MarkType::kMethod }
+        , { nullptr,       MarkType::kNoExample }
+        , { nullptr,       MarkType::kOutdent }
+        , { nullptr,       MarkType::kParam }
+        , { nullptr,       MarkType::kPhraseDef }
+        , { nullptr,       MarkType::kPhraseRef }
+        , { nullptr,       MarkType::kPlatform }
+        , { nullptr,       MarkType::kPopulate }
+        , { nullptr,       MarkType::kPrivate }
+        , { nullptr,       MarkType::kReturn }
+        , { nullptr,       MarkType::kRoot }
+        , { nullptr,       MarkType::kRow }
+        , { nullptr,       MarkType::kSeeAlso }
+        , { nullptr,       MarkType::kSet }
+        , { nullptr,       MarkType::kStdOut }
+        , { &fClassMap,    MarkType::kStruct }
+        , { nullptr,       MarkType::kSubstitute }
+        , { nullptr,       MarkType::kSubtopic }
+        , { nullptr,       MarkType::kTable }
+        , { nullptr,       MarkType::kTemplate }
+        , { nullptr,       MarkType::kText }
+        , { nullptr,       MarkType::kTime }
+        , { nullptr,       MarkType::kToDo }
+        , { nullptr,       MarkType::kTopic }
+        , { nullptr,       MarkType::kTrack }
+        , { &fTypedefMap,  MarkType::kTypedef }
+        , { nullptr,       MarkType::kUnion }
+        , { nullptr,       MarkType::kVolatile }
+        , { nullptr,       MarkType::kWidth }
+        }
+        , fSkip(skip) {
             this->reset();
         }
 
-#undef R_O
-#undef R_N
-#undef R_Y
-#undef R_F
-#undef R_C
-
-#undef M_E
-#undef M_CSST
-#undef M_ST
-#undef M_CS
-#undef M_D
-#undef M
-
-#undef E_Y
-#undef E_N
-#undef E_O
-
     ~BmhParser() override {}
 
     bool addDefinition(const char* defStart, bool hasEnd, MarkType markType,
@@ -1385,8 +1355,8 @@
     bool exampleToScript(Definition*, ExampleOptions, string* result ) const;
     string extractText(const Definition* , TrimExtract ) const;
 
-    RootDefinition* findBmhObject(MarkType markType, const string& typeName) const {
-        auto map = fMaps[(int) markType].fBmh;
+    RootDefinition* findBmhObject(MarkType markType, string typeName) const {
+        auto map = fMaps[(int) markType].fMap;
         if (!map) {
             return nullptr;
         }
@@ -1410,7 +1380,7 @@
     }
 
     bool popParentStack(Definition* definition);
-    void reportDuplicates(const Definition& def, const string& dup) const;
+    void reportDuplicates(const Definition& def, string dup) const;
 
     void reset() override {
         INHERITED::resetCommon();
@@ -1436,28 +1406,35 @@
     vector<string> topicName();
     vector<string> typeName(MarkType markType, bool* expectEnd);
     string typedefName() override;
-    string uniqueName(const string& base, MarkType markType);
-    string uniqueRootName(const string& base, MarkType markType);
+    string uniqueName(string base, MarkType markType);
+    string uniqueRootName(string base, MarkType markType);
     void validate() const;
-    string word(const string& prefix, const string& delimiter);
+    string word(string prefix, string delimiter);
 
 public:
-    struct DefinitionMap {
+    struct MarkProps {
         const char* fName;
-        unordered_map<string, RootDefinition>* fBmh;
         MarkType fMarkType;
         Resolvable fResolve;
         Exemplary fExemplary;  // worthy of an example
         uint64_t fParentMask;
     };
 
+    struct DefinitionMap {
+        unordered_map<string, RootDefinition>* fMap;
+        MarkType fMarkType;
+    };
+
     DefinitionMap fMaps[Last_MarkType + 1];
+
+    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;
@@ -1578,25 +1555,28 @@
     bool checkForWord();
     string className() const;
     bool crossCheck(BmhParser& );
-    IClassDefinition* defineClass(const Definition& includeDef, const string& className);
+    IClassDefinition* defineClass(const Definition& includeDef, string className);
     void dumpClassTokens(IClassDefinition& classDef);
     void dumpComment(const Definition& );
-    void dumpEnum(const Definition& , const string& name);
-    void dumpMethod(const Definition& );
+    void dumpCommonTail(const Definition& );
+    void dumpDefine(const Definition& );
+    void dumpEnum(const Definition& , string name);
+    bool dumpGlobals();
+    void dumpMethod(const Definition& , string className);
     void dumpMember(const Definition& );
-    bool dumpTokens(const string& directory);
-    bool dumpTokens(const string& directory, const string& skClassName);
+    bool dumpTokens();
+    bool dumpTokens(string skClassName);
     bool findComments(const Definition& includeDef, Definition* markupDef);
 
     Definition* findIncludeObject(const Definition& includeDef, MarkType markType,
-            const string& typeName) {
+            string typeName) {
         typedef Definition* DefinitionPtr;
-        unordered_map<string, Definition>* map = fMaps[(int) markType].fInclude;
+        unordered_map<string, Definition*>* map = fMaps[(int) markType].fInclude;
         if (!map) {
             return reportError<DefinitionPtr>("invalid mark type");
         }
         string name = this->uniqueName(*map, typeName);
-        Definition& markupDef = (*map)[name];
+        Definition& markupDef = *(*map)[name];
         if (markupDef.fStart) {
             return reportError<DefinitionPtr>("definition already defined");
         }
@@ -1615,12 +1595,15 @@
     }
 
     static KeyWord FindKey(const char* start, const char* end);
-    bool internalName(const Definition& ) const;
+    bool isClone(const Definition& token);
+    bool isConstructor(const Definition& token, string className);
+    bool isInternalName(const Definition& token);
+    bool isOperator(const Definition& token);
     bool parseChar();
-    bool parseComment(const string& filename, const char* start, const char* end, int lineCount,
+    bool parseComment(string filename, const char* start, const char* end, int lineCount,
             Definition* markupDef);
     bool parseClass(Definition* def, IsStruct);
-    bool parseDefine();
+    bool parseDefine(Definition* child, Definition* markupDef);
     bool parseEnum(Definition* child, Definition* markupDef);
 
     bool parseFromFile(const char* path) override {
@@ -1632,7 +1615,7 @@
         return this->parseInclude(name);
     }
 
-    bool parseInclude(const string& name);
+    bool parseInclude(string name);
     bool parseMember(Definition* child, Definition* markupDef);
     bool parseMethod(Definition* child, Definition* markupDef);
     bool parseObject(Definition* child, Definition* markupDef);
@@ -1671,6 +1654,7 @@
         fInChar = false;
         fInCharCommentString = false;
         fInComment = false;
+        fInDefine = false;
         fInEnum = false;
         fInFunction = false;
         fInString = false;
@@ -1694,7 +1678,7 @@
     }
 
     template <typename T>
-    string uniqueName(const unordered_map<string, T>& m, const string& typeName) {
+    string uniqueName(const unordered_map<string, T>& m, string typeName) {
         string base(typeName.size() > 0 ? typeName : "_anonymous");
         string name(base);
         int anonCount = 1;
@@ -1719,7 +1703,7 @@
         }
     }
 
-    void writeDefinition(const Definition& def, const string& name, int spaces) {
+    void writeDefinition(const Definition& def, string name, int spaces) {
         this->writeBlock((int) (def.fContentEnd - def.fContentStart), def.fContentStart);
         this->writeSpace(spaces);
         this->writeString(name);
@@ -1746,11 +1730,11 @@
         this->lf(1);
     }
 
-    void writeEndTag(const char* tagType, const string& tagID, int spaces = 1) {
+    void writeEndTag(const char* tagType, string tagID, int spaces = 1) {
         this->writeEndTag(tagType, tagID.c_str(), spaces);
     }
 
-    void writeIncompleteTag(const char* tagType, const string& tagID, int spaces = 1) {
+    void writeIncompleteTag(const char* tagType, string tagID, int spaces = 1) {
         this->writeString(string("#") + tagType + " " + tagID);
         this->writeSpace(spaces);
         this->writeString("incomplete");
@@ -1784,14 +1768,14 @@
         this->lf(1);
     }
 
-    void writeTableRow(size_t pad, const string& col1) {
+    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, const string& col1, size_t pad2, const string& col2) {
+    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(), ' ') + " ##";
@@ -1818,7 +1802,7 @@
         this->writeString(tagID);
     }
 
-    void writeTagNoLF(const char* tagType, const string& tagID) {
+    void writeTagNoLF(const char* tagType, string tagID) {
         this->writeTagNoLF(tagType, tagID.c_str());
     }
 
@@ -1827,7 +1811,7 @@
         this->writeTagNoLF(tagType, tagID);
     }
 
-    void writeTag(const char* tagType, const string& tagID) {
+    void writeTag(const char* tagType, string tagID) {
         this->writeTag(tagType, tagID.c_str());
     }
 
@@ -1835,7 +1819,7 @@
     static void ValidateKeyWords();
 
     struct DefinitionMap {
-        unordered_map<string, Definition>* fInclude;
+        unordered_map<string, Definition*>* fInclude;
         MarkType fMarkType;
     };
 
@@ -1844,14 +1828,15 @@
 
     DefinitionMap fMaps[Last_MarkType + 1];
     unordered_map<string, Definition> fIncludeMap;
+    list<Definition> fGlobals;
     unordered_map<string, IClassDefinition> fIClassMap;
-    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;
+    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;
     Definition* fRootTopic;
     Definition* fInBrace;
     Definition* fLastObject;
@@ -1863,6 +1848,7 @@
     bool fInChar;
     bool fInCharCommentString;
     bool fInComment;
+    bool fInDefine;
     bool fInEnum;
     bool fInFunction;
     bool fInString;
@@ -1943,7 +1929,6 @@
         }
 
         const Definition* fDefinition;
-        const Definition* fBracket;
         const char* fStart;
         const char* fEnd;
         bool fWord;
@@ -1975,7 +1960,7 @@
         vector<IterState>& iterStack, IterState** iterState, Preprocessor* );
     void enumSizeItems(const Definition& child);
     bool findEnumSubtopic(string undername, const Definition** ) const;
-	Definition* findMemberCommentBlock(const vector<Definition*>& bmhChildren, const string& name) const;
+	Definition* findMemberCommentBlock(const vector<Definition*>& bmhChildren, string name) const;
     int lookupMethod(const PunctuationState punctuation, const Word word,
             const int start, const int run, int lastWrite,
             const char* data, bool hasIndirection);
@@ -2088,7 +2073,7 @@
 public:
     Catalog(BmhParser* bmh) : FiddleBase(bmh) {}
 
-    bool appendFile(const string& path);
+    bool appendFile(string path);
     bool closeCatalog();
     bool openCatalog(const char* inDir, const char* outDir);
     bool openStatus(const char* inDir, const char* outDir);
@@ -2157,12 +2142,14 @@
     static constexpr const char* kClassesAndStructs = "Class_or_Struct";
     static constexpr const char* kConstants = "Constant";
     static constexpr const char* kConstructors = "Constructor";
+    static constexpr const char* kDefines = "Define";
     static constexpr const char* kMemberFunctions = "Member_Function";
     static constexpr const char* kMembers = "Member";
     static constexpr const char* kOperators = "Operator";
     static constexpr const char* kOverview = "Overview";
     static constexpr const char* kRelatedFunctions = "Related_Function";
     static constexpr const char* kSubtopics = "Overview_Subtopic";
+    static constexpr const char* kTypedefs = "Typedef";
 
 private:
     enum class TableState {
@@ -2187,9 +2174,9 @@
     void childrenOut(const Definition* def, const char* contentStart);
     const Definition* csParent() const;
     const Definition* findParamType();
-    const Definition* isDefined(const TextParser& , const string& ref, BmhParser::Resolvable );
+    const Definition* isDefined(const TextParser& , string ref, BmhParser::Resolvable );
     string linkName(const Definition* ) const;
-    string linkRef(const string& leadingSpaces, const Definition*, const string& ref,
+    string linkRef(string leadingSpaces, const Definition*, string ref,
 			BmhParser::Resolvable ) const;
     void markTypeOut(Definition* );
     void mdHeaderOut(int depth) { mdHeaderOutLF(depth, 2); }
@@ -2233,11 +2220,11 @@
                 || MarkType::kFunction == markType) && fHasFiddle) {
             return BmhParser::Resolvable::kNo;
         }
-        return fBmhParser.fMaps[(int) markType].fResolve;
+        return BmhParser::kMarkProps[(int) markType].fResolve;
     }
 
     void resolveOut(const char* start, const char* end, BmhParser::Resolvable );
-    void rowOut(const char * name, const string& description);
+    void rowOut(const char * name, string description);
     void subtopicOut(const TableContents& tableContents);
     void subtopicsOut();
 
@@ -2264,7 +2251,7 @@
 // some methods cannot be trivially parsed; look for class-name / ~ / operator
 class MethodParser : public TextParser {
 public:
-    MethodParser(const string& className, const string& fileName,
+    MethodParser(string className, string fileName,
             const char* start, const char* end, int lineCount)
         : TextParser(fileName, start, end, lineCount)
         , fClassName(className) {
@@ -2305,7 +2292,7 @@
             }
         }
         if (this->startsWith("Sk") && this->wordEndsWith(".h")) {  // allow include refs
-            this->skipToNonAlphaNum();
+            this->skipToNonName();
         } else {
             this->skipFullName();
             if (this->endsWith("operator")) {
diff --git a/tools/bookmaker/cataloger.cpp b/tools/bookmaker/cataloger.cpp
index de46ecd..0e1e38a 100644
--- a/tools/bookmaker/cataloger.cpp
+++ b/tools/bookmaker/cataloger.cpp
@@ -10,7 +10,7 @@
 #include "SkOSFile.h"
 #include "SkOSPath.h"
 
-bool Catalog::appendFile(const string& path) {
+bool Catalog::appendFile(string path) {
     FILE* file = fopen(path.c_str(), "r");
     if (!file) {
         SkDebugf("could not append %s\n", path.c_str());
diff --git a/tools/bookmaker/definition.cpp b/tools/bookmaker/definition.cpp
index e3a5aa3..06db964 100644
--- a/tools/bookmaker/definition.cpp
+++ b/tools/bookmaker/definition.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "bookmaker.h"
+#include "SkOSPath.h"
 
 #ifdef CONST
 #undef CONST
@@ -138,7 +139,7 @@
                                     {{ CONST,  OpType::kThis,   OpMod::kReference, }}},
 };
 
-OpType lookup_type(const string& typeWord, const string& name) {
+OpType lookup_type(string typeWord, string name) {
     if (typeWord == name || (typeWord == "SkIVector" && name == "SkIPoint")
                          || (typeWord == "SkVector" && name == "SkPoint")) {
         return OpType::kThis;
@@ -191,7 +192,7 @@
     SkASSERT(isStatic == false || returnsConst == false);
     iParser.skipWhiteSpace();
     const char* returnTypeStart = iParser.fChar;
-    iParser.skipToNonAlphaNum();
+    iParser.skipToNonName();
     SkASSERT(iParser.fChar > returnTypeStart);
     string returnType(returnTypeStart, iParser.fChar - returnTypeStart);
     OpType returnOpType = lookup_type(returnType, className);
@@ -241,7 +242,7 @@
             }
             iParser.skipWhiteSpace();
             const char* paramStart = iParser.fChar;
-            iParser.skipToNonAlphaNum();
+            iParser.skipToNonName();
             SkASSERT(iParser.fChar > paramStart);
             string paramType(paramStart, iParser.fChar - paramStart);
             OpType paramOpType = lookup_type(paramType, className);
@@ -257,7 +258,7 @@
                 countsMatch = false;
                 break;
             }
-            iParser.skipToNonAlphaNum();
+            iParser.skipToNonName();
             if ('[' == iParser.peek()) {
                 paramMod = OpMod::kArray;
                 SkAssertResult(iParser.skipExact("[]"));
@@ -324,7 +325,7 @@
 #undef BLANK
 #undef DEFOP
 
-bool Definition::boilerplateIfDef(Definition* parent) {
+bool Definition::boilerplateIfDef() {
     const Definition& label = fTokens.front();
     if (Type::kWord != label.fType) {
         return false;
@@ -333,32 +334,6 @@
     return true;
 }
 
-// todo: this is matching #ifndef SkXXX_DEFINED for no particular reason
-// it doesn't do anything useful with arbitrary input, e.g. #ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
-// also doesn't know what to do with SK_REQUIRE_LOCAL_VAR()
-bool Definition::boilerplateDef(Definition* parent) {
-    if (!this->boilerplateIfDef(parent)) {
-        return false;
-    }
-    const char* s = fName.c_str();
-    const char* e = strchr(s, '_');
-    return true; // fixme: if this is trying to do something useful with define, do it here
-    if (!e) {
-        return false;
-    }
-    string prefix(s, e - s);
-    const char* inName = strstr(parent->fName.c_str(), prefix.c_str());
-    if (!inName) {
-        return false;
-    }
-    if ('/' != inName[-1] && '\\' != inName[-1]) {
-        return false;
-    }
-    if (strcmp(inName + prefix.size(), ".h")) {
-        return false;
-    }
-    return true;
-}
 
 // fixme: this will need to be more complicated to handle all of Skia
 // for now, just handle paint -- maybe fiddle will loosen naming restrictions
@@ -414,7 +389,7 @@
                     if (params.startsWith("const") || params.startsWith("int")
                             || params.startsWith("Sk")) {
                         const char* wordStart = params.fChar;
-                        params.skipToNonAlphaNum();
+                        params.skipToNonName();
                         if (underline) {
                             result += '_';
                         } else {
@@ -422,7 +397,7 @@
                         }
                         result += string(wordStart, params.fChar - wordStart);
                     } else {
-                        params.skipToNonAlphaNum();
+                        params.skipToNonName();
                     }
                     if (!params.eof() && '*' == params.peek()) {
                         if (underline) {
@@ -766,6 +741,18 @@
     return fFiddle.substr(start, end - start);
 }
 
+string Definition::fileName() const {
+    size_t nameStart = fFileName.rfind(SkOSPath::SEPARATOR);
+    if (SkOSPath::SEPARATOR != '/') {
+        size_t altNameStart = fFileName.rfind('/');
+        nameStart = string::npos == nameStart ? altNameStart :
+                string::npos != altNameStart && altNameStart > nameStart ? altNameStart : nameStart;
+    }
+    SkASSERT(string::npos != nameStart);
+    string baseFile = fFileName.substr(nameStart + 1);
+    return baseFile;
+}
+
 const Definition* Definition::findClone(string match) const {
     for (auto child : fChildren) {
         if (!child->fClone) {
@@ -791,7 +778,7 @@
     return nullptr;
 }
 
-const Definition* Definition::hasParam(const string& ref) const {
+const Definition* Definition::hasParam(string ref) const {
     SkASSERT(MarkType::kMethod == fMarkType);
     for (auto iter : fChildren) {
         if (MarkType::kParam != iter->fMarkType) {
@@ -805,7 +792,7 @@
     return nullptr;
 }
 
-bool Definition::hasMatch(const string& name) const {
+bool Definition::hasMatch(string name) const {
     for (auto child : fChildren) {
         if (name == child->fName) {
             return true;
@@ -827,7 +814,7 @@
     return true;
 }
 
-bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const {
+bool Definition::methodHasReturn(string name, TextParser* methodParser) const {
     if (methodParser->skipExact("static")) {
         methodParser->skipWhiteSpace();
     }
@@ -965,7 +952,7 @@
     return normalizedName;
 }
 
-static string unpreformat(const string& orig) {
+static string unpreformat(string orig) {
     string result;
     int amp = 0;
     for (auto c : orig) {
@@ -1030,7 +1017,7 @@
     return result;
 }
 
-bool Definition::paramsMatch(const string& matchFormatted, const string& name) const {
+bool Definition::paramsMatch(string matchFormatted, string name) const {
     string match = unpreformat(matchFormatted);
     TextParser def(fFileName, fStart, fContentStart, fLineCount);
     const char* dName = def.strnstr(name.c_str(), fContentStart);
@@ -1097,7 +1084,7 @@
     return success;
 }
 
-const Definition* RootDefinition::find(const string& ref, AllowParens allowParens) const {
+const Definition* RootDefinition::find(string ref, AllowParens allowParens) const {
     const auto leafIter = fLeaves.find(ref);
     if (leafIter != fLeaves.end()) {
         return &leafIter->second;
diff --git a/tools/bookmaker/fiddleParser.cpp b/tools/bookmaker/fiddleParser.cpp
index 682c87c..524f89d 100644
--- a/tools/bookmaker/fiddleParser.cpp
+++ b/tools/bookmaker/fiddleParser.cpp
@@ -144,6 +144,7 @@
                 }
             } else  if (strncmp(bmh.fChar, fiddle.fChar, fiddleLen)) {
                 if (!foundVolatile) {
+                    SkDebugf("%.*s\n", fiddleLen, fiddle.fChar);
                     bmh.reportError("mismatched stdout text\n");
                 }
             }
diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp
index 0335999..982f7b3 100644
--- a/tools/bookmaker/includeParser.cpp
+++ b/tools/bookmaker/includeParser.cpp
@@ -161,6 +161,10 @@
     switch (keyWord) {
         // these do not link to other # directives
         case KeyWord::kDefine:
+            if (!fInBrace) {
+                SkASSERT(!fInDefine);
+                fInDefine = true;
+            }
         case KeyWord::kInclude:
         case KeyWord::kError:
         break;
@@ -259,7 +263,7 @@
             const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
             switch (token.fMarkType) {
                 case MarkType::kMethod: {
-                    if (this->internalName(token)) {
+                    if (this->isInternalName(token)) {
                         continue;
                     }
                     if (!def) {
@@ -378,7 +382,7 @@
                         firstMember.skipWhiteSpace();
                         SkASSERT('k' == firstMember.peek());
                         const char* savePos = firstMember.fChar;
-                        firstMember.skipToNonAlphaNum();
+                        firstMember.skipToNonName();
                         const char* wordEnd = firstMember.fChar;
                         firstMember.fChar = savePos;
                         const char* lastUnderscore = nullptr;
@@ -499,7 +503,7 @@
 }
 
 IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
-        const string& name) {
+        string name) {
     string className;
     const Definition* test = fParent;
     while (Definition::Type::kFileType != test->fType) {
@@ -539,9 +543,7 @@
             continue;
         }
         if (MarkType::kMember != token.fMarkType) {
-            this->writeString(
-              "# ------------------------------------------------------------------------------");
-            this->lf(2);
+            this->writeBlockSeparator();
         }
         switch (token.fMarkType) {
             case MarkType::kEnum:
@@ -549,7 +551,7 @@
                 this->dumpEnum(token, token.fName);
             break;
             case MarkType::kMethod:
-                this->dumpMethod(token);
+                this->dumpMethod(token, classDef.fName);
             break;
             case MarkType::kMember:
                 this->dumpMember(token);
@@ -558,33 +560,7 @@
             default:
                 SkASSERT(0);
         }
-        this->lf(2);
-        this->writeTag("Example");
-        this->lf(1);
-        this->writeString("// incomplete");
-        this->lf(1);
-        this->writeEndTag();
-        this->lf(2);
-        this->writeTag("SeeAlso");
-        this->writeSpace();
-        this->writeString("incomplete");
-        this->lf(2);
-        switch (token.fMarkType) {
-            case MarkType::kEnum:
-            case MarkType::kEnumClass:
-                this->writeEndTag("Enum");
-            break;
-            case MarkType::kMethod:
-                this->writeEndTag("Method");
-            break;
-            case MarkType::kMember:
-                this->writeEndTag("Member");
-                continue;
-            break;
-            default:
-                SkASSERT(0);
-        }
-        this->lf(2);
+        this->dumpCommonTail(token);
     }
 }
 void IncludeParser::dumpComment(const Definition& token) {
@@ -751,10 +727,55 @@
     }
 }
 
-void IncludeParser::dumpEnum(const Definition& token, const string& name) {
+void IncludeParser::dumpCommonTail(const Definition& token) {
+    this->lf(2);
+    this->writeTag("Example");
+    this->lf(1);
+    this->writeString("// incomplete");
+    this->lf(1);
+    this->writeEndTag();
+    this->lf(2);
+    this->writeTag("SeeAlso");
+    this->writeSpace();
+    this->writeString("incomplete");
+    this->lf(2);
+    this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
+    this->lf(2);
+}
+
+void IncludeParser::dumpDefine(const Definition& token) {
+    this->writeTag("Define", token.fName);
+    this->lf(2);
+    this->writeTag("Code");
+    this->lfAlways(1);
+    this->writeString("###$");
+    this->lfAlways(1);
+    this->indentToColumn(4);
+    this->writeBlock(token.fTerminator - token.fStart, token.fStart);
+    this->lf(1);
+    this->indentToColumn(0);
+    this->writeString("$$$#");
+
+    this->writeEndTag();
+    this->lf(2);
+    this->dumpComment(token);
+    for (auto& child : token.fTokens) {
+        if (MarkType::kComment == child.fMarkType) {
+            continue;
+        }
+        this->writeTag("Param", child.fName);
+        this->writeSpace();
+        this->writeString("incomplete");
+        this->writeSpace();
+        this->writeString("##");
+        this->lf(1);
+    }
+}
+
+void IncludeParser::dumpEnum(const Definition& token, string name) {
     this->writeTag("Enum", name);
     this->lf(2);
-    this->writeString("#Code");
+    this->writeTag("Code");
     this->lfAlways(1);
     this->indentToColumn(4);
     this->writeString("enum");
@@ -787,7 +808,7 @@
     //     start here;
         // get comments before
         // or after const values
-        this->writeString("#Const");
+        this->writeTag("Const");
         this->writeSpace();
         this->writeString(child->fName);
         TextParser val(child);
@@ -827,35 +848,203 @@
     this->lf(2);
 }
 
-void IncludeParser::dumpMethod(const Definition& token) {
-    this->writeString("#Method");
-    this->writeSpace();
-    if ("SK_TO_STRING_NONVIRT" == token.fName) {
-        this->writeString("void toString(SkString* str) const;");
-        this->lf(2);
-        this->writeEndTag("DefinedBy", "void toString(SkString* str) const;");
-        this->lf(2);
-        this->writeTag("Private");
-        this->lf(1);
-        this->writeString("macro expands to: void toString(SkString* str) const;");
+bool IncludeParser::dumpGlobals() {
+    size_t lastBSlash = fFileName.rfind('\\');
+    size_t lastSlash = fFileName.rfind('/');
+    size_t lastDotH = fFileName.rfind(".h");
+    SkASSERT(string::npos != lastDotH);
+    if (string::npos != lastBSlash && (string::npos == lastSlash
+            || lastBSlash < lastSlash)) {
+        lastSlash = lastBSlash;
+    } else if (string::npos == lastSlash) {
+        lastSlash = -1;
+    }
+    lastSlash += 1;
+    string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
+    string fileName = globalsName + "_Reference.bmh";
+    fOut = fopen(fileName.c_str(), "wb");
+    if (!fOut) {
+        SkDebugf("could not open output file %s\n", globalsName.c_str());
+        return false;
+    }
+    string prefixName = globalsName.substr(0, 2);
+    string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
+        ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
+    this->writeTagNoLF("Topic", topicName);
+    this->writeTag("Alias", topicName + "_Reference");
+    this->lf(2);
+    this->writeTag("Subtopic", "Overview");
+    fIndent += 4;
+    this->writeTag("Subtopic", "Subtopic");
+    fIndent += 4;
+    this->writeTag("Populate");
+    fIndent -= 4;
+    this->writeEndTag();
+    fIndent -= 4;
+    this->writeEndTag();
+    this->lf(2);
+    if (!fIDefineMap.empty()) {
+        this->writeTag("Subtopic", "Define");
+        this->writeTag("Populate");
         this->writeEndTag();
         this->lf(2);
-        const char desc[] =
-                "Creates string representation. The representation is read by\n"
-                "internal debugging tools. The interface and implementation may be\n"
-                "suppressed by defining SK_IGNORE_TO_STRING.";
-        this->writeBlock(sizeof(desc) - 1, desc);
-        this->lf(2);
-        this->writeTag("Param", "str");
-        this->writeSpace(2);
-        this->writeString("storage for string representation");
-        this->writeSpace();
-        this->writeString("##");
-        this->lf(2);
-        return;
     }
-    this->writeBlock(token.length(), token.fStart);
-    this->lf(1);
+    if (!fIFunctionMap.empty()) {
+        this->writeTag("Subtopic", "Function");
+        this->writeTag("Populate");
+        this->writeEndTag();
+        this->lf(2);
+    }
+    if (!fIEnumMap.empty()) {
+        this->writeTag("Subtopic", "Enum");
+        this->writeTag("Populate");
+        this->writeEndTag();
+        this->lf(2);
+    }
+    if (!fITemplateMap.empty()) {
+        this->writeTag("Subtopic", "Template");
+        this->writeTag("Populate");
+        this->writeEndTag();
+        this->lf(2);
+    }
+    if (!fITypedefMap.empty()) {
+        this->writeTag("Subtopic", "Typedef");
+        this->writeTag("Populate");
+        this->writeEndTag();
+        this->lf(2);
+    }
+    if (!fIUnionMap.empty()) {
+        this->writeTag("Subtopic", "Union");
+        this->writeTag("Populate");
+        this->writeEndTag();
+        this->lf(2);
+    }
+    std::map<int, Definition*> sortedDefs;
+    for (const auto& entry : fIDefineMap) {
+        sortedDefs[entry.second->fLineCount] = entry.second;
+    }
+    for (const auto& entry : fIFunctionMap) {
+        sortedDefs[entry.second->fLineCount] = entry.second;
+    }
+    for (const auto& entry : fIEnumMap) {
+        sortedDefs[entry.second->fLineCount] = entry.second;
+    }
+    for (const auto& entry : fITemplateMap) {
+        sortedDefs[entry.second->fLineCount] = entry.second;
+    }
+    for (const auto& entry : fITypedefMap) {
+        sortedDefs[entry.second->fLineCount] = entry.second;
+    }
+    for (const auto& entry : fIUnionMap) {
+        sortedDefs[entry.second->fLineCount] = entry.second;
+    }
+    for (const auto& entry : sortedDefs) {
+        const Definition* def = entry.second;
+        this->writeBlockSeparator();
+        switch (def->fMarkType) {
+            case MarkType::kDefine:
+                this->dumpDefine(*def);
+                break;
+            case MarkType::kMethod:
+                this->dumpMethod(*def, globalsName);
+                break;
+            case MarkType::kEnum:
+            case MarkType::kEnumClass:
+                this->dumpEnum(*def, globalsName);
+                break;
+            case MarkType::kTemplate:
+                SkASSERT(0);  // incomplete
+                break;
+            case MarkType::kTypedef: {
+                this->writeTag("Typedef");
+                this->writeSpace();
+                TextParser parser(def);
+                if (!parser.skipExact("typedef")) {
+                    return false;
+                }
+                if (!parser.skipSpace()) {
+                    return false;
+                }
+                this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
+                this->lf(2);
+                this->dumpComment(*def);
+                this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
+                this->lf(2);
+                } continue;
+            case MarkType::kUnion:
+                SkASSERT(0);  // incomplete
+                break;
+            default:
+                SkASSERT(0);
+        }
+        this->dumpCommonTail(*def);
+    }
+    this->writeEndTag("Topic", topicName);
+    this->lfAlways(1);
+    fclose(fOut);
+    SkDebugf("wrote %s\n", fileName.c_str());
+    return true;
+}
+
+bool IncludeParser::isClone(const Definition& token) {
+    string name = token.fName;
+    return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
+}
+
+bool IncludeParser::isConstructor(const Definition& token, string className) {
+    string name = token.fName;
+    return 0 == name.find(className) || '~' == name[0];
+}
+
+bool IncludeParser::isInternalName(const Definition& token) {
+    string name = token.fName;
+    // exception for this SkCanvas function .. for now
+    if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
+        return false;
+    }
+    return name.substr(0, 7) == "android"
+            || 0 == token.fName.find("internal_")
+            || 0 == token.fName.find("Internal_")
+            || 0 == token.fName.find("legacy_")
+            || 0 == token.fName.find("temporary_")
+            || 0 == token.fName.find("private_");
+}
+
+bool IncludeParser::isOperator(const Definition& token) {
+    return "operator" == token.fName.substr(0, 8);
+}
+
+void IncludeParser::dumpMethod(const Definition& token, string className) {
+    this->writeTag("Method");
+    this->writeSpace();
+
+    string name = string(token.fStart ? token.fStart : token.fContentStart,
+            token.length());
+    if (this->isOperator(token)) {
+        string spaceConst(" const");
+        size_t constPos = name.rfind(spaceConst);
+        if (name.length() - spaceConst.length() == constPos) {
+            name = name.substr(0, constPos) + "_const";
+        }
+    }
+    this->writeString(name);
+    string inType;
+    if (this->isConstructor(token, className)) {
+        inType = "Constructor";
+    } else if (this->isOperator(token)) {
+        inType = "Operator";
+    } else {
+        inType = "incomplete";
+    }
+    this->writeTag("In", inType);
+    this->writeTag("Line");
+    this->writeSpace(1);
+    this->writeString("#");
+    this->writeSpace(1);
+    this->writeString("incomplete");
+    this->writeSpace(1);
+    this->writeString("##");
+    this->lf(2);
     this->dumpComment(token);
 }
 
@@ -871,12 +1060,15 @@
     lf(2);
 }
 
-bool IncludeParser::dumpTokens(const string& dir) {
+bool IncludeParser::dumpTokens() {
+    if (!this->dumpGlobals()) {
+        return false;
+    }
     for (const auto& member : fIClassMap) {
         if (string::npos != member.first.find("::")) {
             continue;
         }
-        if (!this->dumpTokens(dir, member.first)) {
+        if (!this->dumpTokens(member.first)) {
             return false;
         }
     }
@@ -884,12 +1076,8 @@
 }
 
     // dump equivalent markup
-bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
-    string fileName = dir;
-    if (dir.length() && '/' != dir[dir.length() - 1]) {
-        fileName += '/';
-    }
-    fileName += skClassName + "_Reference.bmh";
+bool IncludeParser::dumpTokens(string skClassName) {
+    string fileName = skClassName + "_Reference.bmh";
     fOut = fopen(fileName.c_str(), "wb");
     if (!fOut) {
         SkDebugf("could not open output file %s\n", fileName.c_str());
@@ -915,160 +1103,82 @@
         this->lf(1);
     }
     this->lf(2);
-    string className(skClassName.substr(2));
-    vector<string> classNames;
-    vector<string> constNames;
-    vector<string> constructorNames;
-    vector<string> memberNames;
-    vector<string> operatorNames;
-    size_t classMaxLen = 0;
-    size_t constMaxLen = 0;
-    size_t constructorMaxLen = 0;
-    size_t memberMaxLen = 0;
-    size_t operatorMaxLen = 0;
+    bool hasClass = false;
+    bool hasConst = !fIEnumMap.empty();
+    bool hasConstructor = false;
+    bool hasMember = false;
+    bool hasOperator = false;
     for (const auto& oneClass : fIClassMap) {
         if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
             continue;
         }
-        string structName = oneClass.first.substr(skClassName.length() + 2);
-        classMaxLen = SkTMax(classMaxLen, structName.length());
-        classNames.emplace_back(structName);
-    }
-    for (const auto& oneEnum : fIEnumMap) {
-        string enumName = oneEnum.first;
-        constMaxLen = SkTMax(constMaxLen, enumName.length());
-        constNames.emplace_back(enumName);
+        hasClass = true;
+        break;
     }
     for (const auto& token : classMap.fTokens) {
         if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
             continue;
         }
-        string name = token.fName;
-        if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
+        if (this->isInternalName(token)) {
             continue;
         }
-        if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
-            name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
-            constructorMaxLen = SkTMax(constructorMaxLen, name.length());
-            constructorNames.emplace_back(name);
+        if (this->isConstructor(token, skClassName)) {
+            hasConstructor = true;
             continue;
         }
-        if (name.substr(0, 8) == "operator") {
-            name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
-            operatorMaxLen = SkTMax(operatorMaxLen, name.length());
-            operatorNames.emplace_back(name);
+        if (this->isOperator(token)) {
+            hasOperator = true;
             continue;
         }
-        if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
+        if (this->isClone(token)) {
             continue;
         }
-        if ("SK_TO_STRING_NONVIRT" == name) {
-            name = "toString";
-        }
-        size_t paren = name.find('(');
-        size_t funcLen = string::npos == paren ? name.length() : paren;
-        memberMaxLen = SkTMax(memberMaxLen, funcLen);
-        memberNames.emplace_back(name);
+        hasMember = true;
     }
-    this->writeTag("Topic", "Overview");
-    this->lf(2);
-    this->writeTag("Subtopic", "Subtopics");
-    string classesName = classMaxLen ? "Classes_and_Structs" : "";
-    string constsName = constructorMaxLen ? "Constants" : "";
-    string constructorsName = constructorMaxLen ? "Constructors" : "";
-    string membersName = memberMaxLen ? "Member_Functions" : "";
-    string operatorsName = operatorMaxLen ? "Operators" : "";
-    size_t nameLen = SkTMax(classesName.size(), SkTMax(constsName.size(),
-            SkTMax(constructorsName.size(), SkTMax(membersName.size(), operatorsName.size()))));
-    this->writeTableHeader("name", nameLen, "description");
-    string classDesc = classMaxLen ?  "embedded struct and class members" : "";
-    string constDesc = constMaxLen ? "enum and enum class, const values" : "";
-    string constructorDesc = constructorMaxLen ? "functions that construct " + className : "";
-    string memberDesc = memberMaxLen ? "static functions and member methods" : "";
-    string operatorDesc = operatorMaxLen ? "operator overloading methods" : "";
-    size_t descLen = SkTMax(classDesc.size(), SkTMax(constDesc.size(), SkTMax(constructorDesc.size(),
-            SkTMax(memberDesc.size(), operatorDesc.size()))));
-    if (classMaxLen) {
-        this->writeTableRow(nameLen, classesName, descLen, classDesc);
-    }
-    if (constMaxLen) {
-        this->writeTableRow(nameLen, constsName, descLen, constDesc);
-    }
-    if (constructorMaxLen) {
-        this->writeTableRow(nameLen, constructorsName, descLen, constructorDesc);
-    }
-    if (memberMaxLen) {
-        this->writeTableRow(nameLen, membersName, descLen, memberDesc);
-    }
-    if (operatorMaxLen) {
-        this->writeTableRow(nameLen, operatorsName, descLen, operatorDesc);
-    }
-    this->writeTableTrailer();
+    this->writeTag("Subtopic", "Overview");
+    fIndent += 4;
+    this->writeTag("Subtopic", "Subtopic");
+    fIndent += 4;
+    this->writeTag("Populate");
+    fIndent -= 4;
+    this->writeEndTag();
+    fIndent -= 4;
     this->writeEndTag();
     this->lf(2);
-    if (classMaxLen) {
-        std::sort(classNames.begin(), classNames.end());
-        this->writeTag("Subtopic", "Classes_and_Structs");
-        this->writeTableHeader("name", classMaxLen, "description");
-         for (auto& name : classNames) {
-             this->writeTableRow(classMaxLen, name);
-        }
-        this->writeTableTrailer();
-        this->writeEndTag("Subtopic");
+
+    if (hasClass) {
+        this->writeTag("Subtopic", "Class_or_Struct");
+        this->writeTag("Populate");
+        this->writeEndTag();
         this->lf(2);
     }
-    if (constMaxLen) {
-        std::sort(constNames.begin(), constNames.end());
-        this->writeTag("Subtopic", "Constants");
-        this->writeTableHeader("name", constMaxLen, "description");
-        for (auto& name : constNames) {
-            this->writeTableRow(constMaxLen, name);
-        }
-        this->writeTableTrailer();
-        this->writeEndTag("Subtopic");
+    if (hasConst) {
+        this->writeTag("Subtopic", "Constant");
+        this->writeTag("Populate");
+        this->writeEndTag();
         this->lf(2);
     }
-    if (constructorMaxLen) {
-        std::sort(constructorNames.begin(), constructorNames.end());
-        this->writeTag("Subtopic", "Constructors");
-        this->writeTableHeader("name", constructorMaxLen, "description");
-        for (auto& name : constructorNames) {
-            this->writeTableRow(constructorMaxLen, name);
-        }
-        this->writeTableTrailer();
-        this->writeEndTag("Subtopic");
+    if (hasConstructor) {
+        this->writeTag("Subtopic", "Constructor");
+        this->writeTag("Populate");
+        this->writeEndTag();
         this->lf(2);
     }
-    if (operatorMaxLen) {
-        std::sort(operatorNames.begin(), operatorNames.end());
-        this->writeTag("Subtopic", "Operators");
-        this->writeTableHeader("name", operatorMaxLen, "description");
-        for (auto& name : operatorNames) {
-            this->writeTableRow(operatorMaxLen, name);
-        }
-        this->writeTableTrailer();
-        this->writeEndTag("Subtopic");
+    if (hasOperator) {
+        this->writeTag("Subtopic", "Operator");
+        this->writeTag("Populate");
+        this->writeEndTag();
         this->lf(2);
     }
-    if (memberMaxLen) {
-        std::sort(memberNames.begin(), memberNames.end());
-        this->writeTag("Subtopic", "Member_Functions");
-        this->writeTableHeader("name", memberMaxLen, "description");
-        for (auto& name : memberNames) {
-            size_t paren = name.find('(');
-            size_t funcLen = string::npos == paren ? name.length() : paren;
-            this->writeTableRow(memberMaxLen, name.substr(0, funcLen));
-        }
-        this->writeTableTrailer();
-        this->writeEndTag("Subtopic");
+    if (hasMember) {
+        this->writeTag("Subtopic", "Member_Function");
+        this->writeTag("Populate");
+        this->writeEndTag();
         this->lf(2);
     }
-    this->writeEndTag("Topic");
-    this->lf(2);
     for (auto& oneEnum : fIEnumMap) {
-        this->writeString(
-            "# ------------------------------------------------------------------------------");
-        this->dumpEnum(oneEnum.second, oneEnum.first);
+        this->writeBlockSeparator();
+        this->dumpEnum(*oneEnum.second, oneEnum.first);
         this->lf(2);
         this->writeTag("Example");
         this->lfcr();
@@ -1085,9 +1195,7 @@
             continue;
         }
         string innerName = oneClass.first.substr(skClassName.length() + 2);
-        this->writeString(
-            "# ------------------------------------------------------------------------------");
-        this->lf(2);
+        this->writeBlockSeparator();
         KeyWord keyword = oneClass.second.fKeyWord;
         SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
         const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
@@ -1159,14 +1267,6 @@
     return true;
 }
 
-bool IncludeParser::internalName(const Definition& token) const {
-    return 0 == token.fName.find("internal_")
-            || 0 == token.fName.find("Internal_")
-            || 0 == token.fName.find("legacy_")
-            || 0 == token.fName.find("temporary_")
-            || 0 == token.fName.find("private_");
-}
-
 // caller calls reportError, so just return false here
 bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
     SkASSERT(includeDef->fTokens.size() > 0);
@@ -1267,7 +1367,7 @@
     return true;
 }
 
-bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
+bool IncludeParser::parseComment(string filename, const char* start, const char* end,
         int lineCount, Definition* markupDef) {
     TextParser parser(filename, start, end, lineCount);
     // parse doxygen if present
@@ -1277,14 +1377,26 @@
         parser.skipWhiteSpace();
         if ('\\' == parser.peek()) {
             parser.next();
-            if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
-                return reportError<bool>("missing object type");
+            // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
+            if (parser.skipExact("file")) {
+                if (Definition::Type::kFileType != fParent->fType) {
+                    return reportError<bool>("expected parent is file");
+                }
+                string filename = markupDef->fileName();
+                if (!parser.skipWord(filename.c_str())) {
+                    return reportError<bool>("missing object type");
+                }
+            } else if (parser.skipExact("fn")) {
+                SkASSERT(0);  // incomplete
+            } else {
+                if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
+                    return reportError<bool>("missing object type");
+                }
+                if (!parser.skipWord(markupDef->fName.c_str()) &&
+                        KeyWord::kEnum != markupDef->fKeyWord) {
+                    return reportError<bool>("missing object name");
+                }
             }
-            if (!parser.skipWord(markupDef->fName.c_str()) &&
-                    KeyWord::kEnum != markupDef->fKeyWord) {
-                return reportError<bool>("missing object name");
-            }
-
         }
     }
     // remove leading '*' if present
@@ -1308,8 +1420,87 @@
     return true;
 }
 
-bool IncludeParser::parseDefine() {
-
+bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
+    TextParser parser(child);
+    if (!parser.skipExact("#define")) {
+        return false;
+    }
+    if (!parser.skipSpace()) {
+        return false;
+    }
+    const char* nameStart = parser.fChar;
+    parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
+    if (parser.eof()) {
+        return true;    // do nothing if #define doesn't define anything
+    }
+    string nameStr(nameStart, parser.fChar - nameStart);
+    struct Param {
+        const char* fStart;
+        const char* fEnd;
+    };
+    vector<Param> params;
+    if ('(' == parser.peek()) {
+        parser.next();
+        if (!parser.skipSpace()) {
+            return false;
+        }
+        do {
+            const char* paramStart = parser.fChar;
+            if (!parser.skipExact("...")) {
+                parser.skipToNonAlphaNum();
+            }
+            if (parser.eof()) {
+                return false;
+            }
+            params.push_back({paramStart, parser.fChar});
+            if (!parser.skipSpace()) {
+                return false;
+            }
+            if (')' == parser.peek()) {
+                parser.next();
+                break;
+            }
+            if (',' != parser.next()) {
+                return false;
+            }
+            if (!parser.skipSpace()) {
+                return false;
+            }
+        } while (true);
+    }
+    if (!parser.skipSpace()) {
+        return false;
+    }
+    if (!markupDef) {
+        fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
+                child->fLineCount, fParent, '\0');
+        Definition* globalMarkupChild = &fGlobals.back();
+        string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
+        globalMarkupChild->fName = globalUniqueName;
+        globalMarkupChild->fTerminator = child->fContentEnd;
+        if (!this->findComments(*child, globalMarkupChild)) {
+            return false;
+        }
+        fIDefineMap[globalUniqueName] = globalMarkupChild;
+        for (Param param : params) {
+            globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
+                    child->fLineCount, globalMarkupChild, '\0');
+            Definition* paramChild = &globalMarkupChild->fTokens.back();
+            paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
+            paramChild->fTerminator = param.fEnd;
+        }
+        return true;
+    }
+    markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
+            child->fLineCount, markupDef, '\0');
+    Definition* markupChild = &markupDef->fTokens.back();
+    markupChild->fName = nameStr;
+    markupChild->fTerminator = markupChild->fContentEnd;
+    IClassDefinition& classDef = fIClassMap[markupDef->fName];
+    if (!this->findComments(*child, markupChild)) {
+        return false;
+    }
+    classDef.fDefines[nameStr] = markupChild;
     return true;
 }
 
@@ -1332,17 +1523,13 @@
     }
     Definition* markupChild;
     if (!markupDef) {
-        auto finder = fIEnumMap.find(nameStr);
-        if (fIEnumMap.end() != finder) {
-            return child->reportError<bool>("duplicate global enum name");
-        }
-        markupChild = &fIEnumMap[nameStr];
-        markupChild->fContentStart = child->fContentStart;
-        markupChild->fName = nameStr;
-        markupChild->fFiddle = nameStr;
-        markupChild->fContentEnd = child->fContentEnd;
-        markupChild->fFileName = child->fFileName;
-        markupChild->fLineCount = child->fLineCount;
+        fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
+                child->fLineCount, fParent, '\0');
+        markupChild = &fGlobals.back();
+        string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
+        markupChild->fName = globalUniqueName;
+        markupChild->fTerminator = child->fContentEnd;
+        fIEnumMap[globalUniqueName] = markupChild;
     } else {
         markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
             child->fLineCount, markupDef, '\0');
@@ -1407,7 +1594,7 @@
         }
         // if there's comment on same the line as member def, output first as if it was before
 
-        parser.skipToNonAlphaNum();
+        parser.skipToNonName();
         string memberName(memberStart, parser.fChar);
         if (parser.eof() || !parser.skipWhiteSpace()) {
             return this->reportError<bool>("enum member must end with comma 1");
@@ -1486,7 +1673,7 @@
     return true;
 }
 
-bool IncludeParser::parseInclude(const string& name) {
+bool IncludeParser::parseInclude(string name) {
     fParent = &fIncludeMap[name];
     fParent->fName = name;
     fParent->fFileName = fFileName;
@@ -1513,7 +1700,7 @@
         child->fLineCount, markupDef, '\0');
     Definition* markupChild = &markupDef->fTokens.back();
     TextParser nameParser(child);
-    nameParser.skipToNonAlphaNum();
+    nameParser.skipToNonName();
     string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
     IClassDefinition& classDef = fIClassMap[markupDef->fName];
     string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
@@ -1667,19 +1854,15 @@
         if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
             return true;  // expect this is inline class definition outside of class
         }
-        string name(nameParser.fLine, nameParser.lineLength());
-        auto finder = fIFunctionMap.find(name);
-        if (fIFunctionMap.end() != finder) {
-            // create unique name
-            SkASSERT(0);  // incomplete
+        fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
+                fParent, '\0');
+        Definition* globalMarkupChild = &fGlobals.back();
+        string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
+        globalMarkupChild->fName = globalUniqueName;
+        if (!this->findComments(*child, globalMarkupChild)) {
+            return false;
         }
-        auto globalFunction = &fIFunctionMap[name];
-        globalFunction->fContentStart = start;
-        globalFunction->fName = name;
-        globalFunction->fFiddle = name;
-        globalFunction->fContentEnd = end;
-        globalFunction->fMarkType = MarkType::kMethod;
-        globalFunction->fLineCount = tokenIter->fLineCount;
+        fIFunctionMap[globalUniqueName] = globalMarkupChild;
         return true;
     }
     markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
@@ -1799,7 +1982,7 @@
                         case KeyWord::kIf:
                         case KeyWord::kIfndef:
                         case KeyWord::kIfdef:
-                            if (child->boilerplateIfDef(fParent)) {
+                            if (child->boilerplateIfDef()) {
                                 if (!this->parseObjects(child, markupDef)) {
                                     return false;
                                 }
@@ -1807,7 +1990,7 @@
                             }
                             goto preproError;
                         case KeyWord::kDefine:
-                            if (child->boilerplateDef(fParent)) {
+                            if (this->parseDefine(child, markupDef)) {
                                 break;
                             }
                             goto preproError;
@@ -1873,16 +2056,15 @@
     typedefParser.skipWhiteSpace();
     string nameStr = typedefParser.typedefName();
     if (!markupDef) {
-        Definition& typedefDef = fITypedefMap[nameStr];
-        SkASSERT(!typedefDef.fStart);
-        typedefDef.fStart = child->fContentStart;
-        typedefDef.fContentStart = child->fContentStart;
-        typedefDef.fName = nameStr;
-        typedefDef.fFiddle = nameStr;
-        typedefDef.fContentEnd = child->fContentEnd;
-        typedefDef.fTerminator = child->fContentEnd;
-        typedefDef.fMarkType = MarkType::kTypedef;
-        typedefDef.fLineCount = child->fLineCount;
+        fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
+                child->fLineCount, fParent, '\0');
+        Definition* globalMarkupChild = &fGlobals.back();
+        string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
+        globalMarkupChild->fName = globalUniqueName;
+        if (!this->findComments(*child, globalMarkupChild)) {
+            return false;
+        }
+        fITypedefMap[globalUniqueName] = globalMarkupChild;
         return true;
     }
     markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
@@ -1927,9 +2109,17 @@
                 if (KeyWord::kNone == keyWord) {
                     return this->reportError<bool>("unhandled preprocessor directive");
                 }
+                if (fInDefine) {
+                    SkASSERT(KeyWord::kDefine == keyWord);
+                    fInDefine = false;
+                }
                 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
                     this->popBracket();
                 }
+                if (fInBrace) {
+                    SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
+                    fInBrace = nullptr;
+                }
             } else if (Bracket::kSlashSlash == this->topBracket()) {
                 this->popBracket();
             }
@@ -1986,18 +2176,21 @@
                 this->pushBracket(Bracket::kString);
             }
             break;
-        case ':':
         case '(':
-        case '[':
-        case '{': {
-            if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
+            if (fIncludeWord && fChar - fIncludeWord >= 10 &&
                     !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
                 this->pushBracket(Bracket::kDebugCode);
                 break;
             }
+        case ':':
+        case '[':
+        case '{': {
             if (fInCharCommentString) {
                 break;
             }
+            if (fInDefine && fInBrace) {
+                break;
+            }
             if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
                 break;
             }
@@ -2051,6 +2244,9 @@
             if (fInCharCommentString) {
                 break;
             }
+            if (fInDefine && fInBrace) {
+                break;
+            }
             if (!fInBrace) {
                 if (!this->checkForWord()) {
                     return false;
@@ -2104,9 +2300,14 @@
             this->pushBracket(Bracket::kPound);
             break;
         }
+        case ' ':
+            if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
+                SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
+                fInBrace = fParent;
+                // delimiting brackets are space ... unescaped-linefeed
+            }
         case '&':
         case ',':
-        case ' ':
         case '+':
         case '=':
         case '-':
@@ -2260,7 +2461,7 @@
                         std::advance(tokenWalker, 1);
                         if (tokenWalker != fParent->fTokens.end()) {
                             TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
-                            tp.skipToNonAlphaNum();
+                            tp.skipToNonName();
                             start->fName = string(nameStart, tp.fChar - nameStart);
                             start->fContentEnd = fChar;
                             priorEnum->fChildren.emplace_back(start);
diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp
index 9fd238a..daf4c36 100644
--- a/tools/bookmaker/includeWriter.cpp
+++ b/tools/bookmaker/includeWriter.cpp
@@ -684,9 +684,6 @@
     fContinuation = nullptr;
     fDeferComment = nullptr;
     const Definition* csParent = method->csParent();
-    if (!csParent) {
-        SkDebugf("");
-    }
     if (csParent && (0 == fIndent || fIndentNext)) {
         fIndent += 4;
         fIndentNext = false;
@@ -721,22 +718,7 @@
             }
             this->indentToColumn(column);
             fIndent = column;
-#if 0
-            const char* partStart = methodPart->fContentStart;
-            const char* partEnd = methodPart->fContentEnd;
-            while ('\n' == partEnd[-1]) {
-                --partEnd;
-            }
-            while ('#' == partEnd[-1]) { // FIXME: so wrong; should not be before fContentEnd
-                --partEnd;
-            }
-            int partLen = (int) (partEnd - partStart);
-            // FIXME : detect this earlier; assert if #Return is empty
-            SkASSERT(partLen > 0 && partLen < 300);  // may assert if param desc is especially long
-            this->rewriteBlock(partLen, partStart, Phrase::kYes);
-#else
             this->descriptionOut(methodPart, SkipFirstLine::kNo, Phrase::kYes);
-#endif
             fIndent = saveIndent;
             this->lfcr();
         }
@@ -784,7 +766,7 @@
 }
 
 Definition* IncludeWriter::findMemberCommentBlock(const vector<Definition*>& bmhChildren,
-        const string& name) const {
+        string name) const {
     for (auto memberDef : bmhChildren) {
         if (MarkType::kMember != memberDef->fMarkType) {
             continue;
@@ -1222,7 +1204,7 @@
             continue;
         }
         if (MarkType::kMethod == child.fMarkType) {
-            if (this->internalName(child)) {
+            if (this->isInternalName(child)) {
                 continue;
             }
             const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
@@ -1242,6 +1224,7 @@
                 --bodyEnd;
             }
             int blockSize = (int) (bodyEnd - fStart);
+            SkASSERT(blockSize >= 0);
             if (blockSize) {
                 string debugstr(fStart, blockSize);
                 this->writeBlock(blockSize, fStart);
@@ -1617,6 +1600,7 @@
         RootDefinition* root = &bmhParser.fClassMap[skClassName];
         fRootTopic = root->fParent;
         root->clearVisited();
+        fFileName = includeMapper.second.fFileName;
         fStart = includeMapper.second.fContentStart;
         fEnd = includeMapper.second.fContentEnd;
         fAnonymousEnumCount = 1;
diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp
index 4b4e2b8..db6a8cf 100644
--- a/tools/bookmaker/mdOut.cpp
+++ b/tools/bookmaker/mdOut.cpp
@@ -16,11 +16,11 @@
     }                               \
     fprintf(fOut, __VA_ARGS__)
 
-static void add_ref(const string& leadingSpaces, const string& ref, string* result) {
+static void add_ref(string leadingSpaces, string ref, string* result) {
     *result += leadingSpaces + ref;
 }
 
-static string preformat(const string& orig) {
+static string preformat(string orig) {
     string result;
     for (auto c : orig) {
         if ('<' == c) {
@@ -34,7 +34,7 @@
     return result;
 }
 
-static bool all_lower(const string& ref) {
+static bool all_lower(string ref) {
 	for (auto ch : ref) {
 		if (!islower(ch)) {
 			return false;
@@ -57,6 +57,12 @@
         const char* base = t.fChar;
         t.skipWhiteSpace();
         const char* wordStart = t.fChar;
+        if (BmhParser::Resolvable::kFormula == resolvable && !t.eof() && '"' == t.peek()) {
+            t.next();
+            t.skipToEndBracket('"');
+            t.next();
+            continue;
+        }
         t.skipToMethodStart();
         const char* start = t.fChar;
         if (wordStart < start) {
@@ -179,14 +185,16 @@
             // look for Sk / sk / SK ..
         if (!ref.compare(0, 2, "Sk") && ref != "Skew" && ref != "Skews" &&
               ref != "Skip" && ref != "Skips") {
-            if (BmhParser::Resolvable::kOut != resolvable) {
+            if (BmhParser::Resolvable::kOut != resolvable &&
+                    BmhParser::Resolvable::kFormula != resolvable) {
                 t.reportError("missed Sk prefixed");
                 fAddRefFailed = true;
                 return result;
             }
         }
         if (!ref.compare(0, 2, "SK")) {
-            if (BmhParser::Resolvable::kOut != resolvable) {
+            if (BmhParser::Resolvable::kOut != resolvable &&
+                    BmhParser::Resolvable::kFormula != resolvable) {
                 t.reportError("missed SK prefixed");
                 fAddRefFailed = true;
                 return result;
@@ -196,7 +204,7 @@
             // TODO:
             // look for all lowercase w/o trailing parens as mistaken method matches
             // will also need to see if Example Description matches var in example
-            const Definition* def;
+            const Definition* def = nullptr;
             if (fMethod && (def = fMethod->hasParam(ref))) {
 				result += linkRef(leadingSpaces, def, ref, resolvable);
                 fLastParam = def;
@@ -268,7 +276,8 @@
         } while ((test = test->fParent));
     found:
         if (!test) {
-            if (BmhParser::Resolvable::kOut != resolvable) {
+            if (BmhParser::Resolvable::kOut != resolvable &&
+                    BmhParser::Resolvable::kFormula != resolvable) {
                 t.reportError("undefined reference");
                 fAddRefFailed = true;
             } else {
@@ -385,11 +394,13 @@
         fPopulators[kClassesAndStructs].fDescription = "embedded struct and class members";
         fPopulators[kConstants].fDescription = "enum and enum class, const values";
         fPopulators[kConstructors].fDescription = "functions that construct";
+        fPopulators[kDefines].fDescription = "preprocessor definitions of functions, values";
         fPopulators[kMemberFunctions].fDescription = "static functions and member methods";
         fPopulators[kMembers].fDescription = "member values";
         fPopulators[kOperators].fDescription = "operator overloading methods";
         fPopulators[kRelatedFunctions].fDescription = "similar methods grouped together";
         fPopulators[kSubtopics].fDescription = "";
+        fPopulators[kTypedefs].fDescription = "types defined by other types";
         this->populateTables(fRoot);
         this->markTypeOut(topicDef);
     }
@@ -416,7 +427,7 @@
     TextParser paramBody(def);
     const char* descriptionStart = paramBody.fChar;
     if (!islower(descriptionStart[0]) && !isdigit(descriptionStart[0])) {
-        paramBody.skipToNonAlphaNum();
+        paramBody.skipToNonName();
         string ref = string(descriptionStart, paramBody.fChar - descriptionStart);
         if (!this->isDefined(paramBody, ref, BmhParser::Resolvable::kYes)) {
             string errorStr = MarkType::kReturn == def->fMarkType ? "return" : "param";
@@ -504,7 +515,7 @@
     return nullptr;
 }
 
-const Definition* MdOut::isDefined(const TextParser& parser, const string& ref,
+const Definition* MdOut::isDefined(const TextParser& parser, string ref,
         BmhParser::Resolvable resolvable) {
     auto rootIter = fBmhParser.fClassMap.find(ref);
     if (rootIter != fBmhParser.fClassMap.end()) {
@@ -530,6 +541,10 @@
     if (aliasIter != fBmhParser.fAliasMap.end()) {
         return aliasIter->second;
     }
+    auto defineIter = fBmhParser.fDefineMap.find(ref);
+    if (defineIter != fBmhParser.fDefineMap.end()) {
+        return &defineIter->second;
+    }
     for (const auto& external : fBmhParser.fExternals) {
         if (external.fName == ref) {
             return &external;
@@ -682,8 +697,8 @@
 
 // for now, hard-code to html links
 // def should not include SkXXX_
-string MdOut::linkRef(const string& leadingSpaces, const Definition* def,
-        const string& ref, BmhParser::Resolvable resolvable) const {
+string MdOut::linkRef(string leadingSpaces, const Definition* def,
+        string ref, BmhParser::Resolvable resolvable) const {
     string buildup;
     string refName;
     const string* str = &def->fFiddle;
@@ -1015,7 +1030,7 @@
             paramParser.skipWhiteSpace();
             SkASSERT(paramParser.startsWith("#Param"));
             paramParser.next(); // skip hash
-            paramParser.skipToNonAlphaNum(); // skip Param
+            paramParser.skipToNonName(); // skip Param
             paramParser.skipSpace();
             const char* paramName = paramParser.fChar;
             paramParser.skipToSpace();
@@ -1140,7 +1155,7 @@
             break;
         default:
             SkDebugf("fatal error: MarkType::k%s unhandled in %s()\n",
-                    fBmhParser.fMaps[(int) def->fMarkType].fName, __func__);
+                    BmhParser::kMarkProps[(int) def->fMarkType].fName, __func__);
             SkASSERT(0); // handle everything
             break;
     }
@@ -1276,9 +1291,9 @@
         if (MarkType::kTopic == child->fMarkType || MarkType::kSubtopic == child->fMarkType) {
             string name = child->fName;
             bool builtInTopic = name == kClassesAndStructs || name == kConstants
-                    || name == kConstructors || name == kMemberFunctions || name == kMembers
-                    || name == kOperators || name == kOverview || name == kRelatedFunctions
-                    || name == kSubtopics;
+                    || name == kConstructors || name == kDefines || name == kMemberFunctions
+                    || name == kMembers || name == kOperators || name == kOverview
+                    || name == kRelatedFunctions || name == kSubtopics || name == kTypedefs;
             if (!builtInTopic && child->fName != kOverview) {
                 this->populator(kRelatedFunctions).fMembers.push_back(child);
             }
@@ -1298,10 +1313,16 @@
             this->populator(kConstants).fMembers.push_back(child);
             continue;
         }
+        if (MarkType::kDefine == child->fMarkType) {
+            this->populator(kDefines).fMembers.push_back(child);
+        }
         if (MarkType::kMember == child->fMarkType) {
             this->populator(kMembers).fMembers.push_back(child);
             continue;
         }
+        if (MarkType::kTypedef == child->fMarkType) {
+            this->populator(kTypedefs).fMembers.push_back(child);
+        }
         if (MarkType::kMethod != child->fMarkType) {
             continue;
         }
@@ -1410,7 +1431,7 @@
     }
 }
 
-void MdOut::rowOut(const char* name, const string& description) {
+void MdOut::rowOut(const char* name, string description) {
     this->lfAlways(1);
     FPRINTF("| ");
     this->resolveOut(name, name + strlen(name), BmhParser::Resolvable::kYes);
@@ -1425,8 +1446,8 @@
     SkASSERT(csParent);
     this->rowOut("name", "description");
     this->rowOut("---", "---");
-    for (auto item : { kClassesAndStructs, kConstants, kConstructors, kMemberFunctions,
-            kMembers, kOperators, kRelatedFunctions } ) {
+    for (auto item : { kClassesAndStructs, kConstants, kConstructors, kDefines,
+            kMemberFunctions, kMembers, kOperators, kRelatedFunctions, kTypedefs } ) {
         for (auto entry : this->populator(item).fMembers) {
             if (entry->csParent() == csParent) {
                 string description = fPopulators.find(item)->second.fDescription;
diff --git a/tools/bookmaker/spellCheck.cpp b/tools/bookmaker/spellCheck.cpp
index e5ad746..488ceac 100644
--- a/tools/bookmaker/spellCheck.cpp
+++ b/tools/bookmaker/spellCheck.cpp
@@ -47,7 +47,7 @@
     void childCheck(const Definition* def, const char* start);
     void leafCheck(const char* start, const char* end);
     bool parseFromFile(const char* path) override { return true; }
-    void printCheck(const string& str);
+    void printCheck(string str);
 
     void reset() override {
         INHERITED::resetCommon();
@@ -60,7 +60,7 @@
         fInStdOut = false;
     }
 
-    void wordCheck(const string& str);
+    void wordCheck(string str);
     void wordCheck(ptrdiff_t len, const char* ch);
 
     unordered_map<string, CheckEntry> fCode;
@@ -121,7 +121,7 @@
     return true;
 }
 
-static bool all_lower(const string& str) {
+static bool all_lower(string str) {
     for (auto c : str) {
         if (!islower(c)) {
             return false;
@@ -231,7 +231,7 @@
             paramParser.skipWhiteSpace();
             SkASSERT(paramParser.startsWith("#Param"));
             paramParser.next(); // skip hash
-            paramParser.skipToNonAlphaNum(); // skip Param
+            paramParser.skipToNonName(); // skip Param
             paramParser.skipSpace();
             const char* paramName = paramParser.fChar;
             paramParser.skipToSpace();
@@ -345,7 +345,7 @@
 }
 
 bool SpellCheck::checkable(MarkType markType) {
-    return BmhParser::Resolvable::kYes == fBmhParser.fMaps[(int) markType].fResolve;
+    return BmhParser::Resolvable::kYes == fBmhParser.kMarkProps[(int) markType].fResolve;
 }
 
 void SpellCheck::childCheck(const Definition* def, const char* start) {
@@ -459,7 +459,7 @@
     } while (++chPtr <= end);
 }
 
-void SpellCheck::printCheck(const string& str) {
+void SpellCheck::printCheck(string str) {
     string word;
     for (std::stringstream stream(str); stream >> word; ) {
         wordCheck(word);
@@ -566,7 +566,7 @@
     }
 }
 
-void SpellCheck::wordCheck(const string& str) {
+void SpellCheck::wordCheck(string str) {
     if ("nullptr" == str) {
         return;  // doesn't seem worth it, treating nullptr as a word in need of correction
     }