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