blob: 6aed93737fb1fb4f4288077cd1baa127ca7f5630 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
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
10enum class KeyProperty {
11 kNone,
12 kClassSection,
13 kFunction,
14 kModifier,
15 kNumber,
16 kObject,
17 kPreprocessor,
18};
19
20struct IncludeKey {
21 const char* fName;
22 KeyWord fKeyWord;
23 KeyProperty fProperty;
24};
25
26const IncludeKey kKeyWords[] = {
27 { "", KeyWord::kNone, KeyProperty::kNone },
28 { "bool", KeyWord::kBool, KeyProperty::kNumber },
29 { "char", KeyWord::kChar, KeyProperty::kNumber },
30 { "class", KeyWord::kClass, KeyProperty::kObject },
31 { "const", KeyWord::kConst, KeyProperty::kModifier },
32 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
33 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
34 { "double", KeyWord::kDouble, KeyProperty::kNumber },
35 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
36 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
37 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
38 { "enum", KeyWord::kEnum, KeyProperty::kObject },
39 { "float", KeyWord::kFloat, KeyProperty::kNumber },
40 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
41 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
42 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
43 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
44 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
45 { "inline", KeyWord::kInline, KeyProperty::kModifier },
46 { "int", KeyWord::kInt, KeyProperty::kNumber },
47 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
48 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
49 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
50 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
51 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
52 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
53 { "static", KeyWord::kStatic, KeyProperty::kModifier },
54 { "struct", KeyWord::kStruct, KeyProperty::kObject },
55 { "template", KeyWord::kTemplate, KeyProperty::kObject },
56 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
57 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
58 { "union", KeyWord::kUnion, KeyProperty::kObject },
59 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
60 { "void", KeyWord::kVoid, KeyProperty::kNumber },
61};
62
63const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
64
65KeyWord IncludeParser::FindKey(const char* start, const char* end) {
66 int ch = 0;
67 for (size_t index = 0; index < kKeyWordCount; ) {
68 if (start[ch] > kKeyWords[index].fName[ch]) {
69 ++index;
70 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
71 return KeyWord::kNone;
72 }
73 continue;
74 }
75 if (start[ch] < kKeyWords[index].fName[ch]) {
76 return KeyWord::kNone;
77 }
78 ++ch;
79 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040080 if (end - start < (int) strlen(kKeyWords[index].fName)) {
81 return KeyWord::kNone;
82 }
Cary Clark8032b982017-07-28 11:04:54 -040083 return kKeyWords[index].fKeyWord;
84 }
85 }
86 return KeyWord::kNone;
87}
88
89void IncludeParser::ValidateKeyWords() {
90 for (size_t index = 1; index < kKeyWordCount; ++index) {
91 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
92 == (int) kKeyWords[index].fKeyWord);
93 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
94 }
95}
96
97void IncludeParser::addKeyword(KeyWord keyWord) {
98 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent);
99 fIncludeWord = nullptr;
100 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
101 Definition* def = &fParent->fTokens.back();
102 this->addDefinition(def);
103 if (KeyWord::kEnum == fParent->fKeyWord) {
104 fInEnum = true;
105 }
106 }
107}
108
109void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
110 const vector<string>& foundParams) {
111 for (auto& methodParam : methodParams) {
112 bool found = false;
113 for (auto& foundParam : foundParams) {
114 if (methodParam == foundParam) {
115 found = true;
116 break;
117 }
118 }
119 if (!found) {
120 this->keywordStart("Param");
121 fprintf(fOut, "%s ", methodParam.c_str());
122 this->keywordEnd();
123 }
124 }
125 for (auto& foundParam : foundParams) {
126 bool found = false;
127 for (auto& methodParam : methodParams) {
128 if (methodParam == foundParam) {
129 found = true;
130 break;
131 }
132 }
133 if (!found) {
134 this->reportError("doxygen param does not match method declaration");
135 }
136 }
137}
138
139bool IncludeParser::checkForWord() {
140 if (!fIncludeWord) {
141 return true;
142 }
143 KeyWord keyWord = FindKey(fIncludeWord, fChar);
144 if (KeyWord::kNone != keyWord) {
145 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
146 this->addKeyword(keyWord);
147 return true;
148 }
149 } else {
150 this->addWord();
151 return true;
152 }
153 Definition* poundDef = fParent;
154 if (!fParent) {
155 return reportError<bool>("expected parent");
156 }
157 if (Definition::Type::kBracket != poundDef->fType) {
158 return reportError<bool>("expected bracket");
159 }
160 if (Bracket::kPound != poundDef->fBracket) {
161 return reportError<bool>("expected preprocessor");
162 }
163 if (KeyWord::kNone != poundDef->fKeyWord) {
164 return reportError<bool>("already found keyword");
165 }
166 poundDef->fKeyWord = keyWord;
167 fIncludeWord = nullptr;
168 switch (keyWord) {
169 // these do not link to other # directives
170 case KeyWord::kDefine:
171 case KeyWord::kInclude:
172 break;
173 // these start a # directive link
174 case KeyWord::kIf:
175 case KeyWord::kIfdef:
176 case KeyWord::kIfndef:
177 break;
178 // these continue a # directive link
179 case KeyWord::kElif:
180 case KeyWord::kElse: {
181 this->popObject(); // pop elif
182 if (Bracket::kPound != fParent->fBracket) {
183 return this->reportError<bool>("expected preprocessor directive");
184 }
185 this->popBracket(); // pop if
186 poundDef->fParent = fParent;
187 this->addDefinition(poundDef); // push elif back
188 } break;
189 // this ends a # directive link
190 case KeyWord::kEndif:
191 // FIXME : should this be calling popBracket() instead?
192 this->popObject(); // pop endif
193 if (Bracket::kPound != fParent->fBracket) {
194 return this->reportError<bool>("expected preprocessor directive");
195 }
196 this->popBracket(); // pop if/else
197 break;
198 default:
199 SkASSERT(0);
200 }
201 return true;
202}
203
204string IncludeParser::className() const {
205 string name(fParent->fName);
206 size_t slash = name.find_last_of("/");
207 if (string::npos == slash) {
208 slash = name.find_last_of("\\");
209 }
210 SkASSERT(string::npos != slash);
211 string result = name.substr(slash);
212 result = result.substr(1, result.size() - 3);
213 return result;
214}
215
216bool IncludeParser::crossCheck(BmhParser& bmhParser) {
217 string className = this->className();
218 string classPrefix = className + "::";
219 RootDefinition* root = &bmhParser.fClassMap[className];
220 root->clearVisited();
221 for (auto& classMapper : fIClassMap) {
222 if (className != classMapper.first
223 && classPrefix != classMapper.first.substr(0, classPrefix.length())) {
224 continue;
225 }
226 auto& classMap = classMapper.second;
227 auto& tokens = classMap.fTokens;
228 for (const auto& token : tokens) {
229 if (token.fPrivate) {
230 continue;
231 }
232 string fullName = classMapper.first + "::" + token.fName;
233 const Definition* def = root->find(fullName);
234 switch (token.fMarkType) {
235 case MarkType::kMethod: {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400236 if (this->internalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400237 continue;
238 }
239 const char* methodID = bmhParser.fMaps[(int) token.fMarkType].fName;
240 if (!def) {
241 string paramName = className + "::";
242 paramName += string(token.fContentStart,
243 token.fContentEnd - token.fContentStart);
244 def = root->find(paramName);
245 if (!def && 0 == token.fName.find("operator")) {
246 string operatorName = className + "::";
247 TextParser oper("", token.fStart, token.fContentEnd, 0);
248 const char* start = oper.strnstr("operator", token.fContentEnd);
249 SkASSERT(start);
250 oper.skipTo(start);
251 oper.skipToEndBracket('(');
252 int parens = 0;
253 do {
254 if ('(' == oper.peek()) {
255 ++parens;
256 } else if (')' == oper.peek()) {
257 --parens;
258 }
259 } while (!oper.eof() && oper.next() && parens > 0);
260 operatorName += string(start, oper.fChar - start);
261 def = root->find(operatorName);
262 }
263 }
264 if (!def) {
265 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
266 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
267 string constructorName = className + "::";
268 constructorName += string(token.fContentStart + skip,
269 token.fContentEnd - token.fContentStart - skip);
270 def = root->find(constructorName);
271 }
272 if (!def && 0 == token.fName.find("SK_")) {
273 string incName = token.fName + "()";
274 string macroName = className + "::" + incName;
275 def = root->find(macroName);
276 if (def) {
277 if (def->fName == incName) {
278 def->fVisited = true;
279 if ("SK_TO_STRING_NONVIRT" == token.fName) {
280 def = root->find(className + "::toString");
281 if (def) {
282 def->fVisited = true;
283 } else {
284 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
285 }
286 }
287 break;
288 } else {
289 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
290 }
291 }
292 }
293 if (!def) {
294 bool allLower = true;
295 for (size_t index = 0; index < token.fName.length(); ++index) {
296 if (!islower(token.fName[index])) {
297 allLower = false;
298 break;
299 }
300 }
301 if (allLower) {
302 string lowerName = className + "::" + token.fName + "()";
303 def = root->find(lowerName);
304 }
305 }
306 if (!def) {
307 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
308 break;
309 }
310 if (def->crossCheck(methodID, token)) {
311 def->fVisited = true;
312 } else {
313 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
314 }
315 } break;
316 case MarkType::kComment:
317 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400318 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400319 case MarkType::kEnum: {
320 if (!def) {
321 // work backwards from first word to deduce #Enum name
322 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
323 SkAssertResult(firstMember.skipName("enum"));
324 SkAssertResult(firstMember.skipToEndBracket('{'));
325 firstMember.next();
326 firstMember.skipWhiteSpace();
327 SkASSERT('k' == firstMember.peek());
328 const char* savePos = firstMember.fChar;
329 firstMember.skipToNonAlphaNum();
330 const char* wordEnd = firstMember.fChar;
331 firstMember.fChar = savePos;
332 const char* lastUnderscore = nullptr;
333 do {
334 if (!firstMember.skipToEndBracket('_')) {
335 break;
336 }
337 if (firstMember.fChar > wordEnd) {
338 break;
339 }
340 lastUnderscore = firstMember.fChar;
341 } while (firstMember.next());
342 if (lastUnderscore) {
343 ++lastUnderscore;
344 string anonName = className + "::" + string(lastUnderscore,
345 wordEnd - lastUnderscore) + 's';
346 def = root->find(anonName);
347 }
348 if (!def) {
349 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
350 break;
351 }
352 }
353 def->fVisited = true;
354 for (auto& child : def->fChildren) {
355 if (MarkType::kCode == child->fMarkType) {
356 def = child;
357 break;
358 }
359 }
360 if (MarkType::kCode != def->fMarkType) {
361 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
362 break;
363 }
364 if (def->crossCheck(token)) {
365 def->fVisited = true;
366 } else {
367 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
368 }
369 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400370 string constName = MarkType::kEnumClass == token.fMarkType ?
371 fullName : className;
372 constName += "::" + child->fName;
Cary Clark8032b982017-07-28 11:04:54 -0400373 def = root->find(constName);
374 if (!def) {
375 string innerName = classMapper.first + "::" + child->fName;
376 def = root->find(innerName);
377 }
378 if (!def) {
379 if (string::npos == child->fName.find("Legacy_")) {
380 SkDebugf("const missing from bmh: %s\n", constName.c_str());
381 }
382 } else {
383 def->fVisited = true;
384 }
385 }
386 } break;
387 case MarkType::kMember:
388 if (def) {
389 def->fVisited = true;
390 } else {
391 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
392 }
393 break;
394 default:
395 SkASSERT(0); // unhandled
396 break;
397 }
398 }
399 }
400 if (!root->dumpUnVisited()) {
401 SkDebugf("some struct elements not found; struct finding in includeParser is missing\n");
402 }
403 return true;
404}
405
406IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
407 const string& name) {
408 string className;
409 const Definition* test = fParent;
410 while (Definition::Type::kFileType != test->fType) {
411 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
412 className = test->fName + "::";
413 break;
414 }
415 test = test->fParent;
416 }
417 className += name;
418 unordered_map<string, IClassDefinition>& map = fIClassMap;
419 IClassDefinition& markupDef = map[className];
420 if (markupDef.fStart) {
421 typedef IClassDefinition* IClassDefPtr;
422 return INHERITED::reportError<IClassDefPtr>("class already defined");
423 }
424 markupDef.fFileName = fFileName;
425 markupDef.fStart = includeDef.fStart;
426 markupDef.fContentStart = includeDef.fStart;
427 markupDef.fName = className;
428 markupDef.fContentEnd = includeDef.fContentEnd;
429 markupDef.fTerminator = includeDef.fTerminator;
430 markupDef.fParent = fParent;
431 markupDef.fLineCount = fLineCount;
432 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
433 MarkType::kStruct : MarkType::kClass;
434 markupDef.fKeyWord = includeDef.fKeyWord;
435 markupDef.fType = Definition::Type::kMark;
436 fParent = &markupDef;
437 return &markupDef;
438}
439
440void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
441 auto& tokens = classDef.fTokens;
442 for (auto& token : tokens) {
443 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
444 continue;
445 }
446 if (MarkType::kMember != token.fMarkType) {
447 fprintf(fOut, "%s",
448 "# ------------------------------------------------------------------------------\n");
449 fprintf(fOut, "" "\n");
450 }
451 switch (token.fMarkType) {
452 case MarkType::kEnum:
453 fprintf(fOut, "#Enum %s" "\n",
454 token.fName.c_str());
455 fprintf(fOut, "" "\n");
456 fprintf(fOut, "#Code" "\n");
457 fprintf(fOut, " enum %s {" "\n",
458 token.fName.c_str());
459 for (auto& child : token.fChildren) {
460 fprintf(fOut, " %s %.*s" "\n",
461 child->fName.c_str(), child->length(), child->fContentStart);
462 }
463 fprintf(fOut, " };" "\n");
464 fprintf(fOut, "##" "\n");
465 fprintf(fOut, "" "\n");
466 this->dumpComment(&token);
467 for (auto& child : token.fChildren) {
468 fprintf(fOut, "#Const %s", child->fName.c_str());
469 TextParser val(child);
470 if (!val.eof()) {
471 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
472 val.next();
473 val.skipSpace();
474 const char* valEnd = val.anyOf(",\n");
475 if (!valEnd) {
476 valEnd = val.fEnd;
477 }
478 fprintf(fOut, " %.*s", (int) (valEnd - val.fStart), val.fStart);
479 } else {
480 fprintf(fOut, " %.*s",
481 (int) (child->fContentEnd - child->fContentStart),
482 child->fContentStart);
483 }
484 }
485 fprintf(fOut, "" "\n");
486 for (auto& token : child->fTokens) {
487 if (MarkType::kComment == token.fMarkType) {
488 this->dumpComment(&token);
489 }
490 }
491 fprintf(fOut, "##" "\n");
492 }
493 fprintf(fOut, "" "\n");
494 break;
495 case MarkType::kMethod:
496 fprintf(fOut, "#Method %.*s" "\n",
497 token.length(), token.fStart);
498 lfAlways(1);
499 this->dumpComment(&token);
500 break;
501 case MarkType::kMember:
502 this->keywordStart("Member");
503 fprintf(fOut, "%.*s %s ", (int) (token.fContentEnd - token.fContentStart),
504 token.fContentStart, token.fName.c_str());
505 lfAlways(1);
506 for (auto child : token.fChildren) {
507 fprintf(fOut, "%.*s", (int) (child->fContentEnd - child->fContentStart),
508 child->fContentStart);
509 lfAlways(1);
510 }
511 this->keywordEnd();
512 continue;
513 break;
514 default:
515 SkASSERT(0);
516 }
517 this->lf(2);
518 fprintf(fOut, "#Example" "\n");
519 fprintf(fOut, "##" "\n");
520 fprintf(fOut, "" "\n");
521 fprintf(fOut, "#ToDo incomplete ##" "\n");
522 fprintf(fOut, "" "\n");
523 fprintf(fOut, "##" "\n");
524 fprintf(fOut, "" "\n");
525 }
526}
527void IncludeParser::dumpComment(Definition* token) {
528 fLineCount = token->fLineCount;
529 fChar = fLine = token->fContentStart;
530 fEnd = token->fContentEnd;
531 bool sawParam = false;
532 bool multiline = false;
533 bool sawReturn = false;
534 bool sawComment = false;
535 bool methodHasReturn = false;
536 vector<string> methodParams;
537 vector<string> foundParams;
538 Definition methodName;
539 TextParser methodParser(token->fFileName, token->fContentStart, token->fContentEnd,
540 token->fLineCount);
541 if (MarkType::kMethod == token->fMarkType) {
542 methodName.fName = string(token->fContentStart,
543 (int) (token->fContentEnd - token->fContentStart));
544 methodHasReturn = !methodParser.startsWith("void ")
545 && !methodParser.strnchr('~', methodParser.fEnd);
546 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
547 const char* nextEnd = paren;
548 do {
549 string paramName;
550 methodParser.fChar = nextEnd + 1;
551 methodParser.skipSpace();
552 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
553 continue;
554 }
555 methodParams.push_back(paramName);
556 } while (')' != nextEnd[0]);
557 }
558 for (const auto& child : token->fTokens) {
559 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
560 if ('@' == child.fContentStart[0]) {
561 TextParser parser(&child);
562 do {
563 parser.next();
564 if (parser.startsWith("param ")) {
565 parser.skipWord("param");
566 const char* parmStart = parser.fChar;
567 parser.skipToSpace();
568 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
569 parser.skipWhiteSpace();
570 do {
571 size_t nextComma = parmName.find(',');
572 string piece;
573 if (string::npos == nextComma) {
574 piece = parmName;
575 parmName = "";
576 } else {
577 piece = parmName.substr(0, nextComma);
578 parmName = parmName.substr(nextComma + 1);
579 }
580 if (sawParam) {
581 if (multiline) {
582 this->lfAlways(1);
583 }
584 this->keywordEnd();
585 } else {
586 if (sawComment) {
587 this->nl();
588 }
589 this->lf(2);
590 }
591 foundParams.emplace_back(piece);
592 this->keywordStart("Param");
593 fprintf(fOut, "%s ", piece.c_str());
594 fprintf(fOut, "%.*s", (int) (parser.fEnd - parser.fChar), parser.fChar);
595 this->lfAlways(1);
596 sawParam = true;
597 sawComment = false;
598 } while (parmName.length());
599 parser.skipTo(parser.fEnd);
600 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
601 parser.skipWord("return");
602 if ('s' == parser.peek()) {
603 parser.next();
604 }
605 if (sawParam) {
606 if (multiline) {
607 this->lfAlways(1);
608 }
609 this->keywordEnd();
610 }
611 this->checkForMissingParams(methodParams, foundParams);
612 sawParam = false;
613 sawComment = false;
614 multiline = false;
615 this->lf(2);
616 this->keywordStart("Return");
617 fprintf(fOut, "%.*s ", (int) (parser.fEnd - parser.fChar),
618 parser.fChar);
619 this->lfAlways(1);
620 sawReturn = true;
621 parser.skipTo(parser.fEnd);
622 } else {
623 this->reportError("unexpected doxygen directive");
624 }
625 } while (!parser.eof());
626 } else {
627 if (sawComment) {
628 this->nl();
629 }
630 this->lf(1);
631 fprintf(fOut, "%.*s ", child.length(), child.fContentStart);
632 sawComment = true;
633 if (sawParam || sawReturn) {
634 multiline = true;
635 }
636 }
637 }
638 }
639 if (sawParam || sawReturn) {
640 if (multiline) {
641 this->lfAlways(1);
642 }
643 this->keywordEnd();
644 }
645 if (!sawReturn) {
646 if (!sawParam) {
647 if (sawComment) {
648 this->nl();
649 }
650 this->lf(2);
651 }
652 this->checkForMissingParams(methodParams, foundParams);
653 }
654 if (methodHasReturn != sawReturn) {
655 if (!methodHasReturn) {
656 this->reportError("unexpected doxygen return");
657 } else {
658 if (sawComment) {
659 this->nl();
660 }
661 this->lf(2);
662 this->keywordStart("Return");
663 this->keywordEnd();
664 }
665 }
666}
667
668 // dump equivalent markup
669void IncludeParser::dumpTokens() {
670 string skClassName = this->className();
671 string fileName = skClassName + ".bmh";
672 fOut = fopen(fileName.c_str(), "wb");
673 if (!fOut) {
674 SkDebugf("could not open output file %s\n", fileName.c_str());
675 return;
676 }
677 string prefixName = skClassName.substr(0, 2);
678 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
679 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
680 fprintf(fOut, "#Topic %s", topicName.c_str());
681 this->lfAlways(2);
682 fprintf(fOut, "#Class %s", skClassName.c_str());
683 this->lfAlways(2);
684 auto& classMap = fIClassMap[skClassName];
685 auto& tokens = classMap.fTokens;
686 for (auto& token : tokens) {
687 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
688 continue;
689 }
690 fprintf(fOut, "%.*s", (int) (token.fContentEnd - token.fContentStart),
691 token.fContentStart);
692 this->lfAlways(1);
693 }
694 this->lf(2);
695 string className(skClassName.substr(2));
696 vector<string> sortedClasses;
697 size_t maxLen = 0;
698 for (const auto& oneClass : fIClassMap) {
699 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
700 continue;
701 }
702 string structName = oneClass.first.substr(skClassName.length() + 2);
703 maxLen = SkTMax(maxLen, structName.length());
704 sortedClasses.emplace_back(structName);
705 }
706 fprintf(fOut, "#Topic Overview");
707 this->lfAlways(2);
708 fprintf(fOut, "#Subtopic %s_Structs", className.c_str());
709 this->lfAlways(1);
710 fprintf(fOut, "#Table");
711 this->lfAlways(1);
712 fprintf(fOut, "#Legend");
713 this->lfAlways(1);
714 fprintf(fOut, "# %-*s # description ##", (int) maxLen, "struct");
715 this->lfAlways(1);
716 fprintf(fOut, "#Legend ##");
717 this->lfAlways(1);
718 fprintf(fOut, "#Table ##");
719 this->lfAlways(1);
720 for (auto& name : sortedClasses) {
721 fprintf(fOut, "# %-*s # ##", (int) maxLen, name.c_str());
722 this->lfAlways(1);
723 }
724 fprintf(fOut, "#Subtopic ##");
725 this->lfAlways(2);
726 fprintf(fOut, "#Subtopic %s_Member_Functions", className.c_str());
727 this->lfAlways(1);
728 fprintf(fOut, "#Table");
729 this->lfAlways(1);
730 fprintf(fOut, "#Legend");
731 this->lfAlways(1);
732 maxLen = 0;
733 vector<string> sortedNames;
734 for (const auto& token : classMap.fTokens) {
735 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
736 continue;
737 }
738 const string& name = token.fName;
739 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
740 continue;
741 }
742 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
743 continue;
744 }
745 size_t paren = name.find('(');
746 size_t funcLen = string::npos == paren ? name.length() : paren;
747 maxLen = SkTMax(maxLen, funcLen);
748 sortedNames.emplace_back(name);
749 }
750 std::sort(sortedNames.begin(), sortedNames.end());
751 fprintf(fOut, "# %-*s # description ##" "\n",
752 (int) maxLen, "function");
753 fprintf(fOut, "#Legend ##" "\n");
754 for (auto& name : sortedNames) {
755 size_t paren = name.find('(');
756 size_t funcLen = string::npos == paren ? name.length() : paren;
757 fprintf(fOut, "# %-*s # ##" "\n",
758 (int) maxLen, name.substr(0, funcLen).c_str());
759 }
760 fprintf(fOut, "#Table ##" "\n");
761 fprintf(fOut, "#Subtopic ##" "\n");
762 fprintf(fOut, "" "\n");
763 fprintf(fOut, "#Topic ##" "\n");
764 fprintf(fOut, "" "\n");
765
766 for (auto& oneClass : fIClassMap) {
767 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
768 continue;
769 }
770 string innerName = oneClass.first.substr(skClassName.length() + 2);
771 fprintf(fOut, "%s",
772 "# ------------------------------------------------------------------------------");
773 this->lfAlways(2);
774 fprintf(fOut, "#Struct %s", innerName.c_str());
775 this->lfAlways(2);
776 for (auto& token : oneClass.second.fTokens) {
777 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
778 continue;
779 }
780 fprintf(fOut, "%.*s", (int) (token.fContentEnd - token.fContentStart),
781 token.fContentStart);
782 this->lfAlways(1);
783 }
784 this->lf(2);
785 this->dumpClassTokens(oneClass.second);
786 this->lf(2);
787 fprintf(fOut, "#Struct %s ##", innerName.c_str());
788 this->lfAlways(2);
789 }
790 this->dumpClassTokens(classMap);
791 fprintf(fOut, "#Class %s ##" "\n",
792 skClassName.c_str());
793 fprintf(fOut, "" "\n");
794 fprintf(fOut, "#Topic %s ##" "\n",
795 topicName.c_str());
796 fclose(fOut);
797}
798
799bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
800 // add comment preceding class, if any
801 const Definition* parent = includeDef.fParent;
802 int index = includeDef.fParentIndex;
803 auto wordIter = parent->fTokens.begin();
804 std::advance(wordIter, index);
805 SkASSERT(&*wordIter == &includeDef);
806 while (parent->fTokens.begin() != wordIter) {
807 auto testIter = std::prev(wordIter);
808 if (Definition::Type::kWord != testIter->fType
809 && Definition::Type::kKeyWord != testIter->fType
810 && (Definition::Type::kBracket != testIter->fType
811 || Bracket::kAngle != testIter->fBracket)
812 && (Definition::Type::kPunctuation != testIter->fType
813 || Punctuation::kAsterisk != testIter->fPunctuation)) {
814 break;
815 }
816 wordIter = testIter;
817 }
818 auto commentIter = wordIter;
819 while (parent->fTokens.begin() != commentIter) {
820 auto testIter = std::prev(commentIter);
821 bool isComment = Definition::Type::kBracket == testIter->fType
822 && (Bracket::kSlashSlash == testIter->fBracket
823 || Bracket::kSlashStar == testIter->fBracket);
824 if (!isComment) {
825 break;
826 }
827 commentIter = testIter;
828 }
829 while (commentIter != wordIter) {
830 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
831 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
832 return false;
833 }
834 commentIter = std::next(commentIter);
835 }
836 return true;
837}
838
Cary Clarkbad5ad72017-08-03 17:14:08 -0400839bool IncludeParser::internalName(const Definition& token) const {
840 return 0 == token.fName.find("internal_")
841 || 0 == token.fName.find("Internal_")
842 || 0 == token.fName.find("legacy_")
843 || 0 == token.fName.find("temporary_")
844 || 0 == token.fName.find("private_");
845}
846
Cary Clark8032b982017-07-28 11:04:54 -0400847// caller calls reportError, so just return false here
848bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
849 SkASSERT(includeDef->fTokens.size() > 0);
850 if (includeDef->fTokens.size() == 1) {
851 return true; // forward declaration only
852 }
853 // parse class header
854 auto iter = includeDef->fTokens.begin();
855 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
856 // todo : documentation is ignoring this for now
857 iter = std::next(iter);
858 }
859 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
860 includeDef->fName = nameStr;
861 do {
862 if (iter == includeDef->fTokens.end()) {
863 return false;
864 }
865 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
866 break;
867 }
868 } while (static_cast<void>(iter = std::next(iter)), true);
869 if (Punctuation::kLeftBrace != iter->fPunctuation) {
870 return false;
871 }
872 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
873 if (!markupDef) {
874 return false;
875 }
876 markupDef->fStart = iter->fStart;
877 if (!this->findComments(*includeDef, markupDef)) {
878 return false;
879 }
880// if (1 != includeDef->fChildren.size()) {
881// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
882// }
883 includeDef = includeDef->fChildren.front();
884 iter = includeDef->fTokens.begin();
885 // skip until public
886 int publicIndex = 0;
887 if (IsStruct::kNo == isStruct) {
888 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
889 size_t publicLen = strlen(publicName);
890 while (iter != includeDef->fTokens.end()
891 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
892 || strncmp(iter->fStart, publicName, publicLen))) {
893 iter = std::next(iter);
894 ++publicIndex;
895 }
896 }
897 auto childIter = includeDef->fChildren.begin();
898 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
899 (*childIter)->fPrivate = true;
900 childIter = std::next(childIter);
901 }
902 int lastPublic = publicIndex;
903 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
904 size_t protectedLen = strlen(protectedName);
905 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
906 size_t privateLen = strlen(privateName);
907 while (iter != includeDef->fTokens.end()
908 && (protectedLen != (size_t) (iter->fContentEnd - iter->fStart)
909 || strncmp(iter->fStart, protectedName, protectedLen))
910 && (privateLen != (size_t) (iter->fContentEnd - iter->fStart)
911 || strncmp(iter->fStart, privateName, privateLen))) {
912 iter = std::next(iter);
913 ++lastPublic;
914 }
915 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < lastPublic) {
916 Definition* child = *childIter;
917 if (!this->parseObject(child, markupDef)) {
918 return false;
919 }
920 childIter = std::next(childIter);
921 }
922 while (childIter != includeDef->fChildren.end()) {
923 (*childIter)->fPrivate = true;
924 childIter = std::next(childIter);
925 }
926 SkASSERT(fParent->fParent);
927 fParent = fParent->fParent;
928 return true;
929}
930
931bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
932 int lineCount, Definition* markupDef) {
933 TextParser parser(filename, start, end, lineCount);
934 // parse doxygen if present
935 if (parser.startsWith("**")) {
936 parser.next();
937 parser.next();
938 parser.skipWhiteSpace();
939 if ('\\' == parser.peek()) {
940 parser.next();
941 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
942 return reportError<bool>("missing object type");
943 }
944 if (!parser.skipWord(markupDef->fName.c_str())) {
945 return reportError<bool>("missing object name");
946 }
947
948 }
949 }
950 // remove leading '*' if present
951 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
952 while (!parser.eof() && parser.skipWhiteSpace()) {
953 while ('*' == parser.peek()) {
954 parser.next();
955 if (parser.eof()) {
956 break;
957 }
958 parser.skipWhiteSpace();
959 }
960 if (parser.eof()) {
961 break;
962 }
963 const char* lineEnd = parser.trimmedLineEnd();
964 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
965 parser.fLineCount, parent);
966 parser.skipToEndBracket('\n');
967 }
968 return true;
969}
970
971bool IncludeParser::parseDefine() {
972
973 return true;
974}
975
976bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
977 string nameStr;
978 if (child->fTokens.size() > 0) {
979 auto token = child->fTokens.begin();
980 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
981 token = token->fTokens.begin();
982 }
983 if (Definition::Type::kWord == token->fType) {
984 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
985 }
986 }
987 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
988 child->fLineCount, markupDef);
989 Definition* markupChild = &markupDef->fTokens.back();
990 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
991 markupChild->fKeyWord = KeyWord::kEnum;
992 TextParser enumName(child);
993 enumName.skipExact("enum ");
Cary Clarkbad5ad72017-08-03 17:14:08 -0400994 if (enumName.skipExact("class ")) {
995 markupChild->fMarkType = MarkType::kEnumClass;
996 }
Cary Clark8032b982017-07-28 11:04:54 -0400997 const char* nameStart = enumName.fChar;
998 enumName.skipToSpace();
999 markupChild->fName = markupDef->fName + "::" +
1000 string(nameStart, (size_t) (enumName.fChar - nameStart));
1001 if (!this->findComments(*child, markupChild)) {
1002 return false;
1003 }
1004 TextParser parser(child);
1005 parser.skipToEndBracket('{');
1006 const char* dataEnd;
1007 do {
1008 parser.next();
1009 parser.skipWhiteSpace();
1010 if ('}' == parser.peek()) {
1011 break;
1012 }
1013 Definition* comment = nullptr;
1014 // note that comment, if any, can be before or after (on the same line, though) as member
1015 if ('#' == parser.peek()) {
1016 // fixme: handle preprecessor, but just skip it for now
1017 parser.skipToLineStart();
1018 }
1019 while (parser.startsWith("/*") || parser.startsWith("//")) {
1020 parser.next();
1021 const char* start = parser.fChar;
1022 const char* end;
1023 if ('*' == parser.peek()) {
1024 end = parser.strnstr("*/", parser.fEnd);
1025 parser.fChar = end;
1026 parser.next();
1027 parser.next();
1028 } else {
1029 end = parser.trimmedLineEnd();
1030 parser.skipToLineStart();
1031 }
1032 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1033 markupChild);
1034 comment = &markupChild->fTokens.back();
1035 comment->fTerminator = end;
1036 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1037 return false;
1038 }
1039 parser.skipWhiteSpace();
1040 }
1041 parser.skipWhiteSpace();
1042 const char* memberStart = parser.fChar;
1043 if ('}' == memberStart[0]) {
1044 break;
1045 }
1046 parser.skipToNonAlphaNum();
1047 string memberName(memberStart, parser.fChar);
1048 parser.skipWhiteSpace();
1049 const char* dataStart = parser.fChar;
1050 SkASSERT('=' == dataStart[0] || ',' == dataStart[0] || '}' == dataStart[0]
1051 || '/' == dataStart[0]);
1052 dataEnd = parser.anyOf(",}");
1053 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1054 markupChild);
1055 Definition* member = &markupChild->fTokens.back();
1056 member->fName = memberName;
1057 if (comment) {
1058 member->fChildren.push_back(comment);
1059 }
1060 markupChild->fChildren.push_back(member);
1061 parser.skipToEndBracket(dataEnd[0]);
1062 } while (',' == dataEnd[0]);
1063 for (size_t index = 1; index < child->fChildren.size(); ++index) {
1064 const Definition* follower = child->fChildren[index];
1065 if (Definition::Type::kKeyWord == follower->fType) {
1066 markupChild->fTokens.emplace_back(MarkType::kMember, follower->fContentStart,
1067 follower->fContentEnd, follower->fLineCount, markupChild);
1068 Definition* member = &markupChild->fTokens.back();
1069 member->fName = follower->fName;
1070 markupChild->fChildren.push_back(member);
1071 }
1072 }
1073 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1074 SkASSERT(classDef.fStart);
1075 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1076 markupChild->fName = uniqueName;
1077 classDef.fEnums[uniqueName] = markupChild;
1078 return true;
1079}
1080
1081bool IncludeParser::parseInclude(const string& name) {
1082 fParent = &fIncludeMap[name];
1083 fParent->fName = name;
1084 fParent->fFileName = fFileName;
1085 fParent->fType = Definition::Type::kFileType;
1086 fParent->fContentStart = fChar;
1087 fParent->fContentEnd = fEnd;
1088 // parse include file into tree
1089 while (fChar < fEnd) {
1090 if (!this->parseChar()) {
1091 return false;
1092 }
1093 }
1094 // parse tree and add named objects to maps
1095 fParent = &fIncludeMap[name];
1096 if (!this->parseObjects(fParent, nullptr)) {
1097 return false;
1098 }
1099 return true;
1100}
1101
1102bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1103 const char* typeStart = child->fChildren[0]->fContentStart;
1104 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1105 child->fLineCount, markupDef);
1106 Definition* markupChild = &markupDef->fTokens.back();
1107 TextParser nameParser(child);
1108 nameParser.skipToNonAlphaNum();
1109 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1110 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1111 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1112 markupChild->fName = uniqueName;
1113 classDef.fMembers[uniqueName] = markupChild;
1114 if (child->fParentIndex >= 2) {
1115 auto comment = child->fParent->fTokens.begin();
1116 std::advance(comment, child->fParentIndex - 2);
1117 if (Definition::Type::kBracket == comment->fType
1118 && (Bracket::kSlashStar == comment->fBracket
1119 || Bracket::kSlashSlash == comment->fBracket)) {
1120 TextParser parser(&*comment);
1121 do {
1122 parser.skipToAlpha();
1123 if (parser.eof()) {
1124 break;
1125 }
1126 const char* start = parser.fChar;
1127 const char* end = parser.trimmedBracketEnd('\n', OneLine::kYes);
1128 if (Bracket::kSlashStar == comment->fBracket) {
1129 const char* commentEnd = parser.strnstr("*/", end);
1130 if (commentEnd) {
1131 end = commentEnd;
1132 }
1133 }
1134 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1135 markupDef);
1136 Definition* commentChild = &markupDef->fTokens.back();
1137 markupChild->fChildren.emplace_back(commentChild);
1138 parser.skipTo(end);
1139 } while (!parser.eof());
1140 }
1141 }
1142 return true;
1143}
1144
1145bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1146 auto tokenIter = child->fParent->fTokens.begin();
1147 std::advance(tokenIter, child->fParentIndex);
1148 tokenIter = std::prev(tokenIter);
1149 string nameStr(tokenIter->fStart, tokenIter->fContentEnd - tokenIter->fStart);
1150 while (tokenIter != child->fParent->fTokens.begin()) {
1151 auto testIter = std::prev(tokenIter);
1152 switch (testIter->fType) {
1153 case Definition::Type::kWord:
1154 goto keepGoing;
1155 case Definition::Type::kKeyWord: {
1156 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1157 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1158 goto keepGoing;
1159 }
1160 } break;
1161 case Definition::Type::kBracket:
1162 if (Bracket::kAngle == testIter->fBracket) {
1163 goto keepGoing;
1164 }
1165 break;
1166 case Definition::Type::kPunctuation:
1167 if (Punctuation::kSemicolon == testIter->fPunctuation
1168 || Punctuation::kLeftBrace == testIter->fPunctuation
1169 || Punctuation::kColon == testIter->fPunctuation) {
1170 break;
1171 }
1172 keepGoing:
1173 tokenIter = testIter;
1174 continue;
1175 default:
1176 break;
1177 }
1178 break;
1179 }
1180 tokenIter->fName = nameStr;
1181 tokenIter->fMarkType = MarkType::kMethod;
1182 auto testIter = child->fParent->fTokens.begin();
1183 SkASSERT(child->fParentIndex > 0);
1184 std::advance(testIter, child->fParentIndex - 1);
1185 const char* start = tokenIter->fContentStart;
1186 const char* end = tokenIter->fContentEnd;
1187 const char kDebugCodeStr[] = "SkDEBUGCODE";
1188 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1189 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1190 std::advance(testIter, 1);
1191 start = testIter->fContentStart + 1;
1192 end = testIter->fContentEnd - 1;
1193 } else {
1194 end = testIter->fContentEnd;
1195 while (testIter != child->fParent->fTokens.end()) {
1196 testIter = std::next(testIter);
1197 switch (testIter->fType) {
1198 case Definition::Type::kPunctuation:
1199 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1200 || Punctuation::kLeftBrace == testIter->fPunctuation
1201 || Punctuation::kColon == testIter->fPunctuation);
1202 end = testIter->fStart;
1203 break;
1204 case Definition::Type::kKeyWord: {
1205 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1206 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1207 continue;
1208 }
1209 } break;
1210 default:
1211 continue;
1212 }
1213 break;
1214 }
1215 }
1216 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1217 markupDef);
1218 Definition* markupChild = &markupDef->fTokens.back();
1219 // do find instead -- I wonder if there is a way to prevent this in c++
1220 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1221 SkASSERT(classDef.fStart);
1222 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1223 markupChild->fName = uniqueName;
1224 if (!this->findComments(*child, markupChild)) {
1225 return false;
1226 }
1227 classDef.fMethods[uniqueName] = markupChild;
1228 return true;
1229}
1230
1231void IncludeParser::keywordEnd() {
1232 fprintf(fOut, "##");
1233 this->lfAlways(1);
1234}
1235
1236void IncludeParser::keywordStart(const char* keyword) {
1237 this->lf(1);
1238 fprintf(fOut, "#%s ", keyword);
1239}
1240
1241bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1242 for (auto& child : parent->fChildren) {
1243 if (!this->parseObject(child, markupDef)) {
1244 return false;
1245 }
1246 }
1247 return true;
1248}
1249
1250bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1251 // set up for error reporting
1252 fLine = fChar = child->fStart;
1253 fEnd = child->fContentEnd;
1254 // todo: put original line number in child as well
1255 switch (child->fType) {
1256 case Definition::Type::kKeyWord:
1257 switch (child->fKeyWord) {
1258 case KeyWord::kClass:
1259 if (!this->parseClass(child, IsStruct::kNo)) {
1260 return this->reportError<bool>("failed to parse class");
1261 }
1262 break;
1263 case KeyWord::kEnum:
1264 if (!this->parseEnum(child, markupDef)) {
1265 return this->reportError<bool>("failed to parse enum");
1266 }
1267 break;
1268 case KeyWord::kStruct:
1269 if (!this->parseClass(child, IsStruct::kYes)) {
1270 return this->reportError<bool>("failed to parse struct");
1271 }
1272 break;
1273 case KeyWord::kTemplate:
1274 if (!this->parseTemplate()) {
1275 return this->reportError<bool>("failed to parse template");
1276 }
1277 break;
1278 case KeyWord::kTypedef:
1279 if (!this->parseTypedef()) {
1280 return this->reportError<bool>("failed to parse typedef");
1281 }
1282 break;
1283 case KeyWord::kUnion:
1284 if (!this->parseUnion()) {
1285 return this->reportError<bool>("failed to parse union");
1286 }
1287 break;
1288 default:
1289 return this->reportError<bool>("unhandled keyword");
1290 }
1291 break;
1292 case Definition::Type::kBracket:
1293 switch (child->fBracket) {
1294 case Bracket::kParen:
1295 if (!this->parseMethod(child, markupDef)) {
1296 return this->reportError<bool>("failed to parse method");
1297 }
1298 break;
1299 case Bracket::kSlashSlash:
1300 case Bracket::kSlashStar:
1301 // comments are picked up by parsing objects first
1302 break;
1303 case Bracket::kPound:
1304 // special-case the #xxx xxx_DEFINED entries
1305 switch (child->fKeyWord) {
1306 case KeyWord::kIfndef:
1307 case KeyWord::kIfdef:
1308 if (child->boilerplateIfDef(fParent)) {
1309 if (!this->parseObjects(child, markupDef)) {
1310 return false;
1311 }
1312 break;
1313 }
1314 goto preproError;
1315 case KeyWord::kDefine:
1316 if (child->boilerplateDef(fParent)) {
1317 break;
1318 }
1319 goto preproError;
1320 case KeyWord::kEndif:
1321 if (child->boilerplateEndIf()) {
1322 break;
1323 }
1324 case KeyWord::kInclude:
1325 // ignored for now
1326 break;
1327 case KeyWord::kElse:
1328 case KeyWord::kElif:
1329 // todo: handle these
1330 break;
1331 default:
1332 preproError:
1333 return this->reportError<bool>("unhandled preprocessor");
1334 }
1335 break;
1336 case Bracket::kAngle:
1337 // pick up templated function pieces when method is found
1338 break;
1339 default:
1340 return this->reportError<bool>("unhandled bracket");
1341 }
1342 break;
1343 case Definition::Type::kWord:
1344 if (MarkType::kMember != child->fMarkType) {
1345 return this->reportError<bool>("unhandled word type");
1346 }
1347 if (!this->parseMember(child, markupDef)) {
1348 return this->reportError<bool>("unparsable member");
1349 }
1350 break;
1351 default:
1352 return this->reportError<bool>("unhandled type");
1353 break;
1354 }
1355 return true;
1356}
1357
1358bool IncludeParser::parseTemplate() {
1359
1360 return true;
1361}
1362
1363bool IncludeParser::parseTypedef() {
1364
1365 return true;
1366}
1367
1368bool IncludeParser::parseUnion() {
1369
1370 return true;
1371}
1372
1373bool IncludeParser::parseChar() {
1374 char test = *fChar;
1375 if ('\\' == fPrev) {
1376 if ('\n' == test) {
1377 ++fLineCount;
1378 fLine = fChar + 1;
1379 }
1380 goto done;
1381 }
1382 switch (test) {
1383 case '\n':
1384 ++fLineCount;
1385 fLine = fChar + 1;
1386 if (fInChar) {
1387 return reportError<bool>("malformed char");
1388 }
1389 if (fInString) {
1390 return reportError<bool>("malformed string");
1391 }
1392 if (!this->checkForWord()) {
1393 return false;
1394 }
1395 if (Bracket::kPound == this->topBracket()) {
1396 KeyWord keyWord = fParent->fKeyWord;
1397 if (KeyWord::kNone == keyWord) {
1398 return this->reportError<bool>("unhandled preprocessor directive");
1399 }
1400 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) {
1401 this->popBracket();
1402 }
1403 } else if (Bracket::kSlashSlash == this->topBracket()) {
1404 this->popBracket();
1405 }
1406 break;
1407 case '*':
1408 if (!fInCharCommentString && '/' == fPrev) {
1409 this->pushBracket(Bracket::kSlashStar);
1410 }
1411 if (!this->checkForWord()) {
1412 return false;
1413 }
1414 if (!fInCharCommentString) {
1415 this->addPunctuation(Punctuation::kAsterisk);
1416 }
1417 break;
1418 case '/':
1419 if ('*' == fPrev) {
1420 if (!fInCharCommentString) {
1421 return reportError<bool>("malformed closing comment");
1422 }
1423 if (Bracket::kSlashStar == this->topBracket()) {
1424 this->popBracket();
1425 }
1426 break;
1427 }
1428 if (!fInCharCommentString && '/' == fPrev) {
1429 this->pushBracket(Bracket::kSlashSlash);
1430 break;
1431 }
1432 if (!this->checkForWord()) {
1433 return false;
1434 }
1435 break;
1436 case '\'':
1437 if (Bracket::kChar == this->topBracket()) {
1438 this->popBracket();
1439 } else if (!fInComment && !fInString) {
1440 if (fIncludeWord) {
1441 return this->reportError<bool>("word then single-quote");
1442 }
1443 this->pushBracket(Bracket::kChar);
1444 }
1445 break;
1446 case '\"':
1447 if (Bracket::kString == this->topBracket()) {
1448 this->popBracket();
1449 } else if (!fInComment && !fInChar) {
1450 if (fIncludeWord) {
1451 return this->reportError<bool>("word then double-quote");
1452 }
1453 this->pushBracket(Bracket::kString);
1454 }
1455 break;
1456 case ':':
1457 case '(':
1458 case '[':
1459 case '{': {
1460 if (fInCharCommentString) {
1461 break;
1462 }
1463 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1464 break;
1465 }
1466 if (!fInBrace) {
1467 if (!this->checkForWord()) {
1468 return false;
1469 }
1470 if (':' == test && !fInFunction) {
1471 break;
1472 }
1473 if ('{' == test) {
1474 this->addPunctuation(Punctuation::kLeftBrace);
1475 } else if (':' == test) {
1476 this->addPunctuation(Punctuation::kColon);
1477 }
1478 }
1479 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1480 && Bracket::kColon == fInBrace->fBracket) {
1481 Definition* braceParent = fParent->fParent;
1482 braceParent->fChildren.pop_back();
1483 braceParent->fTokens.pop_back();
1484 fParent = braceParent;
1485 fInBrace = nullptr;
1486 }
1487 this->pushBracket(
1488 '(' == test ? Bracket::kParen :
1489 '[' == test ? Bracket::kSquare :
1490 '{' == test ? Bracket::kBrace :
1491 Bracket::kColon);
1492 if (!fInBrace
1493 && ('{' == test || (':' == test && ' ' >= fChar[1]))
1494 && fInFunction) {
1495 fInBrace = fParent;
1496 }
1497 } break;
1498 case '<':
1499 if (fInCharCommentString || fInBrace) {
1500 break;
1501 }
1502 if (!this->checkForWord()) {
1503 return false;
1504 }
1505 if (fInEnum) {
1506 break;
1507 }
1508 this->pushBracket(Bracket::kAngle);
1509 break;
1510 case ')':
1511 case ']':
1512 case '}': {
1513 if (fInCharCommentString) {
1514 break;
1515 }
1516 if (!fInBrace) {
1517 if (!this->checkForWord()) {
1518 return false;
1519 }
1520 }
1521 bool popBraceParent = fInBrace == fParent;
1522 if ((')' == test ? Bracket::kParen :
1523 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
1524 this->popBracket();
1525 if (!fInFunction) {
1526 bool deprecatedMacro = false;
1527 if (')' == test) {
1528 auto iter = fParent->fTokens.end();
1529 bool lookForWord = false;
1530 while (fParent->fTokens.begin() != iter) {
1531 --iter;
1532 if (lookForWord) {
1533 if (Definition::Type::kWord != iter->fType) {
1534 break;
1535 }
1536 string word(iter->fContentStart, iter->length());
1537 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
1538 deprecatedMacro = true;
1539 // remove macro paren (would confuse method parsing later)
1540 fParent->fTokens.pop_back();
1541 fParent->fChildren.pop_back();
1542 }
1543 break;
1544 }
1545 if (Definition::Type::kBracket != iter->fType) {
1546 break;
1547 }
1548 if (Bracket::kParen != iter->fBracket) {
1549 break;
1550 }
1551 lookForWord = true;
1552 }
1553 }
1554 fInFunction = ')' == test && !deprecatedMacro;
1555 } else {
1556 fInFunction = '}' != test;
1557 }
1558 } else {
1559 return reportError<bool>("malformed close bracket");
1560 }
1561 if (popBraceParent) {
1562 Definition* braceParent = fInBrace->fParent;
1563 braceParent->fChildren.pop_back();
1564 braceParent->fTokens.pop_back();
1565 fInBrace = nullptr;
1566 }
1567 } break;
1568 case '>':
1569 if (fInCharCommentString || fInBrace) {
1570 break;
1571 }
1572 if (!this->checkForWord()) {
1573 return false;
1574 }
1575 if (fInEnum) {
1576 break;
1577 }
1578 if (Bracket::kAngle == this->topBracket()) {
1579 this->popBracket();
1580 } else {
1581 return reportError<bool>("malformed close angle bracket");
1582 }
1583 break;
1584 case '#': {
1585 if (fInCharCommentString || fInBrace) {
1586 break;
1587 }
1588 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
1589 this->pushBracket(Bracket::kPound);
1590 break;
1591 }
1592 case '&':
1593 case ',':
1594 case ' ':
1595 if (fInCharCommentString || fInBrace) {
1596 break;
1597 }
1598 if (!this->checkForWord()) {
1599 return false;
1600 }
1601 break;
1602 case ';':
1603 if (fInCharCommentString || fInBrace) {
1604 break;
1605 }
1606 if (!this->checkForWord()) {
1607 return false;
1608 }
1609 if (Definition::Type::kKeyWord == fParent->fType
1610 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001611 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
1612 KeyWord::kEnum == fParent->fParent->fKeyWord) {
1613 this->popObject();
1614 }
Cary Clark8032b982017-07-28 11:04:54 -04001615 if (KeyWord::kEnum == fParent->fKeyWord) {
1616 fInEnum = false;
1617 }
1618 this->popObject();
1619 } else if (Definition::Type::kBracket == fParent->fType
1620 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
1621 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
1622 list<Definition>::iterator baseIter = fParent->fTokens.end();
1623 list<Definition>::iterator namedIter = fParent->fTokens.end();
1624 for (auto tokenIter = fParent->fTokens.end();
1625 fParent->fTokens.begin() != tokenIter--; ) {
1626 if (tokenIter->fLineCount == fLineCount) {
1627 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
1628 if (namedIter != fParent->fTokens.end()) {
1629 return reportError<bool>("found two named member tokens");
1630 }
1631 namedIter = tokenIter;
1632 }
1633 baseIter = tokenIter;
1634 } else {
1635 break;
1636 }
1637 }
1638 // FIXME: if a member definition spans multiple lines, this won't work
1639 if (namedIter != fParent->fTokens.end()) {
1640 if (baseIter == namedIter) {
1641 return this->reportError<bool>("expected type before named token");
1642 }
1643 Definition* member = &*namedIter;
1644 member->fMarkType = MarkType::kMember;
1645 fParent->fChildren.push_back(member);
1646 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
1647 member->fChildren.push_back(&*nameType);
1648 }
1649
1650 }
1651 } else if (fParent->fChildren.size() > 0) {
1652 auto lastIter = fParent->fChildren.end();
1653 Definition* priorEnum;
1654 while (fParent->fChildren.begin() != lastIter) {
1655 std::advance(lastIter, -1);
1656 priorEnum = *lastIter;
1657 if (Definition::Type::kBracket != priorEnum->fType ||
1658 (Bracket::kSlashSlash != priorEnum->fBracket
1659 && Bracket::kSlashStar != priorEnum->fBracket)) {
1660 break;
1661 }
1662 }
1663 if (Definition::Type::kKeyWord == priorEnum->fType
1664 && KeyWord::kEnum == priorEnum->fKeyWord) {
1665 auto tokenWalker = fParent->fTokens.begin();
1666 std::advance(tokenWalker, priorEnum->fParentIndex);
1667 SkASSERT(KeyWord::kEnum == tokenWalker->fKeyWord);
1668 while (tokenWalker != fParent->fTokens.end()) {
1669 std::advance(tokenWalker, 1);
1670 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
1671 break;
1672 }
1673 }
1674 while (tokenWalker != fParent->fTokens.end()) {
1675 std::advance(tokenWalker, 1);
1676 const Definition* test = &*tokenWalker;
1677 if (Definition::Type::kBracket != test->fType ||
1678 (Bracket::kSlashSlash != test->fBracket
1679 && Bracket::kSlashStar != test->fBracket)) {
1680 break;
1681 }
1682 }
1683 Definition* start = &*tokenWalker;
1684 bool foundExpected = true;
1685 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
1686 const Definition* test = &*tokenWalker;
1687 if (expected != test->fKeyWord) {
1688 foundExpected = false;
1689 break;
1690 }
1691 if (tokenWalker == fParent->fTokens.end()) {
1692 break;
1693 }
1694 std::advance(tokenWalker, 1);
1695 }
1696 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
1697 const char* nameStart = tokenWalker->fStart;
1698 std::advance(tokenWalker, 1);
1699 if (tokenWalker != fParent->fTokens.end()) {
1700 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
1701 tp.skipToNonAlphaNum();
1702 start->fName = string(nameStart, tp.fChar - nameStart);
1703 start->fContentEnd = fChar;
1704 priorEnum->fChildren.emplace_back(start);
1705 }
1706 }
1707 }
1708 }
1709 this->addPunctuation(Punctuation::kSemicolon);
1710 fInFunction = false;
1711 break;
1712 case '~':
1713 if (fInEnum) {
1714 break;
1715 }
1716 case '0': case '1': case '2': case '3': case '4':
1717 case '5': case '6': case '7': case '8': case '9':
1718 // TODO: don't want to parse numbers, but do need to track for enum defs
1719 // break;
1720 case 'A': case 'B': case 'C': case 'D': case 'E':
1721 case 'F': case 'G': case 'H': case 'I': case 'J':
1722 case 'K': case 'L': case 'M': case 'N': case 'O':
1723 case 'P': case 'Q': case 'R': case 'S': case 'T':
1724 case 'U': case 'V': case 'W': case 'X': case 'Y':
1725 case 'Z': case '_':
1726 case 'a': case 'b': case 'c': case 'd': case 'e':
1727 case 'f': case 'g': case 'h': case 'i': case 'j':
1728 case 'k': case 'l': case 'm': case 'n': case 'o':
1729 case 'p': case 'q': case 'r': case 's': case 't':
1730 case 'u': case 'v': case 'w': case 'x': case 'y':
1731 case 'z':
1732 if (fInCharCommentString || fInBrace) {
1733 break;
1734 }
1735 if (!fIncludeWord) {
1736 fIncludeWord = fChar;
1737 }
1738 break;
1739 }
1740done:
1741 fPrev = test;
1742 ++fChar;
1743 return true;
1744}
1745
1746void IncludeParser::validate() const {
1747 for (int index = 0; index <= (int) Last_MarkType; ++index) {
1748 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
1749 }
1750 IncludeParser::ValidateKeyWords();
1751}