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