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 #include "bookmaker.h"
      9 #include "SkOSFile.h"
     10 #include "SkOSPath.h"
     11 
     12 const IncludeKey kKeyWords[] = {
     13     { "",           KeyWord::kNone,         KeyProperty::kNone           },
     14     { "SK_API",     KeyWord::kSK_API,       KeyProperty::kModifier       },
     15     { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
     16     { "bool",       KeyWord::kBool,         KeyProperty::kNumber         },
     17     { "char",       KeyWord::kChar,         KeyProperty::kNumber         },
     18     { "class",      KeyWord::kClass,        KeyProperty::kObject         },
     19     { "const",      KeyWord::kConst,        KeyProperty::kModifier       },
     20     { "constexpr",  KeyWord::kConstExpr,    KeyProperty::kModifier       },
     21     { "define",     KeyWord::kDefine,       KeyProperty::kPreprocessor   },
     22     { "double",     KeyWord::kDouble,       KeyProperty::kNumber         },
     23     { "elif",       KeyWord::kElif,         KeyProperty::kPreprocessor   },
     24     { "else",       KeyWord::kElse,         KeyProperty::kPreprocessor   },
     25     { "endif",      KeyWord::kEndif,        KeyProperty::kPreprocessor   },
     26     { "enum",       KeyWord::kEnum,         KeyProperty::kObject         },
     27     { "error",      KeyWord::kError,        KeyProperty::kPreprocessor   },
     28     { "float",      KeyWord::kFloat,        KeyProperty::kNumber         },
     29     { "friend",     KeyWord::kFriend,       KeyProperty::kModifier       },
     30     { "if",         KeyWord::kIf,           KeyProperty::kPreprocessor   },
     31     { "ifdef",      KeyWord::kIfdef,        KeyProperty::kPreprocessor   },
     32     { "ifndef",     KeyWord::kIfndef,       KeyProperty::kPreprocessor   },
     33     { "include",    KeyWord::kInclude,      KeyProperty::kPreprocessor   },
     34     { "inline",     KeyWord::kInline,       KeyProperty::kModifier       },
     35     { "int",        KeyWord::kInt,          KeyProperty::kNumber         },
     36     { "operator",   KeyWord::kOperator,     KeyProperty::kFunction       },
     37     { "private",    KeyWord::kPrivate,      KeyProperty::kClassSection   },
     38     { "protected",  KeyWord::kProtected,    KeyProperty::kClassSection   },
     39     { "public",     KeyWord::kPublic,       KeyProperty::kClassSection   },
     40     { "signed",     KeyWord::kSigned,       KeyProperty::kNumber         },
     41     { "size_t",     KeyWord::kSize_t,       KeyProperty::kNumber         },
     42     { "static",     KeyWord::kStatic,       KeyProperty::kModifier       },
     43     { "struct",     KeyWord::kStruct,       KeyProperty::kObject         },
     44     { "template",   KeyWord::kTemplate,     KeyProperty::kObject         },
     45     { "typedef",    KeyWord::kTypedef,      KeyProperty::kObject         },
     46     { "uint16_t",   KeyWord::kUint16_t,     KeyProperty::kNumber         },
     47     { "uint32_t",   KeyWord::kUint32_t,     KeyProperty::kNumber         },
     48     { "uint64_t",   KeyWord::kUint64_t,     KeyProperty::kNumber         },
     49     { "uint8_t",    KeyWord::kUint8_t,      KeyProperty::kNumber         },
     50     { "union",      KeyWord::kUnion,        KeyProperty::kObject         },
     51     { "unsigned",   KeyWord::kUnsigned,     KeyProperty::kNumber         },
     52     { "void",       KeyWord::kVoid,         KeyProperty::kNumber         },
     53 };
     54 
     55 const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
     56 
     57 KeyWord IncludeParser::FindKey(const char* start, const char* end) {
     58     int ch = 0;
     59     for (size_t index = 0; index < kKeyWordCount; ) {
     60         if (start[ch] > kKeyWords[index].fName[ch]) {
     61             ++index;
     62             if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
     63                 return KeyWord::kNone;
     64             }
     65             continue;
     66         }
     67         if (start[ch] < kKeyWords[index].fName[ch]) {
     68             return KeyWord::kNone;
     69         }
     70         ++ch;
     71         if (start + ch >= end) {
     72             if (end - start < (int) strlen(kKeyWords[index].fName)) {
     73                 return KeyWord::kNone;
     74             }
     75             return kKeyWords[index].fKeyWord;
     76         }
     77     }
     78     return KeyWord::kNone;
     79 }
     80 
     81 void IncludeParser::ValidateKeyWords() {
     82     for (size_t index = 1; index < kKeyWordCount; ++index) {
     83         SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
     84                 == (int) kKeyWords[index].fKeyWord);
     85         SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
     86     }
     87 }
     88 
     89 void IncludeParser::addKeyword(KeyWord keyWord) {
     90     fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent);
     91     fIncludeWord = nullptr;
     92     if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
     93         Definition* def = &fParent->fTokens.back();
     94         this->addDefinition(def);
     95         if (KeyWord::kEnum == fParent->fKeyWord) {
     96             fInEnum = true;
     97         }
     98     }
     99 }
    100 
    101 void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
    102         const vector<string>& foundParams) {
    103     for (auto& methodParam : methodParams) {
    104         bool found = false;
    105         for (auto& foundParam : foundParams) {
    106             if (methodParam == foundParam) {
    107                 found = true;
    108                 break;
    109             }
    110         }
    111         if (!found) {
    112             this->writeIncompleteTag("Param", methodParam, 2);
    113         }
    114     }
    115     for (auto& foundParam : foundParams) {
    116         bool found = false;
    117         for (auto& methodParam : methodParams) {
    118             if (methodParam == foundParam) {
    119                 found = true;
    120                 break;
    121             }
    122         }
    123         if (!found) {
    124             this->reportError("doxygen param does not match method declaration");
    125         }
    126     }
    127 }
    128 
    129 bool IncludeParser::checkForWord() {
    130     if (!fIncludeWord) {
    131         return true;
    132     }
    133     KeyWord keyWord = FindKey(fIncludeWord, fChar);
    134     if (KeyWord::kNone != keyWord) {
    135         if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
    136             this->addKeyword(keyWord);
    137             return true;
    138         }
    139     } else {
    140         this->addWord();
    141         return true;
    142     }
    143     Definition* poundDef = fParent;
    144     if (!fParent) {
    145         return reportError<bool>("expected parent");
    146     }
    147     if (Definition::Type::kBracket != poundDef->fType) {
    148         return reportError<bool>("expected bracket");
    149     }
    150     if (Bracket::kPound != poundDef->fBracket) {
    151         return reportError<bool>("expected preprocessor");
    152     }
    153     if (KeyWord::kNone != poundDef->fKeyWord) {
    154         return reportError<bool>("already found keyword");
    155     }
    156     poundDef->fKeyWord = keyWord;
    157     fIncludeWord = nullptr;
    158     switch (keyWord) {
    159         // these do not link to other # directives
    160         case KeyWord::kDefine:
    161         case KeyWord::kInclude:
    162         case KeyWord::kError:
    163         break;
    164         // these start a # directive link
    165         case KeyWord::kIf:
    166         case KeyWord::kIfdef:
    167         case KeyWord::kIfndef:
    168         break;
    169         // these continue a # directive link
    170         case KeyWord::kElif:
    171         case KeyWord::kElse: {
    172             this->popObject();  // pop elif
    173             if (Bracket::kPound != fParent->fBracket) {
    174                 return this->reportError<bool>("expected preprocessor directive");
    175             }
    176             this->popBracket();  // pop if
    177             poundDef->fParent = fParent;
    178             this->addDefinition(poundDef);  // push elif back
    179         } break;
    180         // this ends a # directive link
    181         case KeyWord::kEndif:
    182         // FIXME : should this be calling popBracket() instead?
    183             this->popObject();  // pop endif
    184             if (Bracket::kPound != fParent->fBracket) {
    185                 return this->reportError<bool>("expected preprocessor directive");
    186             }
    187             this->popBracket();  // pop if/else
    188         break;
    189         default:
    190             SkASSERT(0);
    191     }
    192     return true;
    193 }
    194 
    195 string IncludeParser::className() const {
    196     string name(fParent->fName);
    197     size_t slash = name.find_last_of("/");
    198     if (string::npos == slash) {
    199         slash = name.find_last_of("\\");
    200     }
    201     SkASSERT(string::npos != slash);
    202     string result = name.substr(slash);
    203     result = result.substr(1, result.size() - 3);
    204     return result;
    205 }
    206 
    207 #include <sstream>
    208 #include <iostream>
    209 
    210 bool IncludeParser::crossCheck(BmhParser& bmhParser) {
    211     for (auto& classMapper : fIClassMap) {
    212         string className = classMapper.first;
    213         auto finder = bmhParser.fClassMap.find(className);
    214         if (bmhParser.fClassMap.end() == finder) {
    215             SkASSERT(string::npos != className.find("::"));
    216             continue;
    217         }
    218         RootDefinition* root = &finder->second;
    219         root->clearVisited();
    220     }
    221     for (auto& classMapper : fIClassMap) {
    222         string className = classMapper.first;
    223         std::istringstream iss(className);
    224         string classStr;
    225         string classBase;
    226         RootDefinition* root = nullptr;
    227         while (std::getline(iss, classStr, ':')) {
    228             if (root) {
    229                 if (!classStr.length()) {
    230                     continue;
    231                 }
    232                 classBase += "::" + classStr;
    233                 auto finder = root->fBranches.find(classBase);
    234                 if (root->fBranches.end() != finder) {
    235                     root = finder->second;
    236                 } else {
    237                     SkASSERT(0);
    238                 }
    239             } else {
    240                 classBase = classStr;
    241                 auto finder = bmhParser.fClassMap.find(classBase);
    242                 if (bmhParser.fClassMap.end() != finder) {
    243                     root = &finder->second;
    244                 } else {
    245                     SkASSERT(0);
    246                 }
    247             }
    248         }
    249         auto& classMap = classMapper.second;
    250         auto& tokens = classMap.fTokens;
    251         for (const auto& token : tokens) {
    252             if (token.fPrivate) {
    253                 continue;
    254             }
    255             string fullName = classMapper.first + "::" + token.fName;
    256             const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
    257             switch (token.fMarkType) {
    258                 case MarkType::kMethod: {
    259                     if (this->internalName(token)) {
    260                         continue;
    261                     }
    262                     if (!def) {
    263                         string paramName = className + "::";
    264                         paramName += string(token.fContentStart,
    265                                 token.fContentEnd - token.fContentStart);
    266                         def = root->find(paramName, RootDefinition::AllowParens::kYes);
    267                         if (!def && 0 == token.fName.find("operator")) {
    268                             string operatorName = className + "::";
    269                             TextParser oper("", token.fStart, token.fContentEnd, 0);
    270                             const char* start = oper.strnstr("operator", token.fContentEnd);
    271                             SkASSERT(start);
    272                             oper.skipTo(start);
    273                             oper.skipToEndBracket('(');
    274                             int parens = 0;
    275                             do {
    276                                 if ('(' == oper.peek()) {
    277                                     ++parens;
    278                                 } else if (')' == oper.peek()) {
    279                                     --parens;
    280                                 }
    281                             } while (!oper.eof() && oper.next() && parens > 0);
    282                             operatorName += string(start, oper.fChar - start);
    283                             def = root->find(operatorName, RootDefinition::AllowParens::kYes);
    284                         }
    285                     }
    286                     if (!def) {
    287                         int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
    288                         skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
    289                         string constructorName = className + "::";
    290                         constructorName += string(token.fContentStart + skip,
    291                                 token.fContentEnd - token.fContentStart - skip);
    292                         def = root->find(constructorName, RootDefinition::AllowParens::kYes);
    293                     }
    294                     if (!def && 0 == token.fName.find("SK_")) {
    295                         string incName = token.fName + "()";
    296                         string macroName = className + "::" + incName;
    297                         def = root->find(macroName, RootDefinition::AllowParens::kYes);
    298                         if (def) {
    299                             if (def->fName == incName) {
    300                                 def->fVisited = true;
    301                                 if ("SK_TO_STRING_NONVIRT" == token.fName) {
    302                                     def = root->find(className + "::toString",
    303                                             RootDefinition::AllowParens::kYes);
    304                                     if (def) {
    305                                         def->fVisited = true;
    306                                     } else {
    307                                         SkDebugf("missing toString bmh: %s\n", fullName.c_str());
    308                                         fFailed = true;
    309                                     }
    310                                 }
    311                                 break;
    312                             } else {
    313                                 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
    314                                 fFailed = true;
    315                             }
    316                         }
    317                     }
    318                     if (!def) {
    319                         bool allLower = true;
    320                         for (size_t index = 0; index < token.fName.length(); ++index) {
    321                             if (!islower(token.fName[index])) {
    322                                 allLower = false;
    323                                 break;
    324                             }
    325                         }
    326                         if (allLower) {
    327                             string lowerName = className + "::" + token.fName + "()";
    328                             def = root->find(lowerName, RootDefinition::AllowParens::kYes);
    329                         }
    330                     }
    331                     if (!def) {
    332                         if ("SK_ATTR_DEPRECATED" == token.fName) {
    333                             break;
    334                         }
    335                         if (0 == token.fName.find("SkDEBUGCODE")) {
    336                             break;
    337                         }
    338                     }
    339                     if (!def) {
    340             // simple method names inside nested classes have a bug and are missing trailing parens
    341                         string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
    342                         def = root->find(withParens, RootDefinition::AllowParens::kNo);
    343                     }
    344                     if (!def) {
    345                         if (!root->fDeprecated) {
    346                             SkDebugf("method missing from bmh: %s\n", fullName.c_str());
    347                             fFailed = true;
    348                         }
    349                         break;
    350                     }
    351                     if (def->crossCheck2(token)) {
    352                         def->fVisited = true;
    353                         if (MarkType::kDefinedBy == def->fMarkType) {
    354                             def->fParent->fVisited = true;
    355                         }
    356                     } else {
    357                        SkDebugf("method differs from bmh: %s\n", fullName.c_str());
    358                        fFailed = true;
    359                     }
    360                 } break;
    361                 case MarkType::kComment:
    362                     break;
    363                 case MarkType::kEnumClass:
    364                 case MarkType::kEnum: {
    365                     if (!def) {
    366                         // work backwards from first word to deduce #Enum name
    367                         TextParser firstMember("", token.fStart, token.fContentEnd, 0);
    368                         SkAssertResult(firstMember.skipName("enum"));
    369                         SkAssertResult(firstMember.skipToEndBracket('{'));
    370                         firstMember.next();
    371                         firstMember.skipWhiteSpace();
    372                         SkASSERT('k' == firstMember.peek());
    373                         const char* savePos = firstMember.fChar;
    374                         firstMember.skipToNonAlphaNum();
    375                         const char* wordEnd = firstMember.fChar;
    376                         firstMember.fChar = savePos;
    377                         const char* lastUnderscore = nullptr;
    378                         do {
    379                             if (!firstMember.skipToEndBracket('_')) {
    380                                 break;
    381                             }
    382                             if (firstMember.fChar > wordEnd) {
    383                                 break;
    384                             }
    385                             lastUnderscore = firstMember.fChar;
    386                         } while (firstMember.next());
    387                         if (lastUnderscore) {
    388                             ++lastUnderscore;
    389                             string anonName = className + "::" + string(lastUnderscore,
    390                                     wordEnd - lastUnderscore) + 's';
    391                             def = root->find(anonName, RootDefinition::AllowParens::kYes);
    392                         }
    393                         if (!def) {
    394                             if (!root->fDeprecated) {
    395                                 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
    396                                 fFailed = true;
    397                             }
    398                             break;
    399                         }
    400                     }
    401                     def->fVisited = true;
    402                     for (auto& child : def->fChildren) {
    403                         if (MarkType::kCode == child->fMarkType) {
    404                             def = child;
    405                             break;
    406                         }
    407                     }
    408                     if (MarkType::kCode != def->fMarkType) {
    409                         if (!root->fDeprecated) {
    410                             SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
    411                             fFailed = true;
    412                         }
    413                         break;
    414                     }
    415                     if (def->crossCheck(token)) {
    416                         def->fVisited = true;
    417                     } else {
    418                         SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
    419                         fFailed = true;
    420                     }
    421                     for (auto& child : token.fChildren) {
    422                         string constName = MarkType::kEnumClass == token.fMarkType ?
    423                                 fullName : className;
    424                         constName += "::" + child->fName;
    425                         def = root->find(constName, RootDefinition::AllowParens::kYes);
    426                         if (!def) {
    427                             string innerName = classMapper.first + "::" + child->fName;
    428                             def = root->find(innerName, RootDefinition::AllowParens::kYes);
    429                         }
    430                         if (!def) {
    431                             if (string::npos == child->fName.find("Legacy_")) {
    432                                 if (!root->fDeprecated) {
    433                                     SkDebugf("const missing from bmh: %s\n", constName.c_str());
    434                                     fFailed = true;
    435                                 }
    436                             }
    437                         } else {
    438                             def->fVisited = true;
    439                         }
    440                     }
    441                     } break;
    442                 case MarkType::kMember:
    443                     if (def) {
    444                         def->fVisited = true;
    445                     } else if (!root->fDeprecated) {
    446                         SkDebugf("member missing from bmh: %s\n", fullName.c_str());
    447                         fFailed = true;
    448                     }
    449                     break;
    450                 case MarkType::kTypedef:
    451                     if (def) {
    452                         def->fVisited = true;
    453                     } else if (!root->fDeprecated) {
    454                         SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
    455                         fFailed = true;
    456                     }
    457                     break;
    458                 default:
    459                     SkASSERT(0);  // unhandled
    460                     break;
    461             }
    462         }
    463     }
    464     int crossChecks = 0;
    465     string firstCheck;
    466     for (auto& classMapper : fIClassMap) {
    467         string className = classMapper.first;
    468         auto finder = bmhParser.fClassMap.find(className);
    469         if (bmhParser.fClassMap.end() == finder) {
    470             continue;
    471         }
    472         RootDefinition* root = &finder->second;
    473         if (!root->dumpUnVisited()) {
    474             fFailed = true;
    475         }
    476         if (crossChecks) {
    477             SkDebugf(".");
    478         } else {
    479             SkDebugf("cross-check");
    480             firstCheck = className;
    481         }
    482         ++crossChecks;
    483     }
    484     if (crossChecks) {
    485         if (1 == crossChecks) {
    486             SkDebugf(" %s", firstCheck.c_str());
    487         }
    488         SkDebugf("\n");
    489     }
    490     bmhParser.fWroteOut = true;
    491     return !fFailed;
    492 }
    493 
    494 IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
    495         const string& name) {
    496     string className;
    497     const Definition* test = fParent;
    498     while (Definition::Type::kFileType != test->fType) {
    499         if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
    500             className = test->fName + "::";
    501             break;
    502         }
    503         test = test->fParent;
    504     }
    505     className += name;
    506     unordered_map<string, IClassDefinition>& map = fIClassMap;
    507     IClassDefinition& markupDef = map[className];
    508     if (markupDef.fStart) {
    509         typedef IClassDefinition* IClassDefPtr;
    510         return INHERITED::reportError<IClassDefPtr>("class already defined");
    511     }
    512     markupDef.fFileName = fFileName;
    513     markupDef.fStart = includeDef.fStart;
    514     markupDef.fContentStart = includeDef.fStart;
    515     markupDef.fName = className;
    516     markupDef.fContentEnd = includeDef.fContentEnd;
    517     markupDef.fTerminator = includeDef.fTerminator;
    518     markupDef.fParent = fParent;
    519     markupDef.fLineCount = fLineCount;
    520     markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
    521             MarkType::kStruct : MarkType::kClass;
    522     markupDef.fKeyWord = includeDef.fKeyWord;
    523     markupDef.fType = Definition::Type::kMark;
    524     fParent = &markupDef;
    525     return &markupDef;
    526 }
    527 
    528 void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
    529     auto& tokens = classDef.fTokens;
    530     for (auto& token : tokens) {
    531         if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
    532             continue;
    533         }
    534         if (MarkType::kMember != token.fMarkType) {
    535             this->writeString(
    536               "# ------------------------------------------------------------------------------");
    537             this->lf(2);
    538         }
    539         switch (token.fMarkType) {
    540             case MarkType::kEnum:
    541             case MarkType::kEnumClass:
    542                 this->dumpEnum(token, token.fName);
    543             break;
    544             case MarkType::kMethod:
    545                 this->dumpMethod(token);
    546             break;
    547             case MarkType::kMember:
    548                 this->dumpMember(token);
    549                 continue;
    550             break;
    551             default:
    552                 SkASSERT(0);
    553         }
    554         this->lf(2);
    555         this->writeTag("Example");
    556         this->lf(1);
    557         this->writeString("// incomplete");
    558         this->lf(1);
    559         this->writeEndTag();
    560         this->lf(2);
    561         this->writeTag("SeeAlso");
    562         this->writeSpace();
    563         this->writeString("incomplete");
    564         this->lf(2);
    565         switch (token.fMarkType) {
    566             case MarkType::kEnum:
    567             case MarkType::kEnumClass:
    568                 this->writeEndTag("Enum");
    569             break;
    570             case MarkType::kMethod:
    571                 this->writeEndTag("Method");
    572             break;
    573             case MarkType::kMember:
    574                 this->writeEndTag("Member");
    575                 continue;
    576             break;
    577             default:
    578                 SkASSERT(0);
    579         }
    580         this->lf(2);
    581     }
    582 }
    583 void IncludeParser::dumpComment(const Definition& token) {
    584     fLineCount = token.fLineCount;
    585     fChar = fLine = token.fContentStart;
    586     fEnd = token.fContentEnd;
    587     bool sawParam = false;
    588     bool multiline = false;
    589     bool sawReturn = false;
    590     bool sawComment = false;
    591     bool methodHasReturn = false;
    592     vector<string> methodParams;
    593     vector<string> foundParams;
    594     Definition methodName;
    595     TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
    596             token.fLineCount);
    597     bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
    598     if (MarkType::kMethod == token.fMarkType) {
    599         methodName.fName = debugCode ? token.fName : string(token.fContentStart,
    600                 (int) (token.fContentEnd - token.fContentStart));
    601         methodHasReturn = !methodParser.startsWith("void ")
    602                 && !methodParser.startsWith("static void ")
    603                 && !methodParser.strnchr('~', methodParser.fEnd);
    604         const char* paren = methodParser.strnchr('(', methodParser.fEnd);
    605         const char* nextEnd = paren;
    606         do {
    607             string paramName;
    608             methodParser.fChar = nextEnd + 1;
    609             methodParser.skipSpace();
    610             if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
    611                 continue;
    612             }
    613             methodParams.push_back(paramName);
    614         } while (')' != nextEnd[0]);
    615     }
    616     for (const auto& child : token.fTokens) {
    617         if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
    618             break;
    619         }
    620         if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
    621             if (child.fPrivate) {
    622                 break;
    623             }
    624             if ('@' == child.fContentStart[0]) {
    625                 TextParser parser(&child);
    626                 do {
    627                     parser.next();
    628                     if (parser.startsWith("param ")) {
    629                         parser.skipWord("param");
    630                         const char* parmStart = parser.fChar;
    631                         parser.skipToSpace();
    632                         string parmName = string(parmStart, (int) (parser.fChar - parmStart));
    633                         parser.skipWhiteSpace();
    634                         do {
    635                             size_t nextComma = parmName.find(',');
    636                             string piece;
    637                             if (string::npos == nextComma) {
    638                                 piece = parmName;
    639                                 parmName = "";
    640                             } else {
    641                                 piece = parmName.substr(0, nextComma);
    642                                 parmName = parmName.substr(nextComma + 1);
    643                             }
    644                             if (sawParam) {
    645                                 if (multiline) {
    646                                     this->lf(1);
    647                                 }
    648                                 this->writeEndTag();
    649                             } else {
    650                                 if (sawComment) {
    651                                     this->nl();
    652                                 }
    653                                 this->lf(2);
    654                             }
    655                             foundParams.emplace_back(piece);
    656                             this->writeTag("Param", piece);
    657                             this->writeSpace(2);
    658                             this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
    659                             this->lf(1);
    660                             sawParam = true;
    661                             sawComment = false;
    662                         } while (parmName.length());
    663                         parser.skipTo(parser.fEnd);
    664                     } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
    665                         parser.skipWord("return");
    666                         if ('s' == parser.peek()) {
    667                             parser.next();
    668                         }
    669                         if (sawParam) {
    670                             if (multiline) {
    671                                 this->lf(1);
    672                             }
    673                             this->writeEndTag();
    674                         }
    675                         this->checkForMissingParams(methodParams, foundParams);
    676                         sawParam = false;
    677                         sawComment = false;
    678                         multiline = false;
    679                         this->lf(2);
    680                         this->writeTag("Return");
    681                         this->writeSpace(2);
    682                         this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
    683                         this->lf(1);
    684                         sawReturn = true;
    685                         parser.skipTo(parser.fEnd);
    686                     } else {
    687                         this->reportError("unexpected doxygen directive");
    688                     }
    689                 } while (!parser.eof());
    690             } else if (child.length() > 1) {
    691                 const char* start = child.fContentStart;
    692                 ptrdiff_t length = child.fContentEnd - start;
    693                 SkASSERT(length >= 0);
    694                 while (length && '/' == start[0]) {
    695                     start += 1;
    696                     --length;
    697                 }
    698                 while (length && '/' == start[length - 1]) {
    699                     length -= 1;
    700                     if (length && '*' == start[length - 1]) {
    701                         length -= 1;
    702                     }
    703                 }
    704                 if (length) {
    705                     this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
    706                     if (sawParam || sawReturn) {
    707                         this->indentToColumn(8);
    708                     }
    709                     this->writeBlock(length, start);
    710                     this->writeSpace();
    711                     sawComment = true;
    712                     if (sawParam || sawReturn) {
    713                         multiline = true;
    714                     }
    715                 }
    716             }
    717         }
    718     }
    719     if (sawParam || sawReturn) {
    720         if (multiline) {
    721             this->lf(1);
    722         }
    723         this->writeEndTag();
    724     }
    725     if (!sawReturn) {
    726         if (!sawParam) {
    727             if (sawComment) {
    728                 this->nl();
    729             }
    730             this->lf(2);
    731         }
    732         this->checkForMissingParams(methodParams, foundParams);
    733     }
    734     if (methodHasReturn != sawReturn) {
    735         if (!methodHasReturn) {
    736             this->reportError("unexpected doxygen return");
    737         } else {
    738             if (sawComment) {
    739                 this->nl();
    740             }
    741             this->lf(2);
    742             this->writeIncompleteTag("Return");
    743         }
    744     }
    745 }
    746 
    747 void IncludeParser::dumpEnum(const Definition& token, const string& name) {
    748     this->writeTag("Enum", name);
    749     this->lf(2);
    750     this->writeString("#Code");
    751     this->lfAlways(1);
    752     this->indentToColumn(4);
    753     this->writeString("enum");
    754     this->writeSpace();
    755     if ("_anonymous" != token.fName.substr(0, 10)) {
    756         this->writeString(token.fName);
    757         this->writeSpace();
    758     }
    759     this->writeString("{");
    760     this->lfAlways(1);
    761     for (auto& child : token.fChildren) {
    762         this->indentToColumn(8);
    763         this->writeString(child->fName);
    764         if (child->length()) {
    765             this->writeSpace();
    766             this->writeBlock(child->length(), child->fContentStart);
    767         }
    768         if (',' != fLastChar) {
    769             this->writeString(",");
    770         }
    771         this->lfAlways(1);
    772     }
    773     this->indentToColumn(4);
    774     this->writeString("};");
    775     this->lf(1);
    776     this->writeString("##");
    777     this->lf(2);
    778     this->dumpComment(token);
    779     for (auto& child : token.fChildren) {
    780     //     start here;
    781         // get comments before
    782         // or after const values
    783         this->writeString("#Const");
    784         this->writeSpace();
    785         this->writeString(child->fName);
    786         TextParser val(child);
    787         if (!val.eof()) {
    788             if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
    789                 val.next();
    790                 val.skipSpace();
    791                 const char* valEnd = val.anyOf(",\n");
    792                 if (!valEnd) {
    793                     valEnd = val.fEnd;
    794                 }
    795                 this->writeSpace();
    796                 this->writeBlock(valEnd - val.fStart, val.fStart);
    797             } else {
    798                 this->writeSpace();
    799                 this->writeDefinition(*child);
    800             }
    801         }
    802         this->lf(1);
    803         for (auto comment : child->fChildren) {
    804             if (MarkType::kComment == comment->fMarkType) {
    805                 TextParser parser(comment);
    806                 parser.skipExact("*");
    807                 parser.skipExact("*");
    808                 while (!parser.eof() && parser.skipWhiteSpace()) {
    809                     parser.skipExact("*");
    810                     parser.skipWhiteSpace();
    811                     const char* start = parser.fChar;
    812                     parser.skipToEndBracket('\n');
    813                     this->lf(1);
    814                     this->writeBlock(parser.fChar - start, start);
    815                 }
    816             }
    817         }
    818         this->writeEndTag();
    819     }
    820     this->lf(2);
    821 }
    822 
    823 void IncludeParser::dumpMethod(const Definition& token) {
    824     this->writeString("#Method");
    825     this->writeSpace();
    826     if ("SK_TO_STRING_NONVIRT" == token.fName) {
    827         this->writeString("void toString(SkString* str) const;");
    828         this->lf(2);
    829         this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
    830         this->lf(2);
    831         this->writeTag("Private");
    832         this->lf(1);
    833         this->writeString("macro expands to: void toString(SkString* str) const;");
    834         this->writeEndTag();
    835         this->lf(2);
    836         const char desc[] =
    837                 "Creates string representation. The representation is read by\n"
    838                 "internal debugging tools. The interface and implementation may be\n"
    839                 "suppressed by defining SK_IGNORE_TO_STRING.";
    840         this->writeBlock(sizeof(desc) - 1, desc);
    841         this->lf(2);
    842         this->writeTag("Param", "str");
    843         this->writeSpace(2);
    844         this->writeString("storage for string representation");
    845         this->writeSpace();
    846         this->writeString("##");
    847         this->lf(2);
    848         return;
    849     }
    850     this->writeBlock(token.length(), token.fStart);
    851     this->lf(1);
    852     this->dumpComment(token);
    853 }
    854 
    855 void IncludeParser::dumpMember(const Definition& token) {
    856     this->writeTag("Member");
    857     this->writeSpace();
    858     this->writeDefinition(token, token.fName, 2);
    859     lf(1);
    860     for (auto child : token.fChildren) {
    861         this->writeDefinition(*child);
    862     }
    863     this->writeEndTag();
    864     lf(2);
    865 }
    866 
    867 bool IncludeParser::dumpTokens(const string& dir) {
    868     for (const auto& member : fIClassMap) {
    869         if (string::npos != member.first.find("::")) {
    870             continue;
    871         }
    872         if (!this->dumpTokens(dir, member.first)) {
    873             return false;
    874         }
    875     }
    876     return true;
    877 }
    878 
    879     // dump equivalent markup
    880 bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
    881     string fileName = dir;
    882     if (dir.length() && '/' != dir[dir.length() - 1]) {
    883         fileName += '/';
    884     }
    885     fileName += skClassName + "_Reference.bmh";
    886     fOut = fopen(fileName.c_str(), "wb");
    887     if (!fOut) {
    888         SkDebugf("could not open output file %s\n", fileName.c_str());
    889         return false;
    890     }
    891     string prefixName = skClassName.substr(0, 2);
    892     string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
    893         ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
    894     this->writeTagNoLF("Topic", topicName);
    895     this->writeTag("Alias", topicName + "_Reference");
    896     this->lf(2);
    897     auto& classMap = fIClassMap[skClassName];
    898     SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
    899     const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
    900     this->writeTag(containerType, skClassName);
    901     this->lf(2);
    902     auto& tokens = classMap.fTokens;
    903     for (auto& token : tokens) {
    904         if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
    905             continue;
    906         }
    907         this->writeDefinition(token);
    908         this->lf(1);
    909     }
    910     this->lf(2);
    911     string className(skClassName.substr(2));
    912     vector<string> classNames;
    913     vector<string> constNames;
    914     vector<string> constructorNames;
    915     vector<string> memberNames;
    916     vector<string> operatorNames;
    917     size_t classMaxLen = 0;
    918     size_t constMaxLen = 0;
    919     size_t constructorMaxLen = 0;
    920     size_t memberMaxLen = 0;
    921     size_t operatorMaxLen = 0;
    922     for (const auto& oneClass : fIClassMap) {
    923         if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
    924             continue;
    925         }
    926         string structName = oneClass.first.substr(skClassName.length() + 2);
    927         classMaxLen = SkTMax(classMaxLen, structName.length());
    928         classNames.emplace_back(structName);
    929     }
    930     for (const auto& oneEnum : fIEnumMap) {
    931         string enumName = oneEnum.first;
    932         constMaxLen = SkTMax(constMaxLen, enumName.length());
    933         constNames.emplace_back(enumName);
    934     }
    935     for (const auto& token : classMap.fTokens) {
    936         if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
    937             continue;
    938         }
    939         string name = token.fName;
    940         if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
    941             continue;
    942         }
    943         if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
    944             name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
    945             constructorMaxLen = SkTMax(constructorMaxLen, name.length());
    946             constructorNames.emplace_back(name);
    947             continue;
    948         }
    949         if (name.substr(0, 8) == "operator") {
    950             name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
    951             operatorMaxLen = SkTMax(operatorMaxLen, name.length());
    952             operatorNames.emplace_back(name);
    953             continue;
    954         }
    955         if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
    956             continue;
    957         }
    958         if ("SK_TO_STRING_NONVIRT" == name) {
    959             name = "toString";
    960         }
    961         size_t paren = name.find('(');
    962         size_t funcLen = string::npos == paren ? name.length() : paren;
    963         memberMaxLen = SkTMax(memberMaxLen, funcLen);
    964         memberNames.emplace_back(name);
    965     }
    966     this->writeTag("Topic", "Overview");
    967     this->lf(2);
    968     this->writeTag("Subtopic", "Subtopics");
    969     string classesName = classMaxLen ? "Classes_and_Structs" : "";
    970     string constsName = constructorMaxLen ? "Constants" : "";
    971     string constructorsName = constructorMaxLen ? "Constructors" : "";
    972     string membersName = memberMaxLen ? "Member_Functions" : "";
    973     string operatorsName = operatorMaxLen ? "Operators" : "";
    974     size_t nameLen = SkTMax(classesName.size(), SkTMax(constsName.size(),
    975             SkTMax(constructorsName.size(), SkTMax(membersName.size(), operatorsName.size()))));
    976     this->writeTableHeader("name", nameLen, "description");
    977     string classDesc = classMaxLen ?  "embedded struct and class members" : "";
    978     string constDesc = constMaxLen ? "enum and enum class, const values" : "";
    979     string constructorDesc = constructorMaxLen ? "functions that construct " + className : "";
    980     string memberDesc = memberMaxLen ? "static functions and member methods" : "";
    981     string operatorDesc = operatorMaxLen ? "operator overloading methods" : "";
    982     size_t descLen = SkTMax(classDesc.size(), SkTMax(constDesc.size(), SkTMax(constructorDesc.size(),
    983             SkTMax(memberDesc.size(), operatorDesc.size()))));
    984     if (classMaxLen) {
    985         this->writeTableRow(nameLen, classesName, descLen, classDesc);
    986     }
    987     if (constMaxLen) {
    988         this->writeTableRow(nameLen, constsName, descLen, constDesc);
    989     }
    990     if (constructorMaxLen) {
    991         this->writeTableRow(nameLen, constructorsName, descLen, constructorDesc);
    992     }
    993     if (memberMaxLen) {
    994         this->writeTableRow(nameLen, membersName, descLen, memberDesc);
    995     }
    996     if (operatorMaxLen) {
    997         this->writeTableRow(nameLen, operatorsName, descLen, operatorDesc);
    998     }
    999     this->writeTableTrailer();
   1000     this->writeEndTag();
   1001     this->lf(2);
   1002     if (classMaxLen) {
   1003         std::sort(classNames.begin(), classNames.end());
   1004         this->writeTag("Subtopic", "Classes_and_Structs");
   1005         this->writeTableHeader("name", classMaxLen, "description");
   1006          for (auto& name : classNames) {
   1007              this->writeTableRow(classMaxLen, name);
   1008         }
   1009         this->writeTableTrailer();
   1010         this->writeEndTag("Subtopic");
   1011         this->lf(2);
   1012     }
   1013     if (constMaxLen) {
   1014         std::sort(constNames.begin(), constNames.end());
   1015         this->writeTag("Subtopic", "Constants");
   1016         this->writeTableHeader("name", constMaxLen, "description");
   1017         for (auto& name : constNames) {
   1018             this->writeTableRow(constMaxLen, name);
   1019         }
   1020         this->writeTableTrailer();
   1021         this->writeEndTag("Subtopic");
   1022         this->lf(2);
   1023     }
   1024     if (constructorMaxLen) {
   1025         std::sort(constructorNames.begin(), constructorNames.end());
   1026         this->writeTag("Subtopic", "Constructors");
   1027         this->writeTableHeader("name", constructorMaxLen, "description");
   1028         for (auto& name : constructorNames) {
   1029             this->writeTableRow(constructorMaxLen, name);
   1030         }
   1031         this->writeTableTrailer();
   1032         this->writeEndTag("Subtopic");
   1033         this->lf(2);
   1034     }
   1035     if (operatorMaxLen) {
   1036         std::sort(operatorNames.begin(), operatorNames.end());
   1037         this->writeTag("Subtopic", "Operators");
   1038         this->writeTableHeader("name", operatorMaxLen, "description");
   1039         for (auto& name : operatorNames) {
   1040             this->writeTableRow(operatorMaxLen, name);
   1041         }
   1042         this->writeTableTrailer();
   1043         this->writeEndTag("Subtopic");
   1044         this->lf(2);
   1045     }
   1046     if (memberMaxLen) {
   1047         std::sort(memberNames.begin(), memberNames.end());
   1048         this->writeTag("Subtopic", "Member_Functions");
   1049         this->writeTableHeader("name", memberMaxLen, "description");
   1050         for (auto& name : memberNames) {
   1051             size_t paren = name.find('(');
   1052             size_t funcLen = string::npos == paren ? name.length() : paren;
   1053             this->writeTableRow(memberMaxLen, name.substr(0, funcLen));
   1054         }
   1055         this->writeTableTrailer();
   1056         this->writeEndTag("Subtopic");
   1057         this->lf(2);
   1058     }
   1059     this->writeEndTag("Topic");
   1060     this->lf(2);
   1061     for (auto& oneEnum : fIEnumMap) {
   1062         this->writeString(
   1063             "# ------------------------------------------------------------------------------");
   1064         this->dumpEnum(oneEnum.second, oneEnum.first);
   1065         this->lf(2);
   1066         this->writeTag("Example");
   1067         this->lfcr();
   1068         this->writeString("// incomplete");
   1069         this->writeEndTag();
   1070         this->lf(2);
   1071         this->writeTag("SeeAlso", "incomplete");
   1072         this->lf(2);
   1073         this->writeEndTag("Enum", oneEnum.first);
   1074         this->lf(2);
   1075     }
   1076     for (auto& oneClass : fIClassMap) {
   1077         if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
   1078             continue;
   1079         }
   1080         string innerName = oneClass.first.substr(skClassName.length() + 2);
   1081         this->writeString(
   1082             "# ------------------------------------------------------------------------------");
   1083         this->lf(2);
   1084         KeyWord keyword = oneClass.second.fKeyWord;
   1085         SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
   1086         const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
   1087         this->writeTag(containerType, innerName);
   1088         this->lf(2);
   1089         this->writeTag("Code");
   1090         this->writeEndTag("ToDo", "fill this in manually");
   1091         this->writeEndTag();
   1092         this->lf(2);
   1093         for (auto& token : oneClass.second.fTokens) {
   1094             if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
   1095                 continue;
   1096             }
   1097             this->writeDefinition(token);
   1098         }
   1099         this->lf(2);
   1100         this->dumpClassTokens(oneClass.second);
   1101         this->lf(2);
   1102         this->writeEndTag(containerType, innerName);
   1103         this->lf(2);
   1104     }
   1105     this->dumpClassTokens(classMap);
   1106     this->writeEndTag(containerType, skClassName);
   1107     this->lf(2);
   1108     this->writeEndTag("Topic", topicName);
   1109     this->lfAlways(1);
   1110     fclose(fOut);
   1111     SkDebugf("wrote %s\n", fileName.c_str());
   1112     return true;
   1113 }
   1114 
   1115 bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
   1116     // add comment preceding class, if any
   1117     const Definition* parent = includeDef.fParent;
   1118     int index = includeDef.fParentIndex;
   1119     auto wordIter = parent->fTokens.begin();
   1120     std::advance(wordIter, index);
   1121     SkASSERT(&*wordIter == &includeDef);
   1122     while (parent->fTokens.begin() != wordIter) {
   1123         auto testIter = std::prev(wordIter);
   1124         if (Definition::Type::kWord != testIter->fType
   1125             && Definition::Type::kKeyWord != testIter->fType
   1126             && (Definition::Type::kBracket != testIter->fType
   1127             || Bracket::kAngle != testIter->fBracket)
   1128             && (Definition::Type::kPunctuation != testIter->fType
   1129             || Punctuation::kAsterisk != testIter->fPunctuation)) {
   1130             break;
   1131         }
   1132         wordIter = testIter;
   1133     }
   1134     auto commentIter = wordIter;
   1135     while (parent->fTokens.begin() != commentIter) {
   1136         auto testIter = std::prev(commentIter);
   1137         bool isComment = Definition::Type::kBracket == testIter->fType
   1138                 && (Bracket::kSlashSlash == testIter->fBracket
   1139                 || Bracket::kSlashStar == testIter->fBracket);
   1140         if (!isComment) {
   1141             break;
   1142         }
   1143         commentIter = testIter;
   1144     }
   1145     while (commentIter != wordIter) {
   1146         if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
   1147                 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
   1148             return false;
   1149         }
   1150         commentIter = std::next(commentIter);
   1151     }
   1152     return true;
   1153 }
   1154 
   1155 bool IncludeParser::internalName(const Definition& token) const {
   1156     return 0 == token.fName.find("internal_")
   1157             || 0 == token.fName.find("Internal_")
   1158             || 0 == token.fName.find("legacy_")
   1159             || 0 == token.fName.find("temporary_")
   1160             || 0 == token.fName.find("private_");
   1161 }
   1162 
   1163 // caller calls reportError, so just return false here
   1164 bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
   1165     SkASSERT(includeDef->fTokens.size() > 0);
   1166     // parse class header
   1167     auto iter = includeDef->fTokens.begin();
   1168     if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
   1169         // todo : documentation is ignoring this for now
   1170         iter = std::next(iter);
   1171     }
   1172     string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
   1173     includeDef->fName = nameStr;
   1174     iter = std::next(iter);
   1175     if (iter == includeDef->fTokens.end()) {
   1176         return true;  // forward declaration only
   1177     }
   1178     do {
   1179         if (iter == includeDef->fTokens.end()) {
   1180             return includeDef->reportError<bool>("unexpected end");
   1181         }
   1182         if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
   1183             break;
   1184         }
   1185     } while (static_cast<void>(iter = std::next(iter)), true);
   1186     if (Punctuation::kLeftBrace != iter->fPunctuation) {
   1187         return iter->reportError<bool>("expected left brace");
   1188     }
   1189     IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
   1190     if (!markupDef) {
   1191         return iter->reportError<bool>("expected markup definition");
   1192     }
   1193     markupDef->fStart = iter->fStart;
   1194     if (!this->findComments(*includeDef, markupDef)) {
   1195         return iter->reportError<bool>("find comments failed");
   1196     }
   1197 //    if (1 != includeDef->fChildren.size()) {
   1198 //        return false;  // fix me: SkCanvasClipVisitor isn't correctly parsed
   1199 //    }
   1200     includeDef = includeDef->fChildren.front();
   1201     iter = includeDef->fTokens.begin();
   1202     // skip until public
   1203     int publicIndex = 0;
   1204     if (IsStruct::kNo == isStruct) {
   1205         const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
   1206         size_t publicLen = strlen(publicName);
   1207         while (iter != includeDef->fTokens.end()
   1208                 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
   1209                 || strncmp(iter->fStart, publicName, publicLen))) {
   1210             iter = std::next(iter);
   1211             ++publicIndex;
   1212         }
   1213     }
   1214     auto childIter = includeDef->fChildren.begin();
   1215     while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
   1216         (*childIter)->fPrivate = true;
   1217         childIter = std::next(childIter);
   1218     }
   1219     int keyIndex = publicIndex;
   1220     KeyWord currentKey = KeyWord::kPublic;
   1221     const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
   1222     size_t publicLen = strlen(publicName);
   1223     const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
   1224     size_t protectedLen = strlen(protectedName);
   1225     const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
   1226     size_t privateLen = strlen(privateName);
   1227     while (childIter != includeDef->fChildren.end()) {
   1228         Definition* child = *childIter;
   1229         while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
   1230             const char* testStart = iter->fStart;
   1231             size_t testLen = (size_t) (iter->fContentEnd - testStart);
   1232             iter = std::next(iter);
   1233             ++keyIndex;
   1234             if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
   1235                 currentKey = KeyWord::kPublic;
   1236                 break;
   1237             }
   1238             if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
   1239                 currentKey = KeyWord::kProtected;
   1240                 break;
   1241             }
   1242             if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
   1243                 currentKey = KeyWord::kPrivate;
   1244                 break;
   1245             }
   1246         }
   1247         fLastObject = nullptr;
   1248         if (KeyWord::kPublic == currentKey) {
   1249             if (!this->parseObject(child, markupDef)) {
   1250                 return false;
   1251             }
   1252         } else {
   1253             child->fPrivate = true;
   1254         }
   1255         fLastObject = child;
   1256         childIter = std::next(childIter);
   1257     }
   1258     SkASSERT(fParent->fParent);
   1259     fParent = fParent->fParent;
   1260     return true;
   1261 }
   1262 
   1263 bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
   1264         int lineCount, Definition* markupDef) {
   1265     TextParser parser(filename, start, end, lineCount);
   1266     // parse doxygen if present
   1267     if (parser.startsWith("**")) {
   1268         parser.next();
   1269         parser.next();
   1270         parser.skipWhiteSpace();
   1271         if ('\\' == parser.peek()) {
   1272             parser.next();
   1273             if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
   1274                 return reportError<bool>("missing object type");
   1275             }
   1276             if (!parser.skipWord(markupDef->fName.c_str()) &&
   1277                     KeyWord::kEnum != markupDef->fKeyWord) {
   1278                 return reportError<bool>("missing object name");
   1279             }
   1280 
   1281         }
   1282     }
   1283     // remove leading '*' if present
   1284     Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
   1285     while (!parser.eof() && parser.skipWhiteSpace()) {
   1286         while ('*' == parser.peek()) {
   1287             parser.next();
   1288             if (parser.eof()) {
   1289                 break;
   1290             }
   1291             parser.skipWhiteSpace();
   1292         }
   1293         if (parser.eof()) {
   1294             break;
   1295         }
   1296         const char* lineEnd = parser.trimmedLineEnd();
   1297         markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
   1298                 parser.fLineCount, parent);
   1299         parser.skipToEndBracket('\n');
   1300     }
   1301     return true;
   1302 }
   1303 
   1304 bool IncludeParser::parseDefine() {
   1305 
   1306     return true;
   1307 }
   1308 
   1309 bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
   1310 	TextParser parser(child);
   1311 	parser.skipToEndBracket('{');
   1312 	if (parser.eof()) {
   1313 		return true;	// if enum is a forward declaration, do nothing
   1314 	}
   1315 	parser.next();
   1316 	string nameStr;
   1317     if (child->fTokens.size() > 0) {
   1318         auto token = child->fTokens.begin();
   1319         if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
   1320             token = token->fTokens.begin();
   1321         }
   1322         if (Definition::Type::kWord == token->fType) {
   1323             nameStr += string(token->fStart, token->fContentEnd - token->fStart);
   1324         }
   1325     }
   1326     Definition* markupChild;
   1327     if (!markupDef) {
   1328         auto finder = fIEnumMap.find(nameStr);
   1329         if (fIEnumMap.end() != finder) {
   1330             return child->reportError<bool>("duplicate global enum name");
   1331         }
   1332         markupChild = &fIEnumMap[nameStr];
   1333         markupChild->fContentStart = child->fContentStart;
   1334         markupChild->fName = nameStr;
   1335         markupChild->fFiddle = nameStr;
   1336         markupChild->fContentEnd = child->fContentEnd;
   1337         markupChild->fFileName = child->fFileName;
   1338         markupChild->fLineCount = child->fLineCount;
   1339     } else {
   1340         markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
   1341             child->fLineCount, markupDef);
   1342         markupChild = &markupDef->fTokens.back();
   1343     }
   1344     SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
   1345     markupChild->fKeyWord = KeyWord::kEnum;
   1346     TextParser enumName(child);
   1347     enumName.skipExact("enum ");
   1348     enumName.skipWhiteSpace();
   1349     if (enumName.skipExact("class ")) {
   1350         enumName.skipWhiteSpace();
   1351         markupChild->fMarkType = MarkType::kEnumClass;
   1352     }
   1353     const char* nameStart = enumName.fChar;
   1354     enumName.skipToSpace();
   1355     if (markupDef) {
   1356         markupChild->fName = markupDef->fName + "::";
   1357     }
   1358     markupChild->fName += string(nameStart, (size_t) (enumName.fChar - nameStart));
   1359     if (!this->findComments(*child, markupChild)) {
   1360         return false;
   1361     }
   1362     const char* dataEnd;
   1363     do {
   1364         parser.skipWhiteSpace();
   1365         if ('}' == parser.peek()) {
   1366             break;
   1367         }
   1368         Definition* comment = nullptr;
   1369         // note that comment, if any, can be before or after (on the same line, though) as member
   1370         if ('#' == parser.peek()) {
   1371             // fixme: handle preprecessor, but just skip it for now
   1372             parser.skipToLineStart();
   1373         }
   1374         while (parser.startsWith("/*") || parser.startsWith("//")) {
   1375             parser.next();
   1376             const char* start = parser.fChar;
   1377             const char* end;
   1378             if ('*' == parser.peek()) {
   1379                 end = parser.strnstr("*/", parser.fEnd);
   1380                 parser.fChar = end;
   1381                 parser.next();
   1382                 parser.next();
   1383             } else {
   1384                 end = parser.trimmedLineEnd();
   1385                 parser.skipToLineStart();
   1386             }
   1387             markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
   1388                     markupChild);
   1389             comment = &markupChild->fTokens.back();
   1390             comment->fTerminator = end;
   1391             if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
   1392                 return false;
   1393             }
   1394             parser.skipWhiteSpace();
   1395         }
   1396         parser.skipWhiteSpace();
   1397         const char* memberStart = parser.fChar;
   1398         if ('}' == memberStart[0]) {
   1399             break;
   1400         }
   1401         // if there's comment on same the line as member def, output first as if it was before
   1402 
   1403         parser.skipToNonAlphaNum();
   1404         string memberName(memberStart, parser.fChar);
   1405         if (parser.eof() || !parser.skipWhiteSpace()) {
   1406             return this->reportError<bool>("enum member must end with comma 1");
   1407         }
   1408         const char* dataStart = parser.fChar;
   1409         if ('=' == parser.peek()) {
   1410             parser.skipToEndBracket(',');
   1411         }
   1412         if (!parser.eof() && '#' == parser.peek()) {
   1413             // fixme: handle preprecessor, but just skip it for now
   1414             continue;
   1415         }
   1416         if (parser.eof() || ',' != parser.peek()) {
   1417             return this->reportError<bool>("enum member must end with comma 2");
   1418         }
   1419         dataEnd = parser.fChar;
   1420         const char* start = parser.anyOf("/\n");
   1421         SkASSERT(start);
   1422         parser.skipTo(start);
   1423         if ('/' == parser.next()) {
   1424             char slashStar = parser.next();
   1425             if ('/' == slashStar || '*' == slashStar) {
   1426                 TextParser::Save save(&parser);
   1427                 char doxCheck = parser.next();
   1428                 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
   1429                     save.restore();
   1430                 }
   1431             }
   1432             parser.skipWhiteSpace();
   1433             const char* commentStart = parser.fChar;
   1434             if ('/' == slashStar) {
   1435                 parser.skipToEndBracket('\n');
   1436             } else {
   1437                 parser.skipToEndBracket("*/");
   1438             }
   1439             SkASSERT(!parser.eof());
   1440             const char* commentEnd = parser.fChar;
   1441             markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
   1442                     parser.fLineCount, markupChild);
   1443             comment = &markupChild->fTokens.back();
   1444             comment->fTerminator = commentEnd;
   1445         }
   1446         markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
   1447                 markupChild);
   1448         Definition* member = &markupChild->fTokens.back();
   1449         member->fName = memberName;
   1450         if (comment) {
   1451             member->fChildren.push_back(comment);
   1452             comment->fPrivate = true;
   1453         }
   1454         markupChild->fChildren.push_back(member);
   1455     } while (true);
   1456     for (auto outsideMember : child->fChildren) {
   1457         if (Definition::Type::kBracket == outsideMember->fType) {
   1458             continue;
   1459         }
   1460         SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
   1461         if (KeyWord::kClass == outsideMember->fKeyWord) {
   1462             continue;
   1463         }
   1464         SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
   1465         markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
   1466                 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild);
   1467         Definition* member = &markupChild->fTokens.back();
   1468         member->fName = outsideMember->fName;
   1469         // FIXME: ? add comment as well ?
   1470         markupChild->fChildren.push_back(member);
   1471     }
   1472     if (markupDef) {
   1473         IClassDefinition& classDef = fIClassMap[markupDef->fName];
   1474         SkASSERT(classDef.fStart);
   1475         string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
   1476         markupChild->fName = uniqueName;
   1477         classDef.fEnums[uniqueName] = markupChild;
   1478     }
   1479     return true;
   1480 }
   1481 
   1482 bool IncludeParser::parseInclude(const string& name) {
   1483     fParent = &fIncludeMap[name];
   1484     fParent->fName = name;
   1485     fParent->fFileName = fFileName;
   1486     fParent->fType = Definition::Type::kFileType;
   1487     fParent->fContentStart = fChar;
   1488     fParent->fContentEnd = fEnd;
   1489     // parse include file into tree
   1490     while (fChar < fEnd) {
   1491         if (!this->parseChar()) {
   1492             return false;
   1493         }
   1494     }
   1495     // parse tree and add named objects to maps
   1496     fParent = &fIncludeMap[name];
   1497     if (!this->parseObjects(fParent, nullptr)) {
   1498         return false;
   1499     }
   1500     return true;
   1501 }
   1502 
   1503 bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
   1504     const char* typeStart = child->fChildren[0]->fContentStart;
   1505     markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
   1506         child->fLineCount, markupDef);
   1507     Definition* markupChild = &markupDef->fTokens.back();
   1508     TextParser nameParser(child);
   1509     nameParser.skipToNonAlphaNum();
   1510     string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
   1511     IClassDefinition& classDef = fIClassMap[markupDef->fName];
   1512     string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
   1513     markupChild->fName = uniqueName;
   1514     markupChild->fTerminator = markupChild->fContentEnd;
   1515     classDef.fMembers[uniqueName] = markupChild;
   1516     if (child->fParentIndex >= 2) {
   1517         auto comment = child->fParent->fTokens.begin();
   1518         std::advance(comment, child->fParentIndex - 2);
   1519         if (Definition::Type::kBracket == comment->fType
   1520                 && (Bracket::kSlashStar == comment->fBracket
   1521                 || Bracket::kSlashSlash == comment->fBracket)) {
   1522             TextParser parser(&*comment);
   1523             do {
   1524                 parser.skipToAlpha();
   1525                 if (parser.eof()) {
   1526                     break;
   1527                 }
   1528                 const char* start = parser.fChar;
   1529                 const char* end = parser.trimmedBracketEnd('\n');
   1530                 if (Bracket::kSlashStar == comment->fBracket) {
   1531                     const char* commentEnd = parser.strnstr("*/", end);
   1532                     if (commentEnd) {
   1533                         end = commentEnd;
   1534                     }
   1535                 }
   1536                 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
   1537                         markupDef);
   1538                 Definition* commentChild = &markupDef->fTokens.back();
   1539                 markupChild->fChildren.emplace_back(commentChild);
   1540                 parser.skipTo(end);
   1541             } while (!parser.eof());
   1542         }
   1543     }
   1544     return true;
   1545 }
   1546 
   1547 bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
   1548     auto tokenIter = child->fParent->fTokens.begin();
   1549     std::advance(tokenIter, child->fParentIndex);
   1550     tokenIter = std::prev(tokenIter);
   1551     const char* nameEnd = tokenIter->fContentEnd;
   1552     bool addConst = false;
   1553     auto operatorCheck = tokenIter;
   1554     if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
   1555         operatorCheck = std::prev(tokenIter);
   1556     }
   1557     if (KeyWord::kOperator == operatorCheck->fKeyWord) {
   1558         auto closeParen = std::next(tokenIter);
   1559         SkASSERT(Definition::Type::kBracket == closeParen->fType &&
   1560                 '(' == closeParen->fContentStart[0]);
   1561         nameEnd = closeParen->fContentEnd + 1;
   1562         closeParen = std::next(closeParen);
   1563         if (Definition::Type::kKeyWord == closeParen->fType &&
   1564                 KeyWord::kConst == closeParen->fKeyWord) {
   1565             addConst = true;
   1566         }
   1567         tokenIter = operatorCheck;
   1568     }
   1569     string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
   1570     if (addConst) {
   1571         nameStr += "_const";
   1572     }
   1573     while (tokenIter != child->fParent->fTokens.begin()) {
   1574         auto testIter = std::prev(tokenIter);
   1575         switch (testIter->fType) {
   1576             case Definition::Type::kWord:
   1577                 if (testIter == child->fParent->fTokens.begin() &&
   1578                         (KeyWord::kIfdef == child->fParent->fKeyWord ||
   1579                         KeyWord::kIfndef == child->fParent->fKeyWord ||
   1580                         KeyWord::kIf == child->fParent->fKeyWord)) {
   1581                     std::next(tokenIter);
   1582                     break;
   1583                 }
   1584                 goto keepGoing;
   1585             case Definition::Type::kKeyWord: {
   1586                 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
   1587                 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
   1588                     goto keepGoing;
   1589                 }
   1590             } break;
   1591             case Definition::Type::kBracket:
   1592                 if (Bracket::kAngle == testIter->fBracket) {
   1593                     goto keepGoing;
   1594                 }
   1595                 break;
   1596             case Definition::Type::kPunctuation:
   1597                 if (Punctuation::kSemicolon == testIter->fPunctuation
   1598                         || Punctuation::kLeftBrace == testIter->fPunctuation
   1599                         || Punctuation::kColon == testIter->fPunctuation) {
   1600                     break;
   1601                 }
   1602             keepGoing:
   1603                 tokenIter = testIter;
   1604                 continue;
   1605             default:
   1606                 break;
   1607         }
   1608         break;
   1609     }
   1610     tokenIter->fName = nameStr;
   1611     tokenIter->fMarkType = MarkType::kMethod;
   1612     tokenIter->fPrivate = string::npos != nameStr.find("::");
   1613     auto testIter = child->fParent->fTokens.begin();
   1614     SkASSERT(child->fParentIndex > 0);
   1615     std::advance(testIter, child->fParentIndex - 1);
   1616     if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
   1617             0 == tokenIter->fParentIndex) {
   1618         tokenIter = std::next(tokenIter);
   1619     }
   1620     const char* start = tokenIter->fContentStart;
   1621     const char* end = tokenIter->fContentEnd;
   1622     const char kDebugCodeStr[] = "SkDEBUGCODE";
   1623     const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
   1624     if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
   1625         std::advance(testIter, 1);
   1626         start = testIter->fContentStart + 1;
   1627         end = testIter->fContentEnd - 1;
   1628     } else {
   1629         end = testIter->fContentEnd;
   1630         while (testIter != child->fParent->fTokens.end()) {
   1631             testIter = std::next(testIter);
   1632             switch (testIter->fType) {
   1633                 case Definition::Type::kPunctuation:
   1634                     SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
   1635                             || Punctuation::kLeftBrace == testIter->fPunctuation
   1636                             || Punctuation::kColon == testIter->fPunctuation);
   1637                     end = testIter->fStart;
   1638                     break;
   1639                 case Definition::Type::kKeyWord: {
   1640                     KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
   1641                     if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
   1642                         continue;
   1643                     }
   1644                     } break;
   1645                 default:
   1646                     continue;
   1647             }
   1648             break;
   1649         }
   1650     }
   1651     while (end > start && ' ' >= end[-1]) {
   1652         --end;
   1653     }
   1654     if (!markupDef) {
   1655         auto parentIter = child->fParent->fTokens.begin();
   1656         SkASSERT(child->fParentIndex > 0);
   1657         std::advance(parentIter, child->fParentIndex - 1);
   1658         Definition* methodName = &*parentIter;
   1659         TextParser nameParser(methodName);
   1660         if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
   1661             return true;  // expect this is inline class definition outside of class
   1662         }
   1663         string name(nameParser.fLine, nameParser.lineLength());
   1664         auto finder = fIFunctionMap.find(name);
   1665         if (fIFunctionMap.end() != finder) {
   1666             // create unique name
   1667             SkASSERT(0);  // incomplete
   1668         }
   1669         auto globalFunction = &fIFunctionMap[name];
   1670         globalFunction->fContentStart = start;
   1671         globalFunction->fName = name;
   1672         globalFunction->fFiddle = name;
   1673         globalFunction->fContentEnd = end;
   1674         globalFunction->fMarkType = MarkType::kMethod;
   1675         globalFunction->fLineCount = tokenIter->fLineCount;
   1676         return true;
   1677     }
   1678     markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
   1679             markupDef);
   1680     Definition* markupChild = &markupDef->fTokens.back();
   1681     // do find instead -- I wonder if there is a way to prevent this in c++
   1682     IClassDefinition& classDef = fIClassMap[markupDef->fName];
   1683     SkASSERT(classDef.fStart);
   1684     string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
   1685     markupChild->fName = uniqueName;
   1686     if (!this->findComments(*child, markupChild)) {
   1687         return false;
   1688     }
   1689     classDef.fMethods[uniqueName] = markupChild;
   1690     return true;
   1691 }
   1692 
   1693 bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
   1694     for (auto& child : parent->fChildren) {
   1695         if (!this->parseObject(child, markupDef)) {
   1696             return false;
   1697         }
   1698     }
   1699     return true;
   1700 }
   1701 
   1702 bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
   1703     // set up for error reporting
   1704     fLine = fChar = child->fStart;
   1705     fEnd = child->fContentEnd;
   1706     // todo: put original line number in child as well
   1707     switch (child->fType) {
   1708         case Definition::Type::kKeyWord:
   1709             switch (child->fKeyWord) {
   1710                 case KeyWord::kClass:
   1711                     if (!this->parseClass(child, IsStruct::kNo)) {
   1712                         return false;
   1713                     }
   1714                     break;
   1715                 case KeyWord::kEnum:
   1716                     if (!this->parseEnum(child, markupDef)) {
   1717                         return child->reportError<bool>("failed to parse enum");
   1718                     }
   1719                     break;
   1720                 case KeyWord::kStruct:
   1721                     if (!this->parseClass(child, IsStruct::kYes)) {
   1722                         return child->reportError<bool>("failed to parse struct");
   1723                     }
   1724                     break;
   1725                 case KeyWord::kTemplate:
   1726                     if (!this->parseTemplate()) {
   1727                         return child->reportError<bool>("failed to parse template");
   1728                     }
   1729                     break;
   1730                 case KeyWord::kTypedef:
   1731                     if (!this->parseTypedef(child, markupDef)) {
   1732                         return child->reportError<bool>("failed to parse typedef");
   1733                     }
   1734                     break;
   1735                 case KeyWord::kUnion:
   1736                     if (!this->parseUnion()) {
   1737                         return child->reportError<bool>("failed to parse union");
   1738                     }
   1739                     break;
   1740                 default:
   1741                     return child->reportError<bool>("unhandled keyword");
   1742             }
   1743             break;
   1744         case Definition::Type::kBracket:
   1745             switch (child->fBracket) {
   1746                 case Bracket::kParen:
   1747                     if (fLastObject) {
   1748                         TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
   1749                                 child->fStart, fLastObject->fLineCount);
   1750                         if (!checkDeprecated.eof()) {
   1751                             checkDeprecated.skipWhiteSpace();
   1752                             if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
   1753                                 break;
   1754                             }
   1755                         }
   1756                     }
   1757                     {
   1758                         auto tokenIter = child->fParent->fTokens.begin();
   1759                         std::advance(tokenIter, child->fParentIndex);
   1760                         tokenIter = std::prev(tokenIter);
   1761                         TextParser previousToken(&*tokenIter);
   1762                         if (previousToken.startsWith("SK_ATTR_DEPRECATED")) {
   1763                             break;
   1764                         }
   1765                         if (Bracket::kPound == child->fParent->fBracket &&
   1766                                 KeyWord::kIf == child->fParent->fKeyWord) {
   1767                             // TODO: this will skip methods named defined() -- for the
   1768                             // moment there aren't any
   1769                             if (previousToken.startsWith("defined")) {
   1770                                 break;
   1771                             }
   1772                         }
   1773                     }
   1774                     if (!this->parseMethod(child, markupDef)) {
   1775                         return child->reportError<bool>("failed to parse method");
   1776                     }
   1777                 break;
   1778                 case Bracket::kSlashSlash:
   1779                 case Bracket::kSlashStar:
   1780                     // comments are picked up by parsing objects first
   1781                     break;
   1782                 case Bracket::kPound:
   1783                     // special-case the #xxx xxx_DEFINED entries
   1784                     switch (child->fKeyWord) {
   1785                         case KeyWord::kIf:
   1786                         case KeyWord::kIfndef:
   1787                         case KeyWord::kIfdef:
   1788                             if (child->boilerplateIfDef(fParent)) {
   1789                                 if (!this->parseObjects(child, markupDef)) {
   1790                                     return false;
   1791                                 }
   1792                                 break;
   1793                             }
   1794                             goto preproError;
   1795                         case KeyWord::kDefine:
   1796                             if (child->boilerplateDef(fParent)) {
   1797                                 break;
   1798                             }
   1799                             goto preproError;
   1800                         case KeyWord::kEndif:
   1801                             if (child->boilerplateEndIf()) {
   1802                                 break;
   1803                             }
   1804                         case KeyWord::kError:
   1805                         case KeyWord::kInclude:
   1806                             // ignored for now
   1807                             break;
   1808                         case KeyWord::kElse:
   1809                         case KeyWord::kElif:
   1810                             // todo: handle these
   1811                             break;
   1812                         default:
   1813                         preproError:
   1814                             return child->reportError<bool>("unhandled preprocessor");
   1815                     }
   1816                     break;
   1817                 case Bracket::kAngle:
   1818                     // pick up templated function pieces when method is found
   1819                     break;
   1820                 case Bracket::kDebugCode:
   1821                     if (!this->parseObjects(child, markupDef)) {
   1822                         return false;
   1823                     }
   1824                     break;
   1825                 case Bracket::kSquare: {
   1826                     // check to see if parent is operator, the only case we handle so far
   1827                     auto prev = child->fParent->fTokens.begin();
   1828                     std::advance(prev, child->fParentIndex - 1);
   1829                     if (KeyWord::kOperator != prev->fKeyWord) {
   1830                         return child->reportError<bool>("expected operator overload");
   1831                     }
   1832                     } break;
   1833                 default:
   1834                     return child->reportError<bool>("unhandled bracket");
   1835             }
   1836             break;
   1837         case Definition::Type::kWord:
   1838             if (MarkType::kMember != child->fMarkType) {
   1839                 return child->reportError<bool>("unhandled word type");
   1840             }
   1841             if (!this->parseMember(child, markupDef)) {
   1842                 return child->reportError<bool>("unparsable member");
   1843             }
   1844             break;
   1845         default:
   1846             return child->reportError<bool>("unhandled type");
   1847             break;
   1848     }
   1849     return true;
   1850 }
   1851 
   1852 bool IncludeParser::parseTemplate() {
   1853 
   1854     return true;
   1855 }
   1856 
   1857 bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
   1858     TextParser typedefParser(child);
   1859     typedefParser.skipExact("typedef");
   1860     typedefParser.skipWhiteSpace();
   1861     string nameStr = typedefParser.typedefName();
   1862     if (!markupDef) {
   1863         Definition& typedefDef = fITypedefMap[nameStr];
   1864         SkASSERT(!typedefDef.fStart);
   1865         typedefDef.fStart = child->fContentStart;
   1866         typedefDef.fContentStart = child->fContentStart;
   1867         typedefDef.fName = nameStr;
   1868         typedefDef.fFiddle = nameStr;
   1869         typedefDef.fContentEnd = child->fContentEnd;
   1870         typedefDef.fTerminator = child->fContentEnd;
   1871         typedefDef.fMarkType = MarkType::kTypedef;
   1872         typedefDef.fLineCount = child->fLineCount;
   1873         return true;
   1874     }
   1875     markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
   1876         child->fLineCount, markupDef);
   1877     Definition* markupChild = &markupDef->fTokens.back();
   1878     markupChild->fName = nameStr;
   1879     markupChild->fTerminator = markupChild->fContentEnd;
   1880     IClassDefinition& classDef = fIClassMap[markupDef->fName];
   1881     classDef.fTypedefs[nameStr] = markupChild;
   1882     return true;
   1883 }
   1884 
   1885 bool IncludeParser::parseUnion() {
   1886 
   1887     return true;
   1888 }
   1889 
   1890 bool IncludeParser::parseChar() {
   1891     char test = *fChar;
   1892     if ('\\' == fPrev) {
   1893         if ('\n' == test) {
   1894 //            ++fLineCount;
   1895             fLine = fChar + 1;
   1896         }
   1897         goto done;
   1898     }
   1899     switch (test) {
   1900         case '\n':
   1901 //            ++fLineCount;
   1902             fLine = fChar + 1;
   1903             if (fInChar) {
   1904                 return reportError<bool>("malformed char");
   1905             }
   1906             if (fInString) {
   1907                 return reportError<bool>("malformed string");
   1908             }
   1909             if (!this->checkForWord()) {
   1910                 return false;
   1911             }
   1912             if (Bracket::kPound == this->topBracket()) {
   1913                 KeyWord keyWord = fParent->fKeyWord;
   1914                 if (KeyWord::kNone == keyWord) {
   1915                     return this->reportError<bool>("unhandled preprocessor directive");
   1916                 }
   1917                 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
   1918                     this->popBracket();
   1919                 }
   1920             } else if (Bracket::kSlashSlash == this->topBracket()) {
   1921                 this->popBracket();
   1922             }
   1923             break;
   1924         case '*':
   1925             if (!fInCharCommentString && '/' == fPrev) {
   1926                 this->pushBracket(Bracket::kSlashStar);
   1927             }
   1928             if (!this->checkForWord()) {
   1929                 return false;
   1930             }
   1931             if (!fInCharCommentString) {
   1932                 this->addPunctuation(Punctuation::kAsterisk);
   1933             }
   1934             break;
   1935         case '/':
   1936             if ('*' == fPrev) {
   1937                 if (!fInCharCommentString) {
   1938                     return reportError<bool>("malformed closing comment");
   1939                 }
   1940                 if (Bracket::kSlashStar == this->topBracket()) {
   1941                     TextParser::Save save(this);
   1942                     this->next();  // include close in bracket
   1943                     this->popBracket();
   1944                     save.restore(); // put things back so nothing is skipped
   1945                 }
   1946                 break;
   1947             }
   1948             if (!fInCharCommentString && '/' == fPrev) {
   1949                 this->pushBracket(Bracket::kSlashSlash);
   1950                 break;
   1951             }
   1952             if (!this->checkForWord()) {
   1953                 return false;
   1954             }
   1955             break;
   1956         case '\'':
   1957             if (Bracket::kChar == this->topBracket()) {
   1958                 this->popBracket();
   1959             } else if (!fInComment && !fInString) {
   1960                 if (fIncludeWord) {
   1961                     return this->reportError<bool>("word then single-quote");
   1962                 }
   1963                 this->pushBracket(Bracket::kChar);
   1964             }
   1965             break;
   1966         case '\"':
   1967             if (Bracket::kString == this->topBracket()) {
   1968                 this->popBracket();
   1969             } else if (!fInComment && !fInChar) {
   1970                 if (fIncludeWord) {
   1971                     return this->reportError<bool>("word then double-quote");
   1972                 }
   1973                 this->pushBracket(Bracket::kString);
   1974             }
   1975             break;
   1976         case ':':
   1977         case '(':
   1978         case '[':
   1979         case '{': {
   1980             if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
   1981                     !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
   1982                 this->pushBracket(Bracket::kDebugCode);
   1983                 break;
   1984             }
   1985             if (fInCharCommentString) {
   1986                 break;
   1987             }
   1988             if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
   1989                 break;
   1990             }
   1991             if (!fInBrace) {
   1992                 if (!this->checkForWord()) {
   1993                     return false;
   1994                 }
   1995                 if (':' == test && !fInFunction) {
   1996                     break;
   1997                 }
   1998                 if ('{' == test) {
   1999                     this->addPunctuation(Punctuation::kLeftBrace);
   2000                 } else if (':' == test) {
   2001                     this->addPunctuation(Punctuation::kColon);
   2002                 }
   2003             }
   2004             if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
   2005                     && Bracket::kColon == fInBrace->fBracket) {
   2006                 Definition* braceParent = fParent->fParent;
   2007                 braceParent->fChildren.pop_back();
   2008                 braceParent->fTokens.pop_back();
   2009                 fParent = braceParent;
   2010                 fInBrace = nullptr;
   2011             }
   2012             this->pushBracket(
   2013                     '(' == test ? Bracket::kParen :
   2014                     '[' == test ? Bracket::kSquare :
   2015                     '{' == test ? Bracket::kBrace :
   2016                                   Bracket::kColon);
   2017             if (!fInBrace
   2018                     && ('{' == test || (':' == test && ' ' >= fChar[1]))
   2019                     && fInFunction) {
   2020                 fInBrace = fParent;
   2021             }
   2022             } break;
   2023         case '<':
   2024             if (fInCharCommentString || fInBrace) {
   2025                 break;
   2026             }
   2027             if (!this->checkForWord()) {
   2028                 return false;
   2029             }
   2030             if (fInEnum) {
   2031                 break;
   2032             }
   2033             this->pushBracket(Bracket::kAngle);
   2034             break;
   2035         case ')':
   2036         case ']':
   2037         case '}': {
   2038             if (fInCharCommentString) {
   2039                 break;
   2040             }
   2041             if (!fInBrace) {
   2042                 if (!this->checkForWord()) {
   2043                     return false;
   2044                 }
   2045             }
   2046             bool popBraceParent = fInBrace == fParent;
   2047             if ((')' == test ? Bracket::kParen :
   2048                     ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
   2049                 this->popBracket();
   2050                 if (!fInFunction) {
   2051                     bool deprecatedMacro = false;
   2052                     if (')' == test) {
   2053                         auto iter = fParent->fTokens.end();
   2054                         bool lookForWord = false;
   2055                         while (fParent->fTokens.begin() != iter) {
   2056                             --iter;
   2057                             if (lookForWord) {
   2058                                 if (Definition::Type::kWord != iter->fType) {
   2059                                     break;
   2060                                 }
   2061                                 string word(iter->fContentStart, iter->length());
   2062                                 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
   2063                                     deprecatedMacro = true;
   2064                                     // remove macro paren (would confuse method parsing later)
   2065                                     fParent->fTokens.pop_back();
   2066                                     fParent->fChildren.pop_back();
   2067                                 }
   2068                                 break;
   2069                             }
   2070                             if (Definition::Type::kBracket != iter->fType) {
   2071                                 break;
   2072                             }
   2073                             if (Bracket::kParen != iter->fBracket) {
   2074                                 break;
   2075                             }
   2076                             lookForWord = true;
   2077                         }
   2078                     }
   2079                     fInFunction = ')' == test && !deprecatedMacro;
   2080                 } else {
   2081                     fInFunction = '}' != test;
   2082                 }
   2083             } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
   2084                 this->popBracket();
   2085             } else {
   2086                 return reportError<bool>("malformed close bracket");
   2087             }
   2088             if (popBraceParent) {
   2089                 Definition* braceParent = fInBrace->fParent;
   2090                 braceParent->fChildren.pop_back();
   2091                 braceParent->fTokens.pop_back();
   2092                 fInBrace = nullptr;
   2093             }
   2094             } break;
   2095         case '>':
   2096             if (fInCharCommentString || fInBrace) {
   2097                 break;
   2098             }
   2099             if (!this->checkForWord()) {
   2100                 return false;
   2101             }
   2102             if (fInEnum) {
   2103                 break;
   2104             }
   2105             if (Bracket::kPound == this->topBracket()) {
   2106                 break;
   2107             }
   2108             if (Bracket::kAngle == this->topBracket()) {
   2109                 this->popBracket();
   2110             } else {
   2111                 return reportError<bool>("malformed close angle bracket");
   2112             }
   2113             break;
   2114         case '#': {
   2115             if (fInCharCommentString || fInBrace) {
   2116                 break;
   2117             }
   2118             SkASSERT(!fIncludeWord);  // don't expect this, curious if it is triggered
   2119             this->pushBracket(Bracket::kPound);
   2120             break;
   2121         }
   2122         case '&':
   2123         case ',':
   2124         case ' ':
   2125         case '+':
   2126         case '=':
   2127         case '-':
   2128         case '!':
   2129             if (fInCharCommentString || fInBrace) {
   2130                 break;
   2131             }
   2132             if (!this->checkForWord()) {
   2133                 return false;
   2134             }
   2135             break;
   2136         case ';':
   2137             if (fInCharCommentString || fInBrace) {
   2138                 break;
   2139             }
   2140             if (!this->checkForWord()) {
   2141                 return false;
   2142             }
   2143             if (Definition::Type::kKeyWord == fParent->fType
   2144                     && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
   2145                 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
   2146                         KeyWord::kEnum == fParent->fParent->fKeyWord) {
   2147                     this->popObject();
   2148                 }
   2149                 if (KeyWord::kEnum == fParent->fKeyWord) {
   2150                     fInEnum = false;
   2151                 }
   2152                 this->popObject();
   2153                 fPriorEnum = nullptr;
   2154             } else if (Definition::Type::kBracket == fParent->fType
   2155                     && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
   2156                     && KeyWord::kStruct == fParent->fParent->fKeyWord) {
   2157                 list<Definition>::iterator baseIter = fParent->fTokens.end();
   2158                 list<Definition>::iterator namedIter  = fParent->fTokens.end();
   2159                 for (auto tokenIter = fParent->fTokens.end();
   2160                         fParent->fTokens.begin() != tokenIter--; ) {
   2161                     if (tokenIter->fLineCount == fLineCount) {
   2162                         if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
   2163                             if (namedIter != fParent->fTokens.end()) {
   2164                                 return reportError<bool>("found two named member tokens");
   2165                             }
   2166                             namedIter = tokenIter;
   2167                         }
   2168                         baseIter = tokenIter;
   2169                     } else {
   2170                         break;
   2171                     }
   2172                 }
   2173                 // FIXME: if a member definition spans multiple lines, this won't work
   2174                 if (namedIter != fParent->fTokens.end()) {
   2175                     if (baseIter == namedIter) {
   2176                         return this->reportError<bool>("expected type before named token");
   2177                     }
   2178                     Definition* member = &*namedIter;
   2179                     member->fMarkType = MarkType::kMember;
   2180                     if (!member->fTerminator) {
   2181                         member->fTerminator = member->fContentEnd;
   2182                     }
   2183                     fParent->fChildren.push_back(member);
   2184                     for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
   2185                         member->fChildren.push_back(&*nameType);
   2186                     }
   2187                 }
   2188                 fPriorEnum = nullptr;
   2189             } else if (fParent->fChildren.size() > 0) {
   2190                 auto lastIter = fParent->fChildren.end();
   2191                 Definition* priorEnum = fPriorEnum;
   2192                 fPriorEnum = nullptr;
   2193                 if (!priorEnum) {
   2194                     while (fParent->fChildren.begin() != lastIter) {
   2195                         std::advance(lastIter, -1);
   2196                         priorEnum = *lastIter;
   2197                         if (Definition::Type::kBracket != priorEnum->fType ||
   2198                                 (Bracket::kSlashSlash != priorEnum->fBracket
   2199                                 && Bracket::kSlashStar != priorEnum->fBracket)) {
   2200                             break;
   2201                         }
   2202                     }
   2203                     fPriorIndex = priorEnum->fParentIndex;
   2204                 }
   2205                 if (Definition::Type::kKeyWord == priorEnum->fType
   2206                         && KeyWord::kEnum == priorEnum->fKeyWord) {
   2207                     auto tokenWalker = fParent->fTokens.begin();
   2208                     std::advance(tokenWalker, fPriorIndex);
   2209                     while (tokenWalker != fParent->fTokens.end()) {
   2210                         std::advance(tokenWalker, 1);
   2211                         ++fPriorIndex;
   2212                         if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
   2213                             break;
   2214                         }
   2215                     }
   2216                     while (tokenWalker != fParent->fTokens.end()) {
   2217                         std::advance(tokenWalker, 1);
   2218                         const Definition* test = &*tokenWalker;
   2219                         if (Definition::Type::kBracket != test->fType ||
   2220                                 (Bracket::kSlashSlash != test->fBracket
   2221                                 && Bracket::kSlashStar != test->fBracket)) {
   2222                             break;
   2223                         }
   2224                     }
   2225                     auto saveTokenWalker = tokenWalker;
   2226                     Definition* start = &*tokenWalker;
   2227                     bool foundExpected = true;
   2228                     for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
   2229                         const Definition* test = &*tokenWalker;
   2230                         if (expected != test->fKeyWord) {
   2231                             foundExpected = false;
   2232                             break;
   2233                         }
   2234                         if (tokenWalker == fParent->fTokens.end()) {
   2235                             break;
   2236                         }
   2237                         std::advance(tokenWalker, 1);
   2238                     }
   2239                     if (!foundExpected) {
   2240                         foundExpected = true;
   2241                         tokenWalker = saveTokenWalker;
   2242                         for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
   2243                             const Definition* test = &*tokenWalker;
   2244                             if (expected != test->fKeyWord) {
   2245                                 foundExpected = false;
   2246                                 break;
   2247                             }
   2248                             if (tokenWalker == fParent->fTokens.end()) {
   2249                                 break;
   2250                             }
   2251                             if (KeyWord::kNone != expected) {
   2252                                 std::advance(tokenWalker, 1);
   2253                             }
   2254                         }
   2255                         if (foundExpected) {
   2256                             auto nameToken = priorEnum->fTokens.begin();
   2257                             string enumName = string(nameToken->fContentStart,
   2258                                     nameToken->fContentEnd - nameToken->fContentStart);
   2259                             const Definition* test = &*tokenWalker;
   2260                             string constType = string(test->fContentStart,
   2261                                     test->fContentEnd - test->fContentStart);
   2262                             if (enumName != constType) {
   2263                                 foundExpected = false;
   2264                             } else {
   2265                                 std::advance(tokenWalker, 1);
   2266                             }
   2267                         }
   2268                     }
   2269                     if (foundExpected && tokenWalker != fParent->fTokens.end()) {
   2270                         const char* nameStart = tokenWalker->fStart;
   2271                         std::advance(tokenWalker, 1);
   2272                         if (tokenWalker != fParent->fTokens.end()) {
   2273                             TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
   2274                             tp.skipToNonAlphaNum();
   2275                             start->fName = string(nameStart, tp.fChar - nameStart);
   2276                             start->fContentEnd = fChar;
   2277                             priorEnum->fChildren.emplace_back(start);
   2278                             fPriorEnum = priorEnum;
   2279                         }
   2280                     }
   2281                 }
   2282             }
   2283             this->addPunctuation(Punctuation::kSemicolon);
   2284             fInFunction = false;
   2285             break;
   2286         case '~':
   2287             if (fInEnum) {
   2288                 break;
   2289             }
   2290         case '0': case '1': case '2': case '3': case '4':
   2291         case '5': case '6': case '7': case '8': case '9':
   2292             // TODO: don't want to parse numbers, but do need to track for enum defs
   2293         //    break;
   2294         case 'A': case 'B': case 'C': case 'D': case 'E':
   2295         case 'F': case 'G': case 'H': case 'I': case 'J':
   2296         case 'K': case 'L': case 'M': case 'N': case 'O':
   2297         case 'P': case 'Q': case 'R': case 'S': case 'T':
   2298         case 'U': case 'V': case 'W': case 'X': case 'Y':
   2299         case 'Z': case '_':
   2300         case 'a': case 'b': case 'c': case 'd': case 'e':
   2301         case 'f': case 'g': case 'h': case 'i': case 'j':
   2302         case 'k': case 'l': case 'm': case 'n': case 'o':
   2303         case 'p': case 'q': case 'r': case 's': case 't':
   2304         case 'u': case 'v': case 'w': case 'x': case 'y':
   2305         case 'z':
   2306             if (fInCharCommentString || fInBrace) {
   2307                 break;
   2308             }
   2309             if (!fIncludeWord) {
   2310                 fIncludeWord = fChar;
   2311             }
   2312             break;
   2313     }
   2314 done:
   2315     fPrev = test;
   2316     this->next();
   2317     return true;
   2318 }
   2319 
   2320 void IncludeParser::validate() const {
   2321     for (int index = 0; index <= (int) Last_MarkType; ++index) {
   2322         SkASSERT(fMaps[index].fMarkType == (MarkType) index);
   2323     }
   2324     IncludeParser::ValidateKeyWords();
   2325 }
   2326 
   2327 void IncludeParser::RemoveFile(const char* docs, const char* includes) {
   2328     if (!sk_isdir(includes)) {
   2329         IncludeParser::RemoveOneFile(docs, includes);
   2330     } else {
   2331         SkOSFile::Iter it(includes, ".h");
   2332         for (SkString file; it.next(&file); ) {
   2333             SkString p = SkOSPath::Join(includes, file.c_str());
   2334             const char* hunk = p.c_str();
   2335             if (!SkStrEndsWith(hunk, ".h")) {
   2336                 continue;
   2337             }
   2338             IncludeParser::RemoveOneFile(docs, hunk);
   2339         }
   2340     }
   2341 }
   2342 
   2343 void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
   2344     const char* lastForward = strrchr(includesFile, '/');
   2345     const char* lastBackward = strrchr(includesFile, '\\');
   2346     const char* last = lastForward > lastBackward ? lastForward : lastBackward;
   2347     if (!last) {
   2348         last = includesFile;
   2349     } else {
   2350         last += 1;
   2351     }
   2352     SkString baseName(last);
   2353     SkASSERT(baseName.endsWith(".h"));
   2354     baseName.remove(baseName.size() - 2, 2);
   2355     baseName.append("_Reference.bmh");
   2356     SkString fullName = SkOSPath::Join(docs, baseName.c_str());
   2357     remove(fullName.c_str());
   2358 }
   2359