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