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