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