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