Home | History | Annotate | Download | only in bookmaker
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef bookmaker_DEFINED
      9 #define bookmaker_DEFINED
     10 
     11 #include "SkCommandLineFlags.h"
     12 #include "SkData.h"
     13 #include "SkJSONCPP.h"
     14 
     15 #include <algorithm>
     16 #include <cmath>
     17 #include <cctype>
     18 #include <forward_list>
     19 #include <list>
     20 #include <string>
     21 #include <unordered_map>
     22 #include <vector>
     23 
     24 // std::to_string isn't implemented on android
     25 #include <sstream>
     26 
     27 template <typename T>
     28 std::string to_string(T value)
     29 {
     30     std::ostringstream os ;
     31     os << value ;
     32     return os.str() ;
     33 }
     34 
     35 using std::forward_list;
     36 using std::list;
     37 using std::unordered_map;
     38 using std::string;
     39 using std::vector;
     40 
     41 enum class KeyWord {
     42     kNone,
     43     kSK_API,
     44     kSK_BEGIN_REQUIRE_DENSE,
     45     kBool,
     46     kChar,
     47     kClass,
     48     kConst,
     49     kConstExpr,
     50     kDefine,
     51     kDouble,
     52     kElif,
     53     kElse,
     54     kEndif,
     55     kEnum,
     56     kError,
     57     kFloat,
     58     kFriend,
     59     kIf,
     60     kIfdef,
     61     kIfndef,
     62     kInclude,
     63     kInline,
     64     kInt,
     65     kOperator,
     66     kPrivate,
     67     kProtected,
     68     kPublic,
     69     kSigned,
     70     kSize_t,
     71     kStatic,
     72     kStruct,
     73     kTemplate,
     74     kTypedef,
     75     kUint16_t,
     76     kUint32_t,
     77     kUint64_t,
     78     kUint8_t,
     79     kUnion,
     80     kUnsigned,
     81     kVoid,
     82 };
     83 
     84 enum class MarkType {
     85     kNone,
     86     kAnchor,
     87     kAlias,
     88     kBug,
     89     kClass,
     90     kCode,
     91     kColumn,
     92     kComment,
     93     kConst,
     94     kDefine,
     95     kDefinedBy,
     96     kDeprecated,
     97     kDescription,
     98     kDoxygen,
     99     kDuration,
    100     kEnum,
    101     kEnumClass,
    102     kExample,
    103     kExperimental,
    104     kExternal,
    105     kFile,
    106     kFormula,
    107     kFunction,
    108     kHeight,
    109     kIllustration,
    110     kImage,
    111 	kIn,
    112     kLegend,
    113 	kLine,
    114     kLink,
    115     kList,
    116     kLiteral,  // don't lookup hyperlinks, do substitution, etc
    117     kMarkChar,
    118     kMember,
    119     kMethod,
    120     kNoExample,
    121     kOutdent,
    122     kParam,
    123     kPlatform,
    124     kPopulate,
    125     kPrivate,
    126     kReturn,
    127     kRoot,
    128     kRow,
    129     kSeeAlso,
    130     kSet,
    131     kStdOut,
    132     kStruct,
    133     kSubstitute,
    134     kSubtopic,
    135     kTable,
    136     kTemplate,
    137     kText,
    138     kTime,
    139     kToDo,
    140     kTopic,
    141     kTrack,
    142     kTypedef,
    143     kUnion,
    144     kVolatile,
    145     kWidth,
    146 };
    147 
    148 enum {
    149     Last_MarkType = (int) MarkType::kWidth,
    150 };
    151 
    152 enum class Bracket {
    153     kNone,
    154     kParen,
    155     kSquare,
    156     kBrace,
    157     kAngle,
    158     kString,
    159     kChar,
    160     kSlashStar,
    161     kSlashSlash,
    162     kPound,
    163     kColon,
    164     kDebugCode,  // parens get special treatment so SkDEBUGCODE( isn't treated as method
    165 };
    166 
    167 enum class Punctuation {  // catch-all for misc symbols tracked in C
    168     kNone,
    169     kAsterisk,  // for pointer-to
    170     kSemicolon,  // e.g., to delinate xxx() const ; const int* yyy()
    171     kLeftBrace,
    172     kColon,     // for foo() : bar(1), baz(2) {}
    173 };
    174 
    175 enum class KeyProperty {
    176     kNone,
    177     kClassSection,
    178     kFunction,
    179     kModifier,
    180     kNumber,
    181     kObject,
    182     kPreprocessor,
    183 };
    184 
    185 enum class StatusFilter {
    186     kCompleted,
    187     kInProgress,
    188     kUnknown,
    189 };
    190 
    191 struct IncludeKey {
    192     const char* fName;
    193     KeyWord fKeyWord;
    194     KeyProperty fProperty;
    195 };
    196 
    197 extern const IncludeKey kKeyWords[];
    198 
    199 static inline bool has_nonwhitespace(const string& s) {
    200     bool nonwhite = false;
    201     for (const char& c : s) {
    202         if (' ' < c) {
    203             nonwhite = true;
    204             break;
    205         }
    206     }
    207     return nonwhite;
    208 }
    209 
    210 static inline void trim_end(string &s) {
    211     s.erase(std::find_if(s.rbegin(), s.rend(),
    212             std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    213 }
    214 
    215 static inline void trim_end_spaces(string &s) {
    216     while (s.length() > 0 && ' ' == s.back()) {
    217         s.pop_back();
    218     }
    219 }
    220 
    221 static inline void trim_start(string &s) {
    222     s.erase(s.begin(), std::find_if(s.begin(), s.end(),
    223             std::not1(std::ptr_fun<int, int>(std::isspace))));
    224 }
    225 
    226 static inline void trim_start_end(string& s) {
    227     trim_start(s);
    228     trim_end(s);
    229 }
    230 
    231 class NonAssignable {
    232 public:
    233     NonAssignable(NonAssignable const&) = delete;
    234     NonAssignable& operator=(NonAssignable const&) = delete;
    235     NonAssignable() {}
    236 };
    237 
    238 class Definition;
    239 
    240 class TextParser : public NonAssignable {
    241     TextParser() {}  // only for ParserCommon to call
    242     friend class ParserCommon;
    243 public:
    244     virtual ~TextParser() {}
    245     class Save {
    246     public:
    247         Save(TextParser* parser) {
    248             fParser = parser;
    249             fLine = parser->fLine;
    250             fChar = parser->fChar;
    251             fLineCount = parser->fLineCount;
    252         }
    253 
    254         void restore() const {
    255             fParser->fLine = fLine;
    256             fParser->fChar = fChar;
    257             fParser->fLineCount = fLineCount;
    258         }
    259 
    260     private:
    261         TextParser* fParser;
    262         const char* fLine;
    263         const char* fChar;
    264         int fLineCount;
    265     };
    266 
    267     TextParser(const string& fileName, const char* start, const char* end, int lineCount)
    268         : fFileName(fileName)
    269         , fStart(start)
    270         , fLine(start)
    271         , fChar(start)
    272         , fEnd(end)
    273         , fLineCount(lineCount)
    274     {
    275     }
    276 
    277     TextParser(const Definition* );
    278 
    279     const char* anyOf(const char* str) const {
    280         const char* ptr = fChar;
    281         while (ptr < fEnd) {
    282             if (strchr(str, ptr[0])) {
    283                 return ptr;
    284             }
    285             ++ptr;
    286         }
    287         return nullptr;
    288     }
    289 
    290     const char* anyOf(const char* wordStart, const char* wordList[], size_t wordListCount) const {
    291         const char** wordPtr = wordList;
    292         const char** wordEnd = wordPtr + wordListCount;
    293         const size_t matchLen = fChar - wordStart;
    294         while (wordPtr < wordEnd) {
    295             const char* word = *wordPtr++;
    296             if (strlen(word) == matchLen && !strncmp(wordStart, word, matchLen)) {
    297                 return word;
    298             }
    299         }
    300         return nullptr;
    301     }
    302 
    303     char backup(const char* pattern) const {
    304         size_t len = strlen(pattern);
    305         const char* start = fChar - len;
    306         if (start <= fStart) {
    307             return '\0';
    308         }
    309         if (strncmp(start, pattern, len)) {
    310             return '\0';
    311         }
    312         return start[-1];
    313     }
    314 
    315     bool contains(const char* match, const char* lineEnd, const char** loc) const {
    316         *loc = this->strnstr(match, lineEnd);
    317         return *loc;
    318     }
    319 
    320     const char* doubleLF() const {
    321         int count = 0;
    322         const char* ptr = fChar;
    323         const char* doubleStart = nullptr;
    324         while (ptr < fEnd) {
    325             if ('\n' == ptr[0]) {
    326                 if (++count == 1) {
    327                     doubleStart = ptr;
    328                 } else {
    329                     return doubleStart;
    330                 }
    331             } else if (' ' < ptr[0]) {
    332                 count = 0;
    333             }
    334             ++ptr;
    335         }
    336         return nullptr;
    337     }
    338 
    339     bool endsWith(const char* match) {
    340         int matchLen = strlen(match);
    341         if (matchLen > fChar - fLine) {
    342             return false;
    343         }
    344         return !strncmp(fChar - matchLen, match, matchLen);
    345     }
    346 
    347     bool eof() const { return fChar >= fEnd; }
    348 
    349     const char* lineEnd() const {
    350         const char* ptr = fChar;
    351         do {
    352             if (ptr >= fEnd) {
    353                 return ptr;
    354             }
    355             char test = *ptr++;
    356             if (test == '\n' || test == '\0') {
    357                 break;
    358             }
    359         } while (true);
    360         return ptr;
    361     }
    362 
    363     ptrdiff_t lineLength() const {
    364         return this->lineEnd() - fLine;
    365     }
    366 
    367     bool match(TextParser* );
    368 
    369     char next() {
    370         SkASSERT(fChar < fEnd);
    371         char result = *fChar++;
    372         if ('\n' == result) {
    373             ++fLineCount;
    374             fLine = fChar;
    375         }
    376         return result;
    377     }
    378 
    379     char peek() const { SkASSERT(fChar < fEnd); return *fChar; }
    380 
    381     void restorePlace(const TextParser& save) {
    382         fChar = save.fChar;
    383         fLine = save.fLine;
    384         fLineCount = save.fLineCount;
    385     }
    386 
    387     void savePlace(TextParser* save) {
    388         save->fChar = fChar;
    389         save->fLine = fLine;
    390         save->fLineCount = fLineCount;
    391     }
    392 
    393     void reportError(const char* errorStr) const;
    394     static string ReportFilename(string file);
    395     void reportWarning(const char* errorStr) const;
    396 
    397     template <typename T> T reportError(const char* errorStr) const {
    398         this->reportError(errorStr);
    399         return T();
    400     }
    401 
    402     bool sentenceEnd(const char* check) const {
    403         while (check > fStart) {
    404             --check;
    405             if (' ' < check[0] && '.' != check[0]) {
    406                 return false;
    407             }
    408             if ('.' == check[0]) {
    409                 return ' ' >= check[1];
    410             }
    411             if ('\n' == check[0] && '\n' == check[1]) {
    412                 return true;
    413             }
    414         }
    415         return true;
    416     }
    417 
    418     bool skipToEndBracket(char endBracket, const char* end = nullptr) {
    419         if (nullptr == end) {
    420             end = fEnd;
    421         }
    422         while (fChar[0] != endBracket) {
    423             if (fChar >= end) {
    424                 return false;
    425             }
    426             (void) this->next();
    427         }
    428         return true;
    429     }
    430 
    431     bool skipToEndBracket(const char* endBracket) {
    432         size_t len = strlen(endBracket);
    433         while (strncmp(fChar, endBracket, len)) {
    434             if (fChar >= fEnd) {
    435                 return false;
    436             }
    437             (void) this->next();
    438         }
    439         return true;
    440     }
    441 
    442     bool skipLine() {
    443         return skipToEndBracket('\n');
    444     }
    445 
    446     void skipTo(const char* skip) {
    447        while (fChar < skip) {
    448            this->next();
    449        }
    450     }
    451 
    452     void skipToAlpha() {
    453         while (fChar < fEnd && !isalpha(fChar[0])) {
    454             fChar++;
    455         }
    456     }
    457 
    458     void skipToAlphaNum() {
    459         while (fChar < fEnd && !isalnum(fChar[0])) {
    460             fChar++;
    461         }
    462     }
    463 
    464     bool skipExact(const char* pattern) {
    465         if (!this->startsWith(pattern)) {
    466             return false;
    467         }
    468         this->skipName(pattern);
    469         return true;
    470     }
    471 
    472     // differs from skipToNonAlphaNum in that a.b isn't considered a full name,
    473     // since a.b can't be found as a named definition
    474     void skipFullName() {
    475         while (fChar < fEnd && (isalnum(fChar[0])
    476                 || '_' == fChar[0]  /* || '-' == fChar[0] */
    477                 || (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]))) {
    478             if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
    479                 fChar++;
    480             }
    481             fChar++;
    482         }
    483     }
    484 
    485     bool skipToLineStart() {
    486         if (!this->skipLine()) {
    487             return false;
    488         }
    489         if (!this->eof()) {
    490             return this->skipWhiteSpace();
    491         }
    492         return true;
    493     }
    494 
    495     void skipToNonAlphaNum() {
    496         while (fChar < fEnd && (isalnum(fChar[0])
    497                 || '_' == fChar[0] || '-' == fChar[0]
    498                 || (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1])
    499                 || ('.' == fChar[0] && fChar + 1 < fEnd && isalpha(fChar[1])))) {
    500             if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
    501                 fChar++;
    502             }
    503             fChar++;
    504         }
    505     }
    506 
    507     void skipToSpace() {
    508         while (fChar < fEnd && ' ' != fChar[0]) {
    509             fChar++;
    510         }
    511     }
    512 
    513     void skipToWhiteSpace() {
    514         while (fChar < fEnd && ' ' < fChar[0]) {
    515             fChar++;
    516         }
    517     }
    518 
    519     bool skipName(const char* word) {
    520         size_t len = strlen(word);
    521         if (len <= (size_t) (fEnd - fChar) && !strncmp(word, fChar, len)) {
    522             for (size_t i = 0; i < len; ++i) {
    523                 this->next();
    524             }
    525         }
    526         return this->eof() || ' ' >= fChar[0];
    527     }
    528 
    529     bool skipSpace() {
    530         while (' ' == this->peek()) {
    531             (void) this->next();
    532             if (fChar >= fEnd) {
    533                 return false;
    534             }
    535         }
    536         return true;
    537     }
    538 
    539     bool skipWord(const char* word) {
    540         if (!this->skipWhiteSpace()) {
    541             return false;
    542         }
    543         const char* save = fChar;
    544         if (!this->skipName(word)) {
    545             fChar = save;
    546             return false;
    547         }
    548         if (!this->skipWhiteSpace()) {
    549             return false;
    550         }
    551         return true;
    552     }
    553 
    554     bool skipWhiteSpace() {
    555         while (' ' >= this->peek()) {
    556             (void) this->next();
    557             if (fChar >= fEnd) {
    558                 return false;
    559             }
    560         }
    561         return true;
    562     }
    563 
    564     bool startsWith(const char* str) const {
    565         size_t len = strlen(str);
    566         ptrdiff_t lineLen = fEnd - fChar;
    567         return len <= (size_t) lineLen && 0 == strncmp(str, fChar, len);
    568     }
    569 
    570     // ignores minor white space differences
    571     bool startsWith(const char* str, size_t oLen) const {
    572         size_t tIndex = 0;
    573         size_t tLen = fEnd - fChar;
    574         size_t oIndex = 0;
    575         while (oIndex < oLen && tIndex < tLen) {
    576             bool tSpace = ' ' >= fChar[tIndex];
    577             bool oSpace = ' ' >= str[oIndex];
    578             if (tSpace != oSpace) {
    579                 break;
    580             }
    581             if (tSpace) {
    582                 do {
    583                     ++tIndex;
    584                 } while (tIndex < tLen && ' ' >= fChar[tIndex]);
    585                 do {
    586                     ++oIndex;
    587                 } while (oIndex < oLen && ' ' >= str[oIndex]);
    588                 continue;
    589             }
    590             if (fChar[tIndex] != str[oIndex]) {
    591                 break;
    592             }
    593             ++tIndex;
    594             ++oIndex;
    595         }
    596         return oIndex >= oLen;
    597     }
    598 
    599     const char* strnchr(char ch, const char* end) const {
    600         const char* ptr = fChar;
    601         while (ptr < end) {
    602             if (ptr[0] == ch) {
    603                 return ptr;
    604             }
    605             ++ptr;
    606         }
    607         return nullptr;
    608     }
    609 
    610     const char* strnstr(const char *match, const char* end) const {
    611         size_t matchLen = strlen(match);
    612         SkASSERT(matchLen > 0);
    613         ptrdiff_t len = end - fChar;
    614         SkASSERT(len >= 0);
    615         if ((size_t) len < matchLen ) {
    616             return nullptr;
    617         }
    618         size_t count = len - matchLen;
    619         for (size_t index = 0; index <= count; index++) {
    620             if (0 == strncmp(&fChar[index], match, matchLen)) {
    621                 return &fChar[index];
    622             }
    623         }
    624         return nullptr;
    625     }
    626 
    627     const char* trimmedBracketEnd(const char bracket) const {
    628         int max = (int) (this->lineLength());
    629         int index = 0;
    630         while (index < max && bracket != fChar[index]) {
    631             ++index;
    632         }
    633         SkASSERT(index < max);
    634         while (index > 0 && ' ' >= fChar[index - 1]) {
    635             --index;
    636         }
    637         return fChar + index;
    638     }
    639 
    640     const char* trimmedLineEnd() const {
    641         const char* result = this->lineEnd();
    642         while (result > fChar && ' ' >= result[-1]) {
    643             --result;
    644         }
    645         return result;
    646     }
    647 
    648     void trimEnd() {
    649         while (fEnd > fStart && ' ' >= fEnd[-1]) {
    650             --fEnd;
    651         }
    652     }
    653 
    654     // FIXME: nothing else in TextParser knows from C++ --
    655     // there could be a class between TextParser and ParserCommon
    656     virtual string typedefName();
    657 
    658     const char* wordEnd() const {
    659         const char* end = fChar;
    660         while (isalnum(end[0]) || '_' == end[0] || '-' == end[0]) {
    661             ++end;
    662         }
    663         return end;
    664     }
    665 
    666     string fFileName;
    667     const char* fStart;
    668     const char* fLine;
    669     const char* fChar;
    670     const char* fEnd;
    671     size_t fLineCount;
    672 };
    673 
    674 class EscapeParser : public TextParser {
    675 public:
    676     EscapeParser(const char* start, const char* end) :
    677             TextParser("", start, end, 0) {
    678         const char* reader = fStart;
    679         fStorage = new char[end - start];
    680         char* writer = fStorage;
    681         while (reader < fEnd) {
    682             char ch = *reader++;
    683             if (ch != '\\') {
    684                 *writer++ = ch;
    685             } else {
    686                 char ctrl = *reader++;
    687                 if (ctrl == 'u') {
    688                     unsigned unicode = 0;
    689                     for (int i = 0; i < 4; ++i) {
    690                         unicode <<= 4;
    691                         SkASSERT((reader[0] >= '0' && reader[0] <= '9') ||
    692                             (reader[0] >= 'A' && reader[0] <= 'F') ||
    693                             (reader[0] >= 'a' && reader[0] <= 'f'));
    694                         int nibble = *reader++ - '0';
    695                         if (nibble > 9) {
    696                             nibble = (nibble & ~('a' - 'A')) - 'A' + '9' + 1;
    697                         }
    698                         unicode |= nibble;
    699                     }
    700                     SkASSERT(unicode < 256);
    701                     *writer++ = (unsigned char) unicode;
    702                 } else {
    703                     SkASSERT(ctrl == 'n');
    704                     *writer++ = '\n';
    705                 }
    706             }
    707         }
    708         fStart = fLine = fChar = fStorage;
    709         fEnd = writer;
    710     }
    711 
    712     ~EscapeParser() override {
    713         delete fStorage;
    714     }
    715 private:
    716     char* fStorage;
    717 };
    718 
    719 class RootDefinition;
    720 
    721 class Definition : public NonAssignable {
    722 public:
    723     enum Type {
    724         kNone,
    725         kWord,
    726         kMark,
    727         kKeyWord,
    728         kBracket,
    729         kPunctuation,
    730         kFileType,
    731     };
    732 
    733     enum class TrimExtract {
    734         kNo,
    735         kYes
    736     };
    737 
    738     enum class ExampleOptions {
    739         kText,
    740         kPng,
    741         kAll
    742     };
    743 
    744     enum class MethodType {
    745         kNone,
    746         kConstructor,
    747         kDestructor,
    748         kOperator,
    749     };
    750 
    751     enum class Operator {
    752         kUnknown,
    753         kAdd,
    754         kAddTo,
    755         kArray,
    756         kCast,
    757         kCopy,
    758         kDelete,
    759         kDereference,
    760         kEqual,
    761         kMinus,
    762         kMove,
    763         kMultiply,
    764         kMultiplyBy,
    765         kNew,
    766         kNotEqual,
    767         kSubtract,
    768         kSubtractFrom,
    769     };
    770 
    771     enum class Format {
    772         kIncludeReturn,
    773         kOmitReturn,
    774     };
    775 
    776     Definition() {}
    777 
    778     Definition(const char* start, const char* end, int line, Definition* parent)
    779         : fStart(start)
    780         , fContentStart(start)
    781         , fContentEnd(end)
    782         , fParent(parent)
    783         , fLineCount(line)
    784         , fType(Type::kWord) {
    785         if (parent) {
    786             SkASSERT(parent->fFileName.length() > 0);
    787             fFileName = parent->fFileName;
    788         }
    789         this->setParentIndex();
    790     }
    791 
    792     Definition(MarkType markType, const char* start, int line, Definition* parent)
    793         : Definition(markType, start, nullptr, line, parent) {
    794     }
    795 
    796     Definition(MarkType markType, const char* start, const char* end, int line, Definition* parent)
    797         : Definition(start, end, line, parent) {
    798         fMarkType = markType;
    799         fType = Type::kMark;
    800     }
    801 
    802     Definition(Bracket bracket, const char* start, int lineCount, Definition* parent)
    803         : Definition(start, nullptr, lineCount, parent) {
    804         fBracket = bracket;
    805         fType = Type::kBracket;
    806     }
    807 
    808     Definition(KeyWord keyWord, const char* start, const char* end, int lineCount,
    809             Definition* parent)
    810         : Definition(start, end, lineCount, parent) {
    811         fKeyWord = keyWord;
    812         fType = Type::kKeyWord;
    813     }
    814 
    815     Definition(Punctuation punctuation, const char* start, int lineCount, Definition* parent)
    816         : Definition(start, nullptr, lineCount, parent) {
    817         fPunctuation = punctuation;
    818         fType = Type::kPunctuation;
    819     }
    820 
    821     virtual ~Definition() {}
    822 
    823     virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; }
    824     virtual const RootDefinition* asRoot() const { SkASSERT(0); return nullptr; }
    825     bool boilerplateIfDef(Definition* parent);
    826     bool boilerplateDef(Definition* parent);
    827 
    828     bool boilerplateEndIf() {
    829         return true;
    830     }
    831 
    832     bool checkMethod() const;
    833     bool crossCheck2(const Definition& includeToken) const;
    834     bool crossCheck(const Definition& includeToken) const;
    835     bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const;
    836 
    837     const Definition* csParent() const {
    838         Definition* test = fParent;
    839         while (test) {
    840             if (MarkType::kStruct == test->fMarkType || MarkType::kClass == test->fMarkType) {
    841                 return test;
    842             }
    843             test = test->fParent;
    844         }
    845         return nullptr;
    846     }
    847 
    848     bool exampleToScript(string* result, ExampleOptions ) const;
    849     string extractText(TrimExtract trimExtract) const;
    850     string fiddleName() const;
    851     const Definition* findClone(string match) const;
    852     string formatFunction(Format format) const;
    853     const Definition* hasChild(MarkType markType) const;
    854     bool hasMatch(const string& name) const;
    855     const Definition* hasParam(const string& ref) const;
    856     bool isClone() const { return fClone; }
    857 
    858     Definition* iRootParent() {
    859         Definition* test = fParent;
    860         while (test) {
    861             if (Type::kKeyWord == test->fType && KeyWord::kClass == test->fKeyWord) {
    862                 return test;
    863             }
    864             test = test->fParent;
    865         }
    866         return nullptr;
    867     }
    868 
    869     virtual bool isRoot() const { return false; }
    870     bool isStructOrClass() const;
    871 
    872     int length() const {
    873         return (int) (fContentEnd - fContentStart);
    874     }
    875 
    876     bool methodHasReturn(const string& name, TextParser* methodParser) const;
    877     string methodName() const;
    878     bool nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
    879                          string* paramName) const;
    880     static string NormalizedName(string name);
    881     bool paramsMatch(const string& fullRef, const string& name) const;
    882     bool parseOperator(size_t doubleColons, string& result);
    883 
    884     string printableName() const {
    885         string result(fName);
    886         std::replace(result.begin(), result.end(), '_', ' ');
    887         return result;
    888     }
    889 
    890     template <typename T> T reportError(const char* errorStr) const {
    891         TextParser tp(this);
    892         tp.reportError(errorStr);
    893         return T();
    894     }
    895 
    896     virtual RootDefinition* rootParent() { SkASSERT(0); return nullptr; }
    897     virtual const RootDefinition* rootParent() const { SkASSERT(0); return nullptr; }
    898     void setCanonicalFiddle();
    899 
    900     void setParentIndex() {
    901         fParentIndex = fParent ? (int) fParent->fTokens.size() : -1;
    902     }
    903 
    904     void setWrapper();
    905 
    906     const Definition* topicParent() const {
    907         Definition* test = fParent;
    908         while (test) {
    909             if (MarkType::kTopic == test->fMarkType) {
    910                 return test;
    911             }
    912             test = test->fParent;
    913         }
    914         return nullptr;
    915     }
    916 
    917     string fText;  // if text is constructed instead of in a file, it's put here
    918     const char* fStart = nullptr;  // .. in original text file, or the start of fText
    919     const char* fContentStart;  // start past optional markup name
    920     string fName;
    921     string fFiddle;  // if its a constructor or operator, fiddle name goes here
    922     const char* fContentEnd = nullptr;  // the end of the contained text
    923     const char* fTerminator = nullptr;  // the end of the markup, normally ##\n or \n
    924     Definition* fParent = nullptr;
    925     list<Definition> fTokens;
    926     vector<Definition*> fChildren;
    927     string fHash;  // generated by fiddle
    928     string fFileName;
    929     mutable string fWrapper; // used by Example to wrap into proper function
    930     size_t fLineCount = 0;
    931     int fParentIndex = 0;
    932     MarkType fMarkType = MarkType::kNone;
    933     KeyWord fKeyWord = KeyWord::kNone;
    934     Bracket fBracket = Bracket::kNone;
    935     Punctuation fPunctuation = Punctuation::kNone;
    936     MethodType fMethodType = MethodType::kNone;
    937     Operator fOperator = Operator::kUnknown;
    938     Type fType = Type::kNone;
    939     bool fClone = false;
    940     bool fCloned = false;
    941     bool fDeprecated = false;
    942     bool fOperatorConst = false;
    943     bool fPrivate = false;
    944     bool fShort = false;
    945     bool fToBeDeprecated = false;
    946     bool fMemberStart = false;
    947     bool fAnonymous = false;
    948     mutable bool fVisited = false;
    949 };
    950 
    951 class RootDefinition : public Definition {
    952 public:
    953     enum class AllowParens {
    954         kNo,
    955         kYes,
    956     };
    957 
    958     RootDefinition() {
    959     }
    960 
    961     RootDefinition(MarkType markType, const char* start, int line, Definition* parent)
    962             : Definition(markType, start, line, parent) {
    963     }
    964 
    965     RootDefinition(MarkType markType, const char* start, const char* end, int line,
    966             Definition* parent) : Definition(markType, start, end,  line, parent) {
    967     }
    968 
    969     ~RootDefinition() override {
    970         for (auto& iter : fBranches) {
    971             delete iter.second;
    972         }
    973     }
    974 
    975     RootDefinition* asRoot() override { return this; }
    976     const RootDefinition* asRoot() const override { return this; }
    977     void clearVisited();
    978     bool dumpUnVisited();
    979     const Definition* find(const string& ref, AllowParens ) const;
    980     bool isRoot() const override { return true; }
    981     RootDefinition* rootParent() override { return fRootParent; }
    982     const RootDefinition* rootParent() const override { return fRootParent; }
    983     void setRootParent(RootDefinition* rootParent) { fRootParent = rootParent; }
    984 
    985     unordered_map<string, RootDefinition*> fBranches;
    986     unordered_map<string, Definition> fLeaves;
    987 private:
    988     RootDefinition* fRootParent = nullptr;
    989 };
    990 
    991 struct IClassDefinition : public Definition {
    992     unordered_map<string, Definition*> fEnums;
    993     unordered_map<string, Definition*> fMembers;
    994     unordered_map<string, Definition*> fMethods;
    995     unordered_map<string, Definition*> fStructs;
    996     unordered_map<string, Definition*> fTypedefs;
    997 };
    998 
    999 struct Reference {
   1000     Reference()
   1001         : fLocation(nullptr)
   1002         , fDefinition(nullptr) {
   1003     }
   1004 
   1005     const char* fLocation;  // .. in original text file
   1006     const Definition* fDefinition;
   1007 };
   1008 
   1009 struct TypeNames {
   1010     const char* fName;
   1011     MarkType fMarkType;
   1012 };
   1013 
   1014 class ParserCommon : public TextParser {
   1015 public:
   1016 
   1017     ParserCommon() : TextParser()
   1018         , fParent(nullptr)
   1019         , fDebugOut(false)
   1020     {
   1021     }
   1022 
   1023     ~ParserCommon() override {
   1024     }
   1025 
   1026     void addDefinition(Definition* def) {
   1027         fParent->fChildren.push_back(def);
   1028         fParent = def;
   1029     }
   1030 
   1031     void indentToColumn(int column) {
   1032         SkASSERT(column >= fColumn);
   1033         if (fDebugOut) {
   1034             SkDebugf("%*s", column - fColumn, "");
   1035         }
   1036         fprintf(fOut, "%*s", column - fColumn, "");
   1037         fColumn = column;
   1038         fSpaces += column - fColumn;
   1039     }
   1040 
   1041     bool leadingPunctuation(const char* str, size_t len) const {
   1042         if (!fPendingSpace) {
   1043             return false;
   1044         }
   1045         if (len < 2) {
   1046             return false;
   1047         }
   1048         if ('.' != str[0] && ',' != str[0] && ';' != str[0] && ':' != str[0]) {
   1049             return false;
   1050         }
   1051         return ' ' >= str[1];
   1052     }
   1053 
   1054     void lf(int count) {
   1055         fPendingLF = SkTMax(fPendingLF, count);
   1056         this->nl();
   1057     }
   1058 
   1059     void lfAlways(int count) {
   1060         this->lf(count);
   1061         this->writePending();
   1062     }
   1063 
   1064     void lfcr() {
   1065         this->lf(1);
   1066     }
   1067 
   1068     void nl() {
   1069         fLinefeeds = 0;
   1070         fSpaces = 0;
   1071         fColumn = 0;
   1072         fPendingSpace = 0;
   1073     }
   1074 
   1075     bool parseFile(const char* file, const char* suffix);
   1076     bool parseStatus(const char* file, const char* suffix, StatusFilter filter);
   1077     virtual bool parseFromFile(const char* path) = 0;
   1078     bool parseSetup(const char* path);
   1079 
   1080     void popObject() {
   1081         fParent->fContentEnd = fParent->fTerminator = fChar;
   1082         fParent = fParent->fParent;
   1083     }
   1084 
   1085     const char* ReadToBuffer(string filename, int* size);
   1086 
   1087     virtual void reset() = 0;
   1088 
   1089     void resetCommon() {
   1090         fLine = fChar = fStart;
   1091         fLineCount = 0;
   1092         fParent = nullptr;
   1093         fIndent = 0;
   1094         fOut = nullptr;
   1095         fMaxLF = 2;
   1096         fPendingLF = 0;
   1097         fPendingSpace = 0;
   1098         fOutdentNext = false;
   1099         nl();
   1100    }
   1101 
   1102     void setAsParent(Definition* definition) {
   1103         if (fParent) {
   1104             fParent->fChildren.push_back(definition);
   1105             definition->fParent = fParent;
   1106         }
   1107         fParent = definition;
   1108     }
   1109 
   1110     void singleLF() {
   1111         fMaxLF = 1;
   1112     }
   1113 
   1114     void writeBlock(int size, const char* data) {
   1115         SkAssertResult(writeBlockTrim(size, data));
   1116     }
   1117 
   1118     void writeBlockIndent(int size, const char* data);
   1119     bool writeBlockTrim(int size, const char* data);
   1120 
   1121     void writeCommentHeader() {
   1122         this->lf(2);
   1123         this->writeString("/**");
   1124         this->writeSpace();
   1125     }
   1126 
   1127     void writeCommentTrailer() {
   1128         this->writeString("*/");
   1129         this->lfcr();
   1130     }
   1131 
   1132     void writePending();
   1133 
   1134     // write a pending space, so that two consecutive calls
   1135     // don't double write, and trailing spaces on lines aren't written
   1136     void writeSpace(int count = 1) {
   1137         SkASSERT(!fPendingLF);
   1138         SkASSERT(!fLinefeeds);
   1139         SkASSERT(fColumn > 0);
   1140         SkASSERT(!fSpaces);
   1141         fPendingSpace = count;
   1142     }
   1143 
   1144     void writeString(const char* str);
   1145 
   1146     void writeString(const string& str) {
   1147         this->writeString(str.c_str());
   1148     }
   1149 
   1150     bool writtenFileDiffers(string filename, string readname);
   1151 
   1152     unordered_map<string, sk_sp<SkData>> fRawData;
   1153     unordered_map<string, vector<char>> fLFOnly;
   1154     Definition* fParent;
   1155     FILE* fOut;
   1156     int fLinefeeds;    // number of linefeeds last written, zeroed on non-space
   1157     int fMaxLF;        // number of linefeeds allowed
   1158     int fPendingLF;    // number of linefeeds to write (can be suppressed)
   1159     int fSpaces;       // number of spaces (indent) last written, zeroed on non-space
   1160     int fColumn;       // current column; number of chars past last linefeed
   1161     int fIndent;       // desired indention
   1162     int fPendingSpace; // one or two spaces should preceed the next string or block
   1163     char fLastChar;    // last written
   1164     bool fDebugOut;    // set true to write to std out
   1165     bool fOutdentNext; // set at end of embedded struct to prevent premature outdent
   1166 private:
   1167     typedef TextParser INHERITED;
   1168 };
   1169 
   1170 struct JsonStatus {
   1171     const Json::Value& fObject;
   1172     Json::Value::iterator fIter;
   1173     string fName;
   1174 };
   1175 
   1176 class StatusIter : public ParserCommon {
   1177 public:
   1178     StatusIter(const char* statusFile, const char* suffix, StatusFilter);
   1179     ~StatusIter() override {}
   1180     string baseDir();
   1181     bool empty() { return fStack.empty(); }
   1182     bool next(string* file);
   1183 protected:
   1184     bool parseFromFile(const char* path) override;
   1185     void reset() override;
   1186 private:
   1187     vector<JsonStatus> fStack;
   1188     Json::Value fRoot;
   1189     const char* fSuffix;
   1190     StatusFilter fFilter;
   1191 };
   1192 
   1193 class BmhParser : public ParserCommon {
   1194 public:
   1195     enum class MarkLookup {
   1196         kRequire,
   1197         kAllowUnknown,
   1198     };
   1199 
   1200     enum class Resolvable {
   1201         kNo,      // neither resolved nor output
   1202         kYes,     // resolved, output
   1203         kOut,     // not resolved, but output
   1204         kLiteral, // output untouched (FIXME: is this really different from kOut?)
   1205 		kClone,   // resolved, output, with references to clones as well
   1206     };
   1207 
   1208     enum class Exemplary {
   1209         kNo,
   1210         kYes,
   1211         kOptional,
   1212     };
   1213 
   1214     enum class TableState {
   1215         kNone,
   1216         kColumnStart,
   1217         kColumnEnd,
   1218     };
   1219 
   1220     enum class HasTag {
   1221         kNo,
   1222         kYes,
   1223     };
   1224 
   1225 #define M(mt) (1LL << (int) MarkType::k##mt)
   1226 #define M_D M(Description)
   1227 #define M_CS M(Class) | M(Struct)
   1228 #define M_ST M(Subtopic) | M(Topic)
   1229 #define M_CSST M_CS | M_ST
   1230 #ifdef M_E
   1231 #undef M_E
   1232 #endif
   1233 #define M_E M(Enum) | M(EnumClass)
   1234 
   1235 #define R_Y Resolvable::kYes
   1236 #define R_N Resolvable::kNo
   1237 #define R_O Resolvable::kOut
   1238 #define R_C Resolvable::kClone
   1239 
   1240 #define E_Y Exemplary::kYes
   1241 #define E_N Exemplary::kNo
   1242 #define E_O Exemplary::kOptional
   1243 
   1244     BmhParser(bool skip) : ParserCommon()
   1245         , fMaps {
   1246 // names without formal definitions (e.g. Column) aren't included
   1247 // fill in other names once they're actually used
   1248   { "",            nullptr,      MarkType::kNone,         R_Y, E_N, 0 }
   1249 , { "A",           nullptr,      MarkType::kAnchor,       R_N, E_N, 0 }
   1250 , { "Alias",       nullptr,      MarkType::kAlias,        R_N, E_N, 0 }
   1251 , { "Bug",         nullptr,      MarkType::kBug,          R_N, E_N, 0 }
   1252 , { "Class",       &fClassMap,   MarkType::kClass,        R_Y, E_O, M_CSST | M(Root) }
   1253 , { "Code",        nullptr,      MarkType::kCode,         R_O, E_N, M_CSST | M_E | M(Method) }
   1254 , { "",            nullptr,      MarkType::kColumn,       R_Y, E_N, M(Row) }
   1255 , { "",            nullptr,      MarkType::kComment,      R_N, E_N, 0 }
   1256 , { "Const",       &fConstMap,   MarkType::kConst,        R_Y, E_O, M_E | M_ST  }
   1257 , { "Define",      nullptr,      MarkType::kDefine,       R_O, E_N, M_ST }
   1258 , { "DefinedBy",   nullptr,      MarkType::kDefinedBy,    R_N, E_N, M(Method) }
   1259 , { "Deprecated",  nullptr,      MarkType::kDeprecated,   R_Y, E_N, 0 }
   1260 , { "Description", nullptr,      MarkType::kDescription,  R_Y, E_N, M(Example) | M(NoExample) }
   1261 , { "Doxygen",     nullptr,      MarkType::kDoxygen,      R_Y, E_N, 0 }
   1262 , { "Duration",    nullptr,      MarkType::kDuration,     R_N, E_N, M(Example) | M(NoExample) }
   1263 , { "Enum",        &fEnumMap,    MarkType::kEnum,         R_Y, E_O, M_CSST | M(Root) }
   1264 , { "EnumClass",   &fClassMap,   MarkType::kEnumClass,    R_Y, E_O, M_CSST | M(Root) }
   1265 , { "Example",     nullptr,      MarkType::kExample,
   1266                                                      R_O, E_N, M_CSST | M_E | M(Method) | M(Const) }
   1267 , { "Experimental", nullptr,     MarkType::kExperimental, R_Y, E_N, 0 }
   1268 , { "External",    nullptr,      MarkType::kExternal,     R_Y, E_N, M(Root) }
   1269 , { "File",        nullptr,      MarkType::kFile,         R_N, E_N, M(Track) }
   1270 , { "Formula",     nullptr,      MarkType::kFormula,      R_O, E_N,
   1271                                               M(Column) | M_E | M_ST | M(Member) | M(Method) | M_D }
   1272 , { "Function",    nullptr,      MarkType::kFunction,     R_O, E_N, M(Example) | M(NoExample) }
   1273 , { "Height",      nullptr,      MarkType::kHeight,       R_N, E_N, M(Example) | M(NoExample) }
   1274 , { "Illustration", nullptr,     MarkType::kIllustration, R_N, E_N, M(Subtopic) }
   1275 , { "Image",       nullptr,      MarkType::kImage,        R_N, E_N, M(Example) | M(NoExample) }
   1276 , { "In",          nullptr,      MarkType::kIn,           R_N, E_N,
   1277                                                              M_CSST | M_E | M(Method) | M(Typedef) }
   1278 , { "Legend",      nullptr,      MarkType::kLegend,       R_Y, E_N, M(Table) }
   1279 , { "Line",        nullptr,      MarkType::kLine,         R_N, E_N,
   1280                                                              M_CSST | M_E | M(Method) | M(Typedef) }
   1281 , { "",            nullptr,      MarkType::kLink,         R_N, E_N, M(Anchor) }
   1282 , { "List",        nullptr,      MarkType::kList,         R_Y, E_N, M(Method) | M_CSST | M_E | M_D }
   1283 , { "Literal",     nullptr,      MarkType::kLiteral,      R_N, E_N, M(Code) }
   1284 , { "",            nullptr,      MarkType::kMarkChar,     R_N, E_N, 0 }
   1285 , { "Member",      nullptr,      MarkType::kMember,       R_Y, E_N, M_CSST }
   1286 , { "Method",      &fMethodMap,  MarkType::kMethod,       R_Y, E_Y, M_CSST }
   1287 , { "NoExample",   nullptr,      MarkType::kNoExample,    R_O, E_N, M_CSST | M_E | M(Method) }
   1288 , { "Outdent",     nullptr,      MarkType::kOutdent,      R_N, E_N, M(Code) }
   1289 , { "Param",       nullptr,      MarkType::kParam,        R_Y, E_N, M(Method) }
   1290 , { "Platform",    nullptr,      MarkType::kPlatform,     R_N, E_N, M(Example) | M(NoExample) }
   1291 , { "Populate",    nullptr,      MarkType::kPopulate,     R_N, E_N, M(Subtopic) }
   1292 , { "Private",     nullptr,      MarkType::kPrivate,      R_N, E_N, 0 }
   1293 , { "Return",      nullptr,      MarkType::kReturn,       R_Y, E_N, M(Method) }
   1294 , { "",            nullptr,      MarkType::kRoot,         R_Y, E_N, 0 }
   1295 , { "",            nullptr,      MarkType::kRow,          R_Y, E_N, M(Table) | M(List) }
   1296 , { "SeeAlso",     nullptr,      MarkType::kSeeAlso,      R_C, E_N,
   1297                                                              M_CSST | M_E | M(Method) | M(Typedef) }
   1298 , { "Set",         nullptr,      MarkType::kSet,          R_N, E_N, M(Example) | M(NoExample) }
   1299 , { "StdOut",      nullptr,      MarkType::kStdOut,       R_N, E_N, M(Example) | M(NoExample) }
   1300 , { "Struct",      &fClassMap,   MarkType::kStruct,       R_Y, E_O, M(Class) | M(Root) | M_ST }
   1301 , { "Substitute",  nullptr,      MarkType::kSubstitute,   R_N, E_N, M_ST }
   1302 , { "Subtopic",    nullptr,      MarkType::kSubtopic,     R_Y, E_Y, M_CSST }
   1303 , { "Table",       nullptr,      MarkType::kTable,        R_Y, E_N, M(Method) | M_CSST | M_E }
   1304 , { "Template",    nullptr,      MarkType::kTemplate,     R_Y, E_N, 0 }
   1305 , { "",            nullptr,      MarkType::kText,         R_N, E_N, 0 }
   1306 , { "Time",        nullptr,      MarkType::kTime,         R_Y, E_N, M(Track) }
   1307 , { "ToDo",        nullptr,      MarkType::kToDo,         R_N, E_N, 0 }
   1308 , { "Topic",       nullptr,      MarkType::kTopic,        R_Y, E_Y, M_CS | M(Root) | M(Topic) }
   1309 , { "Track",       nullptr,      MarkType::kTrack,        R_Y, E_N, M_E | M_ST }
   1310 , { "Typedef",     &fTypedefMap, MarkType::kTypedef,      R_Y, E_N, M(Class) | M_ST }
   1311 , { "",            nullptr,      MarkType::kUnion,        R_Y, E_N, 0 }
   1312 , { "Volatile",    nullptr,      MarkType::kVolatile,     R_N, E_N, M(StdOut) }
   1313 , { "Width",       nullptr,      MarkType::kWidth,        R_N, E_N, M(Example) | M(NoExample) } }
   1314 , fSkip(skip)
   1315         {
   1316             this->reset();
   1317         }
   1318 
   1319 #undef R_O
   1320 #undef R_N
   1321 #undef R_Y
   1322 
   1323 #undef M_E
   1324 #undef M_CSST
   1325 #undef M_ST
   1326 #undef M_CS
   1327 #undef M_D
   1328 #undef M
   1329 
   1330     ~BmhParser() override {}
   1331 
   1332     bool addDefinition(const char* defStart, bool hasEnd, MarkType markType,
   1333             const vector<string>& typeNameBuilder, HasTag hasTag);
   1334     bool checkEndMarker(MarkType markType, string name) const;
   1335     bool checkExamples() const;
   1336     bool checkParamReturn(const Definition* definition) const;
   1337     bool dumpExamples(const char* fiddleJsonFileName) const;
   1338     bool childOf(MarkType markType) const;
   1339     string className(MarkType markType);
   1340     bool collectExternals();
   1341     int endHashCount() const;
   1342     bool endTableColumn(const char* end, const char* terminator);
   1343 
   1344     RootDefinition* findBmhObject(MarkType markType, const string& typeName) const {
   1345         auto map = fMaps[(int) markType].fBmh;
   1346         if (!map) {
   1347             return nullptr;
   1348         }
   1349         return &(*map)[typeName];
   1350     }
   1351 
   1352     bool findDefinitions();
   1353     Definition* findExample(string name) const;
   1354     MarkType getMarkType(MarkLookup lookup) const;
   1355     bool hasEndToken() const;
   1356     string memberName();
   1357     string methodName();
   1358     const Definition* parentSpace() const;
   1359 
   1360     bool parseFromFile(const char* path) override {
   1361         if (!INHERITED::parseSetup(path)) {
   1362             return false;
   1363         }
   1364         fCheckMethods = !strstr(path, "undocumented.bmh");
   1365         return findDefinitions();
   1366     }
   1367 
   1368     bool popParentStack(Definition* definition);
   1369     void reportDuplicates(const Definition& def, const string& dup) const;
   1370 
   1371     void reset() override {
   1372         INHERITED::resetCommon();
   1373         fRoot = nullptr;
   1374         fWorkingColumn = nullptr;
   1375         fRow = nullptr;
   1376         fTableState = TableState::kNone;
   1377         fMC = '#';
   1378         fInChar = false;
   1379         fInCharCommentString = false;
   1380         fInComment = false;
   1381         fInEnum = false;
   1382         fInString = false;
   1383         fCheckMethods = false;
   1384     }
   1385 
   1386     bool skipNoName();
   1387     bool skipToDefinitionEnd(MarkType markType);
   1388 	bool skipToString();
   1389     void spellCheck(const char* match, SkCommandLineFlags::StringArray report) const;
   1390     void spellStatus(const char* match, SkCommandLineFlags::StringArray report) const;
   1391     vector<string> topicName();
   1392     vector<string> typeName(MarkType markType, bool* expectEnd);
   1393     string typedefName() override;
   1394     string uniqueName(const string& base, MarkType markType);
   1395     string uniqueRootName(const string& base, MarkType markType);
   1396     void validate() const;
   1397     string word(const string& prefix, const string& delimiter);
   1398 
   1399 public:
   1400     struct DefinitionMap {
   1401         const char* fName;
   1402         unordered_map<string, RootDefinition>* fBmh;
   1403         MarkType fMarkType;
   1404         Resolvable fResolve;
   1405         Exemplary fExemplary;  // worthy of an example
   1406         uint64_t fParentMask;
   1407     };
   1408 
   1409     DefinitionMap fMaps[Last_MarkType + 1];
   1410     forward_list<RootDefinition> fTopics;
   1411     forward_list<Definition> fMarkup;
   1412     forward_list<RootDefinition> fExternals;
   1413     vector<string> fInputFiles;
   1414     unordered_map<string, RootDefinition> fClassMap;
   1415     unordered_map<string, RootDefinition> fConstMap;
   1416     unordered_map<string, RootDefinition> fEnumMap;
   1417     unordered_map<string, RootDefinition> fMethodMap;
   1418     unordered_map<string, RootDefinition> fTypedefMap;
   1419     unordered_map<string, Definition*> fTopicMap;
   1420     unordered_map<string, Definition*> fAliasMap;
   1421     RootDefinition* fRoot;
   1422     Definition* fWorkingColumn;
   1423     Definition* fRow;
   1424     const char* fColStart;
   1425     TableState fTableState;
   1426     mutable char fMC;  // markup character
   1427     bool fAnonymous;
   1428     bool fCloned;
   1429     bool fInChar;
   1430     bool fInCharCommentString;
   1431     bool fInEnum;
   1432     bool fInComment;
   1433     bool fInString;
   1434     bool fCheckMethods;
   1435     bool fSkip = false;
   1436     bool fWroteOut = false;
   1437 private:
   1438     typedef ParserCommon INHERITED;
   1439 };
   1440 
   1441 class IncludeParser : public ParserCommon {
   1442 public:
   1443     enum class IsStruct {
   1444         kNo,
   1445         kYes,
   1446     };
   1447 
   1448     IncludeParser() : ParserCommon()
   1449         , fMaps {
   1450           { nullptr,        MarkType::kNone }
   1451         , { nullptr,        MarkType::kAnchor }
   1452         , { nullptr,        MarkType::kAlias }
   1453         , { nullptr,        MarkType::kBug }
   1454         , { nullptr,        MarkType::kClass }
   1455         , { nullptr,        MarkType::kCode }
   1456         , { nullptr,        MarkType::kColumn }
   1457         , { nullptr,        MarkType::kComment }
   1458         , { nullptr,        MarkType::kConst }
   1459         , { &fIDefineMap,   MarkType::kDefine }
   1460         , { nullptr,        MarkType::kDefinedBy }
   1461         , { nullptr,        MarkType::kDeprecated }
   1462         , { nullptr,        MarkType::kDescription }
   1463         , { nullptr,        MarkType::kDoxygen }
   1464         , { nullptr,        MarkType::kDuration }
   1465         , { &fIEnumMap,     MarkType::kEnum }
   1466         , { &fIEnumMap,     MarkType::kEnumClass }
   1467         , { nullptr,        MarkType::kExample }
   1468         , { nullptr,        MarkType::kExperimental }
   1469         , { nullptr,        MarkType::kExternal }
   1470         , { nullptr,        MarkType::kFile }
   1471         , { nullptr,        MarkType::kFormula }
   1472         , { nullptr,        MarkType::kFunction }
   1473         , { nullptr,        MarkType::kHeight }
   1474         , { nullptr,        MarkType::kIllustration }
   1475 		, { nullptr,        MarkType::kImage }
   1476 		, { nullptr,        MarkType::kIn }
   1477 		, { nullptr,        MarkType::kLegend }
   1478 		, { nullptr,        MarkType::kLine }
   1479 		, { nullptr,        MarkType::kLink }
   1480         , { nullptr,        MarkType::kList }
   1481         , { nullptr,        MarkType::kLiteral }
   1482         , { nullptr,        MarkType::kMarkChar }
   1483         , { nullptr,        MarkType::kMember }
   1484         , { nullptr,        MarkType::kMethod }
   1485         , { nullptr,        MarkType::kNoExample }
   1486         , { nullptr,        MarkType::kOutdent }
   1487         , { nullptr,        MarkType::kParam }
   1488         , { nullptr,        MarkType::kPlatform }
   1489         , { nullptr,        MarkType::kPopulate }
   1490         , { nullptr,        MarkType::kPrivate }
   1491         , { nullptr,        MarkType::kReturn }
   1492         , { nullptr,        MarkType::kRoot }
   1493         , { nullptr,        MarkType::kRow }
   1494         , { nullptr,        MarkType::kSeeAlso }
   1495         , { nullptr,        MarkType::kSet }
   1496         , { nullptr,        MarkType::kStdOut }
   1497         , { &fIStructMap,   MarkType::kStruct }
   1498         , { nullptr,        MarkType::kSubstitute }
   1499         , { nullptr,        MarkType::kSubtopic }
   1500         , { nullptr,        MarkType::kTable }
   1501         , { &fITemplateMap, MarkType::kTemplate }
   1502         , { nullptr,        MarkType::kText }
   1503         , { nullptr,        MarkType::kTime }
   1504         , { nullptr,        MarkType::kToDo }
   1505         , { nullptr,        MarkType::kTopic }
   1506         , { nullptr,        MarkType::kTrack }
   1507         , { &fITypedefMap,  MarkType::kTypedef }
   1508         , { &fIUnionMap,    MarkType::kUnion }
   1509         , { nullptr,        MarkType::kVolatile }
   1510         , { nullptr,        MarkType::kWidth } }
   1511     {
   1512         this->reset();
   1513     }
   1514 
   1515     ~IncludeParser() override {}
   1516 
   1517     void addKeyword(KeyWord keyWord);
   1518 
   1519     void addPunctuation(Punctuation punctuation) {
   1520         fParent->fTokens.emplace_back(punctuation, fChar, fLineCount, fParent);
   1521     }
   1522 
   1523     void addWord() {
   1524         fParent->fTokens.emplace_back(fIncludeWord, fChar, fLineCount, fParent);
   1525         fIncludeWord = nullptr;
   1526     }
   1527 
   1528     void checkForMissingParams(const vector<string>& methodParams,
   1529                                const vector<string>& foundParams);
   1530     bool checkForWord();
   1531     string className() const;
   1532     bool crossCheck(BmhParser& );
   1533     IClassDefinition* defineClass(const Definition& includeDef, const string& className);
   1534     void dumpClassTokens(IClassDefinition& classDef);
   1535     void dumpComment(const Definition& );
   1536     void dumpEnum(const Definition& , const string& name);
   1537     void dumpMethod(const Definition& );
   1538     void dumpMember(const Definition& );
   1539     bool dumpTokens(const string& directory);
   1540     bool dumpTokens(const string& directory, const string& skClassName);
   1541     bool findComments(const Definition& includeDef, Definition* markupDef);
   1542 
   1543     Definition* findIncludeObject(const Definition& includeDef, MarkType markType,
   1544             const string& typeName) {
   1545         typedef Definition* DefinitionPtr;
   1546         unordered_map<string, Definition>* map = fMaps[(int) markType].fInclude;
   1547         if (!map) {
   1548             return reportError<DefinitionPtr>("invalid mark type");
   1549         }
   1550         string name = this->uniqueName(*map, typeName);
   1551         Definition& markupDef = (*map)[name];
   1552         if (markupDef.fStart) {
   1553             return reportError<DefinitionPtr>("definition already defined");
   1554         }
   1555         markupDef.fFileName = fFileName;
   1556         markupDef.fStart = includeDef.fStart;
   1557         markupDef.fContentStart = includeDef.fStart;
   1558         markupDef.fName = name;
   1559         markupDef.fContentEnd = includeDef.fContentEnd;
   1560         markupDef.fTerminator = includeDef.fTerminator;
   1561         markupDef.fParent = fParent;
   1562         markupDef.fLineCount = includeDef.fLineCount;
   1563         markupDef.fMarkType = markType;
   1564         markupDef.fKeyWord = includeDef.fKeyWord;
   1565         markupDef.fType = Definition::Type::kMark;
   1566         return &markupDef;
   1567     }
   1568 
   1569     static KeyWord FindKey(const char* start, const char* end);
   1570     bool internalName(const Definition& ) const;
   1571     bool parseChar();
   1572     bool parseComment(const string& filename, const char* start, const char* end, int lineCount,
   1573             Definition* markupDef);
   1574     bool parseClass(Definition* def, IsStruct);
   1575     bool parseDefine();
   1576     bool parseEnum(Definition* child, Definition* markupDef);
   1577 
   1578     bool parseFromFile(const char* path) override {
   1579         this->reset();
   1580         if (!INHERITED::parseSetup(path)) {
   1581             return false;
   1582         }
   1583         string name(path);
   1584         return this->parseInclude(name);
   1585     }
   1586 
   1587     bool parseInclude(const string& name);
   1588     bool parseMember(Definition* child, Definition* markupDef);
   1589     bool parseMethod(Definition* child, Definition* markupDef);
   1590     bool parseObject(Definition* child, Definition* markupDef);
   1591     bool parseObjects(Definition* parent, Definition* markupDef);
   1592     bool parseTemplate();
   1593     bool parseTypedef(Definition* child, Definition* markupDef);
   1594     bool parseUnion();
   1595 
   1596     void popBracket() {
   1597         SkASSERT(Definition::Type::kBracket == fParent->fType);
   1598         this->popObject();
   1599         Bracket bracket = this->topBracket();
   1600         this->setBracketShortCuts(bracket);
   1601     }
   1602 
   1603     void pushBracket(Bracket bracket) {
   1604         this->setBracketShortCuts(bracket);
   1605         fParent->fTokens.emplace_back(bracket, fChar, fLineCount, fParent);
   1606         Definition* container = &fParent->fTokens.back();
   1607         this->addDefinition(container);
   1608     }
   1609 
   1610     static void RemoveFile(const char* docs, const char* includes);
   1611     static void RemoveOneFile(const char* docs, const char* includesFileOrPath);
   1612 
   1613     void reset() override {
   1614         INHERITED::resetCommon();
   1615         fRootTopic = nullptr;
   1616         fInBrace = nullptr;
   1617         fIncludeWord = nullptr;
   1618         fLastObject = nullptr;
   1619         fPrev = '\0';
   1620         fInChar = false;
   1621         fInCharCommentString = false;
   1622         fInComment = false;
   1623         fInEnum = false;
   1624         fInFunction = false;
   1625         fInString = false;
   1626         fFailed = false;
   1627         fPriorEnum = nullptr;
   1628     }
   1629 
   1630     void setBracketShortCuts(Bracket bracket) {
   1631         fInComment = Bracket::kSlashSlash == bracket || Bracket::kSlashStar == bracket;
   1632         fInString = Bracket::kString == bracket;
   1633         fInChar = Bracket::kChar == bracket;
   1634         fInCharCommentString = fInChar || fInComment || fInString;
   1635     }
   1636 
   1637     Bracket topBracket() const {
   1638         Definition* parent = fParent;
   1639         while (parent && Definition::Type::kBracket != parent->fType) {
   1640             parent = parent->fParent;
   1641         }
   1642         return parent ? parent->fBracket : Bracket::kNone;
   1643     }
   1644 
   1645     template <typename T>
   1646     string uniqueName(const unordered_map<string, T>& m, const string& typeName) {
   1647         string base(typeName.size() > 0 ? typeName : "_anonymous");
   1648         string name(base);
   1649         int anonCount = 1;
   1650         do {
   1651             auto iter = m.find(name);
   1652             if (iter == m.end()) {
   1653                 return name;
   1654             }
   1655             name = base + '_';
   1656             name += to_string(++anonCount);
   1657         } while (true);
   1658         // should never get here
   1659         return string();
   1660     }
   1661 
   1662     void validate() const;
   1663 
   1664     void writeDefinition(const Definition& def) {
   1665         if (def.length() > 1) {
   1666             this->writeBlock((int) (def.fContentEnd - def.fContentStart), def.fContentStart);
   1667             this->lf(1);
   1668         }
   1669     }
   1670 
   1671     void writeDefinition(const Definition& def, const string& name, int spaces) {
   1672         this->writeBlock((int) (def.fContentEnd - def.fContentStart), def.fContentStart);
   1673         this->writeSpace(spaces);
   1674         this->writeString(name);
   1675         this->lf(1);
   1676     }
   1677 
   1678     void writeEndTag() {
   1679         this->lf(1);
   1680         this->writeString("##");
   1681         this->lf(1);
   1682     }
   1683 
   1684     void writeEndTag(const char* tagType) {
   1685         this->lf(1);
   1686         this->writeString(string("#") + tagType + " ##");
   1687         this->lf(1);
   1688     }
   1689 
   1690     void writeEndTag(const char* tagType, const char* tagID, int spaces = 1) {
   1691         this->lf(1);
   1692         this->writeString(string("#") + tagType + " " + tagID);
   1693         this->writeSpace(spaces);
   1694         this->writeString("##");
   1695         this->lf(1);
   1696     }
   1697 
   1698     void writeEndTag(const char* tagType, const string& tagID, int spaces = 1) {
   1699         this->writeEndTag(tagType, tagID.c_str(), spaces);
   1700     }
   1701 
   1702     void writeIncompleteTag(const char* tagType, const string& tagID, int spaces = 1) {
   1703         this->writeString(string("#") + tagType + " " + tagID);
   1704         this->writeSpace(spaces);
   1705         this->writeString("incomplete");
   1706         this->writeSpace();
   1707         this->writeString("##");
   1708         this->lf(1);
   1709     }
   1710 
   1711     void writeIncompleteTag(const char* tagType) {
   1712         this->writeString(string("#") + tagType + " incomplete ##");
   1713         this->lf(1);
   1714     }
   1715 
   1716     void writeTableHeader(const char* col1, size_t pad, const char* col2) {
   1717         this->lf(1);
   1718         this->writeString("#Table");
   1719         this->lf(1);
   1720         this->writeString("#Legend");
   1721         this->lf(1);
   1722         string legend = "# ";
   1723         legend += col1;
   1724         if (pad > strlen(col1)) {
   1725             legend += string(pad - strlen(col1), ' ');
   1726         }
   1727         legend += " # ";
   1728         legend += col2;
   1729         legend += " ##";
   1730         this->writeString(legend);
   1731         this->lf(1);
   1732         this->writeString("#Legend ##");
   1733         this->lf(1);
   1734     }
   1735 
   1736     void writeTableRow(size_t pad, const string& col1) {
   1737         this->lf(1);
   1738         string row = "# " + col1 + string(pad - col1.length(), ' ') + " # ##";
   1739         this->writeString(row);
   1740         this->lf(1);
   1741     }
   1742 
   1743     void writeTableRow(size_t pad1, const string& col1, size_t pad2, const string& col2) {
   1744         this->lf(1);
   1745         string row = "# " + col1 + string(pad1 - col1.length(), ' ') + " # " +
   1746                 col2 + string(pad2 - col2.length(), ' ') + " ##";
   1747         this->writeString(row);
   1748         this->lf(1);
   1749     }
   1750 
   1751     void writeTableTrailer() {
   1752         this->lf(1);
   1753         this->writeString("#Table ##");
   1754         this->lf(1);
   1755     }
   1756 
   1757     void writeTag(const char* tagType) {
   1758         this->lf(1);
   1759         this->writeString("#");
   1760         this->writeString(tagType);
   1761     }
   1762 
   1763     void writeTagNoLF(const char* tagType, const char* tagID) {
   1764         this->writeString("#");
   1765         this->writeString(tagType);
   1766         this->writeSpace();
   1767         this->writeString(tagID);
   1768     }
   1769 
   1770     void writeTagNoLF(const char* tagType, const string& tagID) {
   1771         this->writeTagNoLF(tagType, tagID.c_str());
   1772     }
   1773 
   1774     void writeTag(const char* tagType, const char* tagID) {
   1775         this->lf(1);
   1776         this->writeTagNoLF(tagType, tagID);
   1777     }
   1778 
   1779     void writeTag(const char* tagType, const string& tagID) {
   1780         this->writeTag(tagType, tagID.c_str());
   1781     }
   1782 
   1783 protected:
   1784     static void ValidateKeyWords();
   1785 
   1786     struct DefinitionMap {
   1787         unordered_map<string, Definition>* fInclude;
   1788         MarkType fMarkType;
   1789     };
   1790 
   1791     DefinitionMap fMaps[Last_MarkType + 1];
   1792     unordered_map<string, Definition> fIncludeMap;
   1793     unordered_map<string, IClassDefinition> fIClassMap;
   1794     unordered_map<string, Definition> fIDefineMap;
   1795     unordered_map<string, Definition> fIEnumMap;
   1796     unordered_map<string, Definition> fIFunctionMap;
   1797     unordered_map<string, Definition> fIStructMap;
   1798     unordered_map<string, Definition> fITemplateMap;
   1799     unordered_map<string, Definition> fITypedefMap;
   1800     unordered_map<string, Definition> fIUnionMap;
   1801     Definition* fRootTopic;
   1802     Definition* fInBrace;
   1803     Definition* fLastObject;
   1804     Definition* fPriorEnum;
   1805     int fPriorIndex;
   1806     const char* fIncludeWord;
   1807     char fPrev;
   1808     bool fInChar;
   1809     bool fInCharCommentString;
   1810     bool fInComment;
   1811     bool fInEnum;
   1812     bool fInFunction;
   1813     bool fInString;
   1814     bool fFailed;
   1815 
   1816     typedef ParserCommon INHERITED;
   1817 };
   1818 
   1819 class IncludeWriter : public IncludeParser {
   1820 public:
   1821     enum class Word {
   1822         kStart,
   1823         kCap,
   1824         kFirst,
   1825         kUnderline,
   1826         kMixed,
   1827     };
   1828 
   1829     enum class Phrase {
   1830         kNo,
   1831         kYes,
   1832     };
   1833 
   1834     enum class PunctuationState {
   1835         kStart,
   1836         kDelimiter,
   1837         kParen,     // treated as a delimiter unless following a space, and followed by word
   1838         kPeriod,
   1839         kSpace,
   1840     };
   1841 
   1842     enum class RefType {
   1843         kUndefined,
   1844         kNormal,
   1845         kExternal,
   1846     };
   1847 
   1848 	enum class SkipFirstLine {
   1849 		kNo,
   1850 		kYes,
   1851 	};
   1852 
   1853     enum class Wrote {
   1854         kNone,
   1855         kLF,
   1856         kChars,
   1857     };
   1858 
   1859     struct IterState {
   1860         IterState (list<Definition>::iterator tIter, list<Definition>::iterator tIterEnd)
   1861             : fDefIter(tIter)
   1862             , fDefEnd(tIterEnd) {
   1863         }
   1864         list<Definition>::iterator fDefIter;
   1865         list<Definition>::iterator fDefEnd;
   1866     };
   1867 
   1868     struct ParentPair {
   1869         const Definition* fParent;
   1870         const ParentPair* fPrev;
   1871     };
   1872 
   1873     IncludeWriter() : IncludeParser() {
   1874         this->reset();
   1875     }
   1876 
   1877     ~IncludeWriter() override {}
   1878 
   1879     bool contentFree(int size, const char* data) const {
   1880         while (size > 0 && data[0] <= ' ') {
   1881             --size;
   1882             ++data;
   1883         }
   1884         while (size > 0 && data[size - 1] <= ' ') {
   1885             --size;
   1886         }
   1887         return 0 == size;
   1888     }
   1889 
   1890 	void constOut(const Definition* memberStart, const Definition& child,
   1891 		const Definition* bmhConst);
   1892     void descriptionOut(const Definition* def, SkipFirstLine );
   1893     void enumHeaderOut(const RootDefinition* root, const Definition& child);
   1894     void enumMembersOut(const RootDefinition* root, Definition& child);
   1895     void enumSizeItems(const Definition& child);
   1896 	Definition* findMemberCommentBlock(const vector<Definition*>& bmhChildren, const string& name) const;
   1897     int lookupMethod(const PunctuationState punctuation, const Word word,
   1898             const int start, const int run, int lastWrite,
   1899             const char* data, bool hasIndirection);
   1900     int lookupReference(const PunctuationState punctuation, const Word word,
   1901             const int start, const int run, int lastWrite, const char last,
   1902             const char* data);
   1903     void methodOut(const Definition* method, const Definition& child);
   1904     bool populate(Definition* def, ParentPair* parentPair, RootDefinition* root);
   1905     bool populate(BmhParser& bmhParser);
   1906 
   1907     void reset() override {
   1908         INHERITED::resetCommon();
   1909         fBmhMethod = nullptr;
   1910         fBmhParser = nullptr;
   1911         fEnumDef = nullptr;
   1912         fMethodDef = nullptr;
   1913         fBmhStructDef = nullptr;
   1914         fAttrDeprecated = nullptr;
   1915         fInStruct = false;
   1916         fWroteMethod = false;
   1917         fIndentNext = false;
   1918         fPendingMethod = false;
   1919     }
   1920 
   1921     string resolveMethod(const char* start, const char* end, bool first);
   1922     string resolveRef(const char* start, const char* end, bool first, RefType* refType);
   1923     Wrote rewriteBlock(int size, const char* data, Phrase phrase);
   1924     Definition* structMemberOut(const Definition* memberStart, const Definition& child);
   1925     void structOut(const Definition* root, const Definition& child,
   1926             const char* commentStart, const char* commentEnd);
   1927     void structSetMembersShort(const vector<Definition*>& bmhChildren);
   1928     void structSizeMembers(const Definition& child);
   1929 private:
   1930     BmhParser* fBmhParser;
   1931     Definition* fDeferComment;
   1932     Definition* fLastComment;
   1933     const Definition* fBmhMethod;
   1934     const Definition* fEnumDef;
   1935     const Definition* fMethodDef;
   1936     const Definition* fBmhStructDef;
   1937     const Definition* fAttrDeprecated;
   1938     const char* fContinuation;  // used to construct paren-qualified method name
   1939     int fAnonymousEnumCount;
   1940     int fEnumItemValueTab;
   1941     int fEnumItemCommentTab;
   1942     int fStructMemberTab;
   1943     int fStructValueTab;
   1944     int fStructCommentTab;
   1945     bool fInStruct;
   1946     bool fWroteMethod;
   1947     bool fIndentNext;
   1948     bool fPendingMethod;
   1949 
   1950     typedef IncludeParser INHERITED;
   1951 };
   1952 
   1953 class FiddleBase : public ParserCommon {
   1954 protected:
   1955     FiddleBase(BmhParser* bmh) : ParserCommon()
   1956         , fBmhParser(bmh)
   1957         , fContinuation(false)
   1958         , fTextOut(false)
   1959         , fPngOut(false)
   1960     {
   1961         this->reset();
   1962     }
   1963 
   1964     void reset() override {
   1965         INHERITED::resetCommon();
   1966     }
   1967 
   1968     Definition* findExample(string name) const { return fBmhParser->findExample(name); }
   1969     bool parseFiddles();
   1970     virtual bool pngOut(Definition* example) = 0;
   1971     virtual bool textOut(Definition* example, const char* stdOutStart,
   1972         const char* stdOutEnd) = 0;
   1973 
   1974     BmhParser* fBmhParser;  // must be writable; writes example hash
   1975     string fFullName;
   1976     bool fContinuation;
   1977     bool fTextOut;
   1978     bool fPngOut;
   1979 private:
   1980     typedef ParserCommon INHERITED;
   1981 };
   1982 
   1983 class FiddleParser : public FiddleBase {
   1984 public:
   1985     FiddleParser(BmhParser* bmh) : FiddleBase(bmh) {
   1986        fTextOut = true;
   1987     }
   1988 
   1989     bool parseFromFile(const char* path) override {
   1990         if (!INHERITED::parseSetup(path)) {
   1991             return false;
   1992         }
   1993         return parseFiddles();
   1994     }
   1995 
   1996 private:
   1997     bool pngOut(Definition* example) override {
   1998         return true;
   1999     }
   2000 
   2001     bool textOut(Definition* example, const char* stdOutStart,
   2002         const char* stdOutEnd) override;
   2003 
   2004     typedef FiddleBase INHERITED;
   2005 };
   2006 
   2007 class Catalog : public FiddleBase {
   2008 public:
   2009     Catalog(BmhParser* bmh) : FiddleBase(bmh) {}
   2010 
   2011     bool appendFile(const string& path);
   2012     bool closeCatalog();
   2013     bool openCatalog(const char* inDir, const char* outDir);
   2014     bool openStatus(const char* inDir, const char* outDir);
   2015 
   2016     bool parseFromFile(const char* path) override ;
   2017 private:
   2018     bool pngOut(Definition* example) override;
   2019     bool textOut(Definition* example, const char* stdOutStart,
   2020         const char* stdOutEnd) override;
   2021 
   2022     string fDocsDir;
   2023 
   2024     typedef FiddleBase INHERITED;
   2025 };
   2026 
   2027 class HackParser : public ParserCommon {
   2028 public:
   2029     HackParser(const BmhParser& bmhParser)
   2030         : ParserCommon()
   2031         , fBmhParser(bmhParser) {
   2032         this->reset();
   2033     }
   2034 
   2035     void addOneLiner(const Definition* defTable, const Definition* child, bool hasLine,
   2036             bool lfAfter);
   2037 
   2038     bool parseFromFile(const char* path) override {
   2039         if (!INHERITED::parseSetup(path)) {
   2040             return false;
   2041         }
   2042         return hackFiles();
   2043     }
   2044 
   2045     void reset() override {
   2046         INHERITED::resetCommon();
   2047     }
   2048 
   2049     string searchTable(const Definition* tableHolder, const Definition* match);
   2050 
   2051     void topicIter(const Definition* );
   2052 
   2053 private:
   2054     const BmhParser& fBmhParser;
   2055     const Definition* fClassesAndStructs;
   2056     const Definition* fConstants;
   2057     const Definition* fConstructors;
   2058     const Definition* fMemberFunctions;
   2059     const Definition* fMembers;
   2060     const Definition* fOperators;
   2061     const Definition* fRelatedFunctions;
   2062     bool hackFiles();
   2063 
   2064     typedef ParserCommon INHERITED;
   2065 };
   2066 
   2067 class MdOut : public ParserCommon {
   2068 public:
   2069     MdOut(const BmhParser& bmh) : ParserCommon()
   2070         , fBmhParser(bmh) {
   2071         this->reset();
   2072     }
   2073 
   2074     bool buildReferences(const char* docDir, const char* mdOutDirOrFile);
   2075     bool buildStatus(const char* docDir, const char* mdOutDir);
   2076 
   2077     static constexpr const char* kClassesAndStructs = "Class_or_Struct";
   2078     static constexpr const char* kConstants = "Constant";
   2079     static constexpr const char* kConstructors = "Constructor";
   2080     static constexpr const char* kMemberFunctions = "Member_Function";
   2081     static constexpr const char* kMembers = "Member";
   2082     static constexpr const char* kOperators = "Operator";
   2083     static constexpr const char* kOverview = "Overview";
   2084     static constexpr const char* kRelatedFunctions = "Related_Function";
   2085     static constexpr const char* kSubtopics = "Overview_Subtopic";
   2086 
   2087 private:
   2088     enum class TableState {
   2089         kNone,
   2090         kRow,
   2091         kColumn,
   2092     };
   2093 
   2094     struct TableContents {
   2095         TableContents()
   2096             : fShowClones(false) {
   2097         }
   2098 
   2099         string fDescription;
   2100         vector<const Definition*> fMembers;
   2101         bool fShowClones;
   2102     };
   2103 
   2104     string addReferences(const char* start, const char* end, BmhParser::Resolvable );
   2105     bool buildRefFromFile(const char* fileName, const char* outDir);
   2106     bool checkParamReturnBody(const Definition* def) const;
   2107     void childrenOut(const Definition* def, const char* contentStart);
   2108     const Definition* csParent() const;
   2109     const Definition* findParamType();
   2110     const Definition* isDefined(const TextParser& parser, const string& ref, bool report) const;
   2111     string linkName(const Definition* ) const;
   2112     string linkRef(const string& leadingSpaces, const Definition*, const string& ref,
   2113 			BmhParser::Resolvable ) const;
   2114     void markTypeOut(Definition* );
   2115     void mdHeaderOut(int depth) { mdHeaderOutLF(depth, 2); }
   2116     void mdHeaderOutLF(int depth, int lf);
   2117     void overviewOut();
   2118     bool parseFromFile(const char* path) override { return true; }
   2119     void populateTables(const Definition* def);
   2120 
   2121     TableContents& populator(const char* key) {
   2122         auto entry = fPopulators.find(key);
   2123         // FIXME: this should have been detected earlier
   2124         SkASSERT(fPopulators.end() != entry);
   2125         return entry->second;
   2126     }
   2127 
   2128     void reset() override {
   2129         INHERITED::resetCommon();
   2130         fEnumClass = nullptr;
   2131         fMethod = nullptr;
   2132         fRoot = nullptr;
   2133         fLastParam = nullptr;
   2134         fTableState = TableState::kNone;
   2135         fHasFiddle = false;
   2136         fInDescription = false;
   2137         fInList = false;
   2138         fRespectLeadingSpace = false;
   2139     }
   2140 
   2141     BmhParser::Resolvable resolvable(const Definition* definition) const {
   2142         MarkType markType = definition->fMarkType;
   2143         if (MarkType::kCode == markType) {
   2144             for (auto child : definition->fChildren) {
   2145                 if (MarkType::kLiteral == child->fMarkType) {
   2146                     return BmhParser::Resolvable::kLiteral;
   2147                 }
   2148             }
   2149         }
   2150         if ((MarkType::kExample == markType
   2151                 || MarkType::kFunction == markType) && fHasFiddle) {
   2152             return BmhParser::Resolvable::kNo;
   2153         }
   2154         return fBmhParser.fMaps[(int) markType].fResolve;
   2155     }
   2156 
   2157     void resolveOut(const char* start, const char* end, BmhParser::Resolvable );
   2158     void rowOut(const char * name, const string& description);
   2159     void subtopicOut(const TableContents& tableContents);
   2160     void subtopicsOut();
   2161 
   2162     unordered_map<string, TableContents> fPopulators;
   2163     vector<const Definition*> fClassStack;
   2164 
   2165     const BmhParser& fBmhParser;
   2166     const Definition* fEnumClass;
   2167     Definition* fMethod;
   2168     const RootDefinition* fRoot;
   2169     const Definition* fLastParam;
   2170     TableState fTableState;
   2171     bool fHasFiddle;
   2172     bool fInDescription;   // FIXME: for now, ignore unfound camelCase in description since it may
   2173                            // be defined in example which at present cannot be linked to
   2174     bool fInList;
   2175     bool fRespectLeadingSpace;
   2176     typedef ParserCommon INHERITED;
   2177 };
   2178 
   2179 
   2180 // some methods cannot be trivially parsed; look for class-name / ~ / operator
   2181 class MethodParser : public TextParser {
   2182 public:
   2183     MethodParser(const string& className, const string& fileName,
   2184             const char* start, const char* end, int lineCount)
   2185         : TextParser(fileName, start, end, lineCount)
   2186         , fClassName(className) {
   2187     }
   2188 
   2189     ~MethodParser() override {}
   2190 
   2191     void skipToMethodStart() {
   2192         if (!fClassName.length()) {
   2193             this->skipToAlphaNum();
   2194             return;
   2195         }
   2196         while (!this->eof() && !isalnum(this->peek()) && '~' != this->peek()) {
   2197             this->next();
   2198         }
   2199     }
   2200 
   2201     void skipToMethodEnd() {
   2202         if (this->eof()) {
   2203             return;
   2204         }
   2205         if (fClassName.length()) {
   2206             if ('~' == this->peek()) {
   2207                 this->next();
   2208                 if (!this->startsWith(fClassName.c_str())) {
   2209                     --fChar;
   2210                     return;
   2211                 }
   2212             }
   2213             if (this->startsWith(fClassName.c_str()) || this->startsWith("operator")) {
   2214                 const char* ptr = this->anyOf("\n (");
   2215                 if (ptr && '(' ==  *ptr) {
   2216                     this->skipToEndBracket(')');
   2217                     SkAssertResult(')' == this->next());
   2218                     this->skipExact("_const");
   2219                     return;
   2220                 }
   2221             }
   2222         }
   2223         if (this->startsWith("Sk") && this->wordEndsWith(".h")) {  // allow include refs
   2224             this->skipToNonAlphaNum();
   2225         } else {
   2226             this->skipFullName();
   2227             if (this->endsWith("operator")) {
   2228                 const char* ptr = this->anyOf("\n (");
   2229                 if (ptr && '(' ==  *ptr) {
   2230                     this->skipToEndBracket(')');
   2231                     SkAssertResult(')' == this->next());
   2232                     this->skipExact("_const");
   2233                 }
   2234             }
   2235         }
   2236     }
   2237 
   2238     bool wordEndsWith(const char* str) const {
   2239         const char* space = this->strnchr(' ', fEnd);
   2240         if (!space) {
   2241             return false;
   2242         }
   2243         size_t len = strlen(str);
   2244         if (space < fChar + len) {
   2245             return false;
   2246         }
   2247         return !strncmp(str, space - len, len);
   2248     }
   2249 
   2250 private:
   2251     string fClassName;
   2252     typedef TextParser INHERITED;
   2253 };
   2254 
   2255 bool SelfCheck(const BmhParser& );
   2256 
   2257 #endif
   2258