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