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