1 /*
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 "SkOSFile.h"
9 #include "SkOSPath.h"
10
11 #include "bmhParser.h"
12 #include "includeParser.h"
13
14 const IncludeKey kKeyWords[] = {
15 { "", KeyWord::kNone, KeyProperty::kNone },
16 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
17 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
18 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
19 { "bool", KeyWord::kBool, KeyProperty::kNumber },
20 { "char", KeyWord::kChar, KeyProperty::kNumber },
21 { "class", KeyWord::kClass, KeyProperty::kObject },
22 { "const", KeyWord::kConst, KeyProperty::kModifier },
23 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
24 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
25 { "double", KeyWord::kDouble, KeyProperty::kNumber },
26 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
27 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
28 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
29 { "enum", KeyWord::kEnum, KeyProperty::kObject },
30 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
31 { "float", KeyWord::kFloat, KeyProperty::kNumber },
32 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
33 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
34 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
35 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
36 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
37 { "inline", KeyWord::kInline, KeyProperty::kModifier },
38 { "int", KeyWord::kInt, KeyProperty::kNumber },
39 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
40 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
41 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
42 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
43 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
44 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
45 { "static", KeyWord::kStatic, KeyProperty::kModifier },
46 { "struct", KeyWord::kStruct, KeyProperty::kObject },
47 { "template", KeyWord::kTemplate, KeyProperty::kObject },
48 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
49 { "typename", KeyWord::kTypename, KeyProperty::kObject },
50 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
51 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
52 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
53 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
54 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
55 { "union", KeyWord::kUnion, KeyProperty::kObject },
56 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
57 { "using", KeyWord::kUsing, KeyProperty::kObject },
58 { "void", KeyWord::kVoid, KeyProperty::kNumber },
59 };
60
61 const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
62
FindKey(const char * start,const char * end)63 KeyWord IncludeParser::FindKey(const char* start, const char* end) {
64 int ch = 0;
65 for (size_t index = 0; index < kKeyWordCount; ) {
66 if (start[ch] > kKeyWords[index].fName[ch]) {
67 ++index;
68 if (ch > 0 && (index == kKeyWordCount ||
69 kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
70 return KeyWord::kNone;
71 }
72 continue;
73 }
74 if (start[ch] < kKeyWords[index].fName[ch]) {
75 return KeyWord::kNone;
76 }
77 ++ch;
78 if (start + ch >= end) {
79 if (end - start < (int) strlen(kKeyWords[index].fName)) {
80 return KeyWord::kNone;
81 }
82 return kKeyWords[index].fKeyWord;
83 }
84 }
85 return KeyWord::kNone;
86 }
87
ValidateKeyWords()88 void IncludeParser::ValidateKeyWords() {
89 for (size_t index = 1; index < kKeyWordCount; ++index) {
90 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
91 == (int) kKeyWords[index].fKeyWord);
92 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
93 }
94 }
95
addKeyword(KeyWord keyWord)96 void IncludeParser::addKeyword(KeyWord keyWord) {
97 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
98 fIncludeWord = nullptr;
99 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
100 Definition* def = &fParent->fTokens.back();
101 this->addDefinition(def);
102 if (KeyWord::kEnum == fParent->fKeyWord) {
103 fInEnum = true;
104 }
105 }
106 }
107
looks_like_method(const TextParser & tp)108 static bool looks_like_method(const TextParser& tp) {
109 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
110 t.skipSpace();
111 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
112 && !t.skipExact("enum")) {
113 return true;
114 }
115 t.skipSpace();
116 if (t.skipExact("SK_API")) {
117 t.skipSpace();
118 }
119 if (!isupper(t.peek())) {
120 return true;
121 }
122 return nullptr != t.strnchr('(', t.fEnd);
123 }
124
looks_like_forward_declaration(const TextParser & tp)125 static bool looks_like_forward_declaration(const TextParser& tp) {
126 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
127 t.skipSpace();
128 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
129 && !t.skipExact("enum")) {
130 return false;
131 }
132 t.skipSpace();
133 if (t.skipExact("SK_API")) {
134 t.skipSpace();
135 }
136 if (!isupper(t.peek())) {
137 return false;
138 }
139 t.skipToNonAlphaNum();
140 if (t.eof() || ';' != t.next()) {
141 return false;
142 }
143 if (t.eof() || '\n' != t.next()) {
144 return false;
145 }
146 return t.eof();
147 }
148
looks_like_constructor(const TextParser & tp)149 static bool looks_like_constructor(const TextParser& tp) {
150 TextParser t(tp.fFileName, tp.fLine, tp.lineEnd(), tp.fLineCount);
151 t.skipSpace();
152 if (!isupper(t.peek())) {
153 if (':' == t.next() && ' ' >= t.peek()) {
154 return true;
155 }
156 return false;
157 }
158 t.skipToNonAlphaNum();
159 if ('(' != t.peek()) {
160 return false;
161 }
162 if (!t.skipToEndBracket(')')) {
163 return false;
164 }
165 SkAssertResult(')' == t.next());
166 t.skipSpace();
167 return tp.fChar == t.fChar;
168 }
169
looks_like_class_decl(const TextParser & tp)170 static bool looks_like_class_decl(const TextParser& tp) {
171 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
172 t.skipSpace();
173 if (!t.skipExact("class")) {
174 return false;
175 }
176 t.skipSpace();
177 if (t.skipExact("SK_API")) {
178 t.skipSpace();
179 }
180 if (!isupper(t.peek())) {
181 return false;
182 }
183 t.skipToNonAlphaNum();
184 return !t.skipToEndBracket('(');
185 }
186
looks_like_const(const TextParser & tp)187 static bool looks_like_const(const TextParser& tp) {
188 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
189 if (!t.startsWith("static constexpr ")) {
190 return false;
191 }
192 if (t.skipToEndBracket(" k")) {
193 SkAssertResult(t.skipExact(" k"));
194 } else if (t.skipToEndBracket(" SK_")) {
195 SkAssertResult(t.skipExact(" SK_"));
196 } else {
197 return false;
198 }
199 if (!isupper(t.peek())) {
200 return false;
201 }
202 return t.skipToEndBracket(" = ");
203 }
204
looks_like_member(const TextParser & tp)205 static bool looks_like_member(const TextParser& tp) {
206 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
207 const char* end = t.anyOf("(;");
208 if (!end || '(' == *end) {
209 return false;
210 }
211 bool foundMember = false;
212 do {
213 const char* next = t.anyOf(" ;");
214 if (';' == *next) {
215 break;
216 }
217 t.skipTo(next);
218 t.skipSpace();
219 foundMember = 'f' == t.fChar[0] && isupper(t.fChar[1]);
220 } while (true);
221 return foundMember;
222 }
223
skip_constructor_initializers(TextParser & t)224 static void skip_constructor_initializers(TextParser& t) {
225 SkAssertResult(':' == t.next());
226 do {
227 t.skipWhiteSpace();
228 t.skipToNonAlphaNum();
229 t.skipWhiteSpace();
230 if ('{' == t.peek()) {
231 t.skipToBalancedEndBracket('{', '}');
232 }
233 do {
234 const char* limiter = t.anyOf("(,{");
235 t.skipTo(limiter);
236 if ('(' != t.peek()) {
237 break;
238 }
239 t.skipToBalancedEndBracket('(', ')');
240 } while (true);
241 if ('{' == t.peek()) {
242 return;
243 }
244 SkAssertResult(',' == t.next());
245 } while (true);
246 }
247
248 static const char kInline[] = "inline ";
249 static const char kSK_API[] = "SK_API ";
250 static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
251
advanceInclude(TextParser & i)252 bool IncludeParser::advanceInclude(TextParser& i) {
253 if (!i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn)) {
254 return false;
255 }
256 if (fCheck.fPrivateBrace) {
257 if (i.startsWith("};")) {
258 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
259 fCheck.fPrivateBrace = 0;
260 fCheck.fDoubleReturn = true;
261 } else {
262 i.skipExact("};");
263 fCheck.fBraceCount -= 1;
264 }
265 return false;
266 }
267 if (i.startsWith("public:")) {
268 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
269 fCheck.fPrivateBrace = 0;
270 if (fCheck.fPrivateProtected) {
271 i.skipExact("public:");
272 }
273 } else {
274 i.skipExact("public:");
275 }
276 } else {
277 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
278 }
279 return false;
280 } else if (i.startsWith("};")) {
281 fCheck.fDoubleReturn = 2;
282 }
283 if (i.skipExact(kInline)) {
284 fCheck.fSkipInline = true;
285 return false;
286 }
287 if (i.skipExact(kSK_API)) {
288 fCheck.fSkipAPI = true;
289 return false;
290 }
291 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
292 fCheck.fSkipWarnUnused = true;
293 return false;
294 }
295 if (i.skipExact("SK_ATTR_DEPRECATED")) {
296 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
297 return false;
298 }
299 if (i.skipExact("SkDEBUGCODE")) {
300 i.skipWhiteSpace();
301 if ('(' != i.peek()) {
302 i.reportError("expected open paren");
303 }
304 TextParserSave save(&i);
305 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
306 fCheck.fInDebugCode = i.fChar - 1;
307 save.restore();
308 SkAssertResult('(' == i.next());
309 }
310 if ('{' == i.peek()) {
311 if (looks_like_method(i)) {
312 fCheck.fState = CheckCode::State::kMethod;
313 bool inBalance = false;
314 TextParser paren(i.fFileName, i.fStart, i.fEnd, i.fLineCount);
315 paren.skipToEndBracket('(');
316 paren.skipToBalancedEndBracket('(', ')');
317 inBalance = i.fChar < paren.fChar;
318 if (!inBalance) {
319 if (!i.skipToBalancedEndBracket('{', '}')) {
320 i.reportError("unbalanced open brace");
321 }
322 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
323 return false;
324 }
325 } else if (looks_like_class_decl(i)) {
326 fCheck.fState = CheckCode::State::kClassDeclaration;
327 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
328 fCheck.fPrivateProtected = false;
329 }
330 }
331 if (':' == i.peek() && looks_like_constructor(i)) {
332 fCheck.fState = CheckCode::State::kConstructor;
333 skip_constructor_initializers(i);
334 return false;
335 }
336 if ('#' == i.peek()) {
337 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
338 return false;
339 }
340 if (i.startsWith("//")) {
341 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
342 return false;
343 }
344 if (i.startsWith("/*")) {
345 i.skipToEndBracket("*/");
346 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
347 return false;
348 }
349 if (looks_like_forward_declaration(i)) {
350 fCheck.fState = CheckCode::State::kForwardDeclaration;
351 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
352 return false;
353 }
354 if (i.skipExact("private:") || i.skipExact("protected:")) {
355 if (!fCheck.fBraceCount) {
356 i.reportError("expect private in brace");
357 }
358 fCheck.fPrivateBrace = fCheck.fBraceCount;
359 fCheck.fPrivateProtected = true;
360 return false;
361 }
362 const char* funcEnd = i.anyOf("(\n");
363 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
364 && (i.contains("internal_", funcEnd, nullptr)
365 || i.contains("private_", funcEnd, nullptr)
366 || i.contains("legacy_", funcEnd, nullptr)
367 || i.contains("temporary_", funcEnd, nullptr))) {
368 i.skipTo(funcEnd);
369 if (!i.skipToBalancedEndBracket('(', ')')) {
370 i.reportError("unbalanced open parent");
371 }
372 i.skipSpace();
373 i.skipExact("const ");
374 i.skipSpace();
375 if (';' == i.peek()) {
376 i.next();
377 }
378 fCheck.fState = CheckCode::State::kNone;
379 return false;
380 }
381 return true;
382 }
383
codeBlockAppend(string & result,string s) const384 void IncludeParser::codeBlockAppend(string& result, string s) const {
385 for (char c : s) {
386 this->codeBlockAppend(result, c);
387 }
388 }
389
codeBlockAppend(string & result,char ch) const390 void IncludeParser::codeBlockAppend(string& result, char ch) const {
391 if (Elided::kYes == fElided && fCheck.fBraceCount) {
392 return;
393 }
394 this->stringAppend(result, ch);
395 }
396
codeBlockSpaces(string & result,int indent) const397 void IncludeParser::codeBlockSpaces(string& result, int indent) const {
398 if (!indent) {
399 return;
400 }
401 if (Elided::kYes == fElided && fCheck.fBraceCount) {
402 return;
403 }
404 SkASSERT(indent > 0);
405 if (fDebugWriteCodeBlock) {
406 SkDebugf("%*c", indent, ' ');
407 }
408 result.append(indent, ' ');
409 }
410
writeCodeBlock(const Definition & iDef)411 string IncludeParser::writeCodeBlock(const Definition& iDef) {
412 if (MarkType::kComment == iDef.fMarkType) {
413 return "";
414 }
415 if (iDef.fUndocumented) {
416 return "";
417 }
418 TextParser i(&iDef);
419 (void) i.skipExact("SkDEBUGCODE(");
420 if (MarkType::kConst == iDef.fMarkType && !i.fEnd) {
421 // TODO: end should have been set earlier
422 auto iter = iDef.fParent->fTokens.begin();
423 std::advance(iter, iDef.fParentIndex + 1);
424 SkASSERT(iter != iDef.fParent->fTokens.end());
425 i.fEnd = iter->fContentStart;
426 }
427 const char* loc;
428 if (MarkType::kMember == iDef.fMarkType) {
429 const char* parentEnd = iDef.fParent->fContentEnd;
430 TextParser newEnd(&iDef);
431 newEnd.fEnd = parentEnd;
432 const char* memberEnd = newEnd.anyOf(",};");
433 if (memberEnd && (';' == memberEnd[0] || ',' == memberEnd[0])) {
434 i.fEnd = memberEnd + 1;
435 }
436 }
437 if (i.contains("//", i.fEnd, &loc)) {
438 i.fEnd = loc;
439 }
440 if (i.contains("/*", i.fEnd, &loc)) {
441 i.fEnd = loc;
442 }
443 if (i.contains("{", i.fEnd, &loc)) {
444 bool inBalance = false;
445 if (MarkType::kMethod == iDef.fMarkType) {
446 TextParser paren(&iDef);
447 paren.skipToEndBracket('(');
448 paren.skipToBalancedEndBracket('(', ')');
449 inBalance = loc < paren.fChar;
450 }
451 if (!inBalance) {
452 i.fEnd = loc + 1;
453 while (i.fEnd < iDef.fContentEnd && ' ' >= i.fEnd[0]) {
454 ++i.fEnd;
455 }
456 }
457 }
458 while (i.fEnd > i.fStart && ' ' == i.fEnd[-1]) {
459 --i.fEnd;
460 }
461 const char* before = iDef.fContentStart;
462 while (' ' == *--before)
463 ;
464 int startIndent = iDef.fContentStart - before - 1;
465 bool saveDebugWriteBlock = fDebugWriteCodeBlock;
466 fDebugWriteCodeBlock = false;
467 string result = writeCodeBlock(i, iDef.fMarkType, startIndent);
468 fDebugWriteCodeBlock = saveDebugWriteBlock;
469 if (!result.empty()) {
470 if (MarkType::kNone != fPreviousMarkType && iDef.fMarkType != fPreviousMarkType
471 && ((MarkType::kEnum != fPreviousMarkType
472 && MarkType::kEnumClass != fPreviousMarkType)
473 || MarkType::kMember != iDef.fMarkType)
474 && (MarkType::kMember != fPreviousMarkType
475 || iDef.fParent == fPreviousDef->fParent)) {
476 result = "\n" + result;
477 }
478 if (fDebugWriteCodeBlock) {
479 SkDebugf("%s", result.c_str());
480 }
481 fPreviousDef = &iDef;
482 fPreviousMarkType = iDef.fMarkType;
483 }
484 for (auto& token : iDef.fTokens) {
485 result += this->writeCodeBlock(token);
486 }
487 if (MarkType::kEnum == iDef.fMarkType || MarkType::kEnumClass == iDef.fMarkType
488 || MarkType::kStruct == iDef.fMarkType || MarkType::kClass == iDef.fMarkType) {
489 this->codeBlockSpaces(result, startIndent);
490 this->codeBlockAppend(result, "};\n\n");
491 }
492 return result;
493 }
494
writeCodeBlock(TextParser & i,MarkType markType,int startIndent)495 string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
496 string result;
497 char last;
498 int lastIndent = 0;
499 bool lastDoubleMeUp = false;
500 fCheck.reset();
501 if (MarkType::kDefine == markType) {
502 result = "#define ";
503 } else {
504 this->codeBlockSpaces(result, startIndent);
505 }
506 do {
507 if (!this->advanceInclude(i)) {
508 continue;
509 }
510 do {
511 last = i.peek();
512 SkASSERT(' ' < last);
513 if (fCheck.fInDebugCode == i.fChar) {
514 fCheck.fInDebugCode = nullptr;
515 i.next(); // skip close paren
516 break;
517 }
518 if (CheckCode::State::kMethod == fCheck.fState) {
519 this->codeBlockAppend(result, ';');
520 fCheck.fState = CheckCode::State::kNone;
521 }
522 if (fCheck.fWriteReturn) {
523 this->codeBlockAppend(result, '\n');
524 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
525 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
526 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
527 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
528 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
529 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
530 this->codeBlockAppend(result, '\n');
531 }
532 fCheck.fTypedefReturn = false;
533 lastDoubleMeUp = doubleMeUp;
534 }
535 if (doubleMeUp) {
536 fCheck.fTypedefReturn = true;
537 }
538 lastIndent = fCheck.fIndent;
539 }
540 if (fCheck.fIndent) {
541 size_t indent = fCheck.fIndent;
542 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
543 indent -= sizeof(kInline) - 1;
544 }
545 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
546 indent -= sizeof(kSK_API) - 1;
547 }
548 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
549 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
550 }
551
552 this->codeBlockSpaces(result, indent);
553 }
554 this->codeBlockAppend(result, last);
555 fCheck.fWriteReturn = false;
556 fCheck.fIndent = 0;
557 fCheck.fBraceCount += '{' == last;
558 fCheck.fBraceCount -= '}' == last;
559 if (';' == last) {
560 fCheck.fSkipInline = false;
561 fCheck.fSkipAPI = false;
562 fCheck.fSkipWarnUnused = false;
563 }
564 if (fCheck.fBraceCount < 0) {
565 i.reportError("unbalanced close brace");
566 return result;
567 }
568 i.next();
569 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
570 } while (!i.eof());
571 if (CheckCode::State::kMethod == fCheck.fState) {
572 this->codeBlockAppend(result, ';');
573 }
574 bool elided = Elided::kYes == fElided;
575 bool elidedTemplate = elided && !strncmp(i.fStart, "template ", 9);
576 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
577 bool statementEnd = !result.empty() && (MarkType::kMethod == markType
578 || MarkType::kTypedef == markType || '}' == result.back());
579 bool semiEnd = !result.empty() && (',' == result.back() || ';' == result.back());
580 if (fCheck.fWriteReturn || elidedTClass) {
581 this->codeBlockAppend(result, '\n');
582 }
583 if (elided && ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass)) {
584 this->codeBlockAppend(result, '}');
585 statementEnd = true;
586 }
587 if (elided || statementEnd) {
588 this->codeBlockAppend(result, ";\n");
589 } else if (elidedTemplate || semiEnd) {
590 this->codeBlockAppend(result, '\n');
591 }
592 return result;
593 }
594
checkForMissingParams(const vector<string> & methodParams,const vector<string> & foundParams)595 void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
596 const vector<string>& foundParams) {
597 for (auto& methodParam : methodParams) {
598 bool found = false;
599 for (auto& foundParam : foundParams) {
600 if (methodParam == foundParam) {
601 found = true;
602 break;
603 }
604 }
605 if (!found) {
606 this->writeIncompleteTag("Param", methodParam, 2);
607 }
608 }
609 for (auto& foundParam : foundParams) {
610 bool found = false;
611 for (auto& methodParam : methodParams) {
612 if (methodParam == foundParam) {
613 found = true;
614 break;
615 }
616 }
617 if (!found) {
618 this->reportError("doxygen param does not match method declaration");
619 }
620 }
621 }
622
checkForWord()623 bool IncludeParser::checkForWord() {
624 if (!fIncludeWord) {
625 return true;
626 }
627 KeyWord keyWord = FindKey(fIncludeWord, fChar);
628 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
629 Bracket bracket = this->topBracket();
630 if (Bracket::kParen == bracket) {
631 return true;
632 }
633 }
634 if (KeyWord::kNone != keyWord) {
635 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
636 this->addKeyword(keyWord);
637 return true;
638 }
639 } else {
640 this->addWord();
641 return true;
642 }
643 Definition* poundDef = fParent;
644 if (!fParent) {
645 return reportError<bool>("expected parent");
646 }
647 if (Definition::Type::kBracket != poundDef->fType) {
648 return reportError<bool>("expected bracket");
649 }
650 if (Bracket::kPound != poundDef->fBracket) {
651 return reportError<bool>("expected preprocessor");
652 }
653 if (KeyWord::kNone != poundDef->fKeyWord) {
654 return reportError<bool>("already found keyword");
655 }
656 poundDef->fKeyWord = keyWord;
657 fIncludeWord = nullptr;
658 switch (keyWord) {
659 // these do not link to other # directives
660 case KeyWord::kDefine:
661 if (!fInBrace) {
662 SkASSERT(!fInDefine);
663 fInDefine = true;
664 }
665 case KeyWord::kInclude:
666 case KeyWord::kError:
667 break;
668 // these start a # directive link
669 case KeyWord::kIf:
670 case KeyWord::kIfdef:
671 case KeyWord::kIfndef:
672 break;
673 // these continue a # directive link
674 case KeyWord::kElif:
675 case KeyWord::kElse:
676 this->popObject(); // pop elif
677 if (Bracket::kPound != fParent->fBracket) {
678 return this->reportError<bool>("expected preprocessor directive");
679 }
680 this->popBracket(); // pop if
681 poundDef->fParent = fParent;
682 fParent = poundDef; // push elif back
683 break;
684 // this ends a # directive link
685 case KeyWord::kEndif:
686 // FIXME : should this be calling popBracket() instead?
687 this->popObject(); // pop endif
688 if (Bracket::kPound != fParent->fBracket) {
689 return this->reportError<bool>("expected preprocessor directive");
690 }
691 this->popBracket(); // pop if/else
692 break;
693 default:
694 SkASSERT(0);
695 }
696 return true;
697 }
698
className() const699 string IncludeParser::className() const {
700 string name(fParent->fName);
701 size_t slash = name.find_last_of("/");
702 if (string::npos == slash) {
703 slash = name.find_last_of("\\");
704 }
705 SkASSERT(string::npos != slash);
706 string result = name.substr(slash);
707 result = result.substr(1, result.size() - 3);
708 return result;
709 }
710
writeCodeBlock()711 void IncludeParser::writeCodeBlock() {
712 fElided = Elided::kNo;
713 for (auto& classMapper : fIClassMap) {
714 fPreviousMarkType = MarkType::kNone;
715 fPreviousDef = nullptr;
716 classMapper.second.fCode = this->writeCodeBlock(classMapper.second);
717 }
718 for (auto& enumMapper : fIEnumMap) {
719 fPreviousMarkType = MarkType::kNone;
720 fPreviousDef = nullptr;
721 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second);
722 }
723 for (auto& typedefMapper : fITypedefMap) {
724 fPreviousMarkType = MarkType::kNone;
725 fPreviousDef = nullptr;
726 typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second);
727 }
728 for (auto& defineMapper : fIDefineMap) {
729 fPreviousMarkType = MarkType::kNone;
730 fPreviousDef = nullptr;
731 defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second);
732 }
733 }
734
checkName(Definition * def)735 void IncludeParser::checkName(Definition* def) {
736 SkASSERT(!def->fName.empty());
737 TextParser parser(def->fFileName, &def->fName.front(), &def->fName.back() + 1, def->fLineCount);
738 const vector<string> skipWords = { "deprecated", "experimental", "internal", "private",
739 "legacy", "temporary" };
740 if (!parser.anyWord(skipWords, 0).empty()) {
741 def->fUndocumented = true;
742 }
743 }
744
745 #include <sstream>
746 #include <iostream>
747
checkTokens(list<Definition> & tokens,string key,string className,RootDefinition * root,BmhParser & bmhParser)748 void IncludeParser::checkTokens(list<Definition>& tokens, string key, string className,
749 RootDefinition* root, BmhParser& bmhParser) {
750 for (const auto& token : tokens) {
751 if (token.fPrivate) {
752 continue;
753 }
754 string fullName = key + "::" + token.fName;
755 const Definition* def = nullptr;
756 if (root) {
757 def = root->find(fullName, RootDefinition::AllowParens::kYes);
758 }
759 switch (token.fMarkType) {
760 case MarkType::kMethod: {
761 if (this->isInternalName(token)) {
762 continue;
763 }
764 if (!root) {
765 if (token.fUndocumented) {
766 break;
767 }
768 auto methIter = bmhParser.fMethodMap.find(token.fName);
769 if (bmhParser.fMethodMap.end() != methIter) {
770 def = &methIter->second;
771 if (def->crossCheck2(token)) {
772 def->fVisited = true;
773 } else {
774 this->suggestFix(Suggest::kMethodDiffers, token, root, def);
775 fFailed = true;
776 }
777 } else {
778 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
779 fFailed = true;
780 }
781 break;
782 }
783 if (!def) {
784 string paramName = className + "::";
785 paramName += string(token.fContentStart,
786 token.fContentEnd - token.fContentStart);
787 if (string::npos != paramName.find('\n')) {
788 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
789 paramName.end());
790 }
791 def = root->find(paramName, RootDefinition::AllowParens::kYes);
792 if (!def && 0 == token.fName.find("operator")) {
793 string operatorName = className + "::";
794 TextParser oper("", token.fStart, token.fContentEnd, 0);
795 const char* start = oper.strnstr("operator", token.fContentEnd);
796 SkASSERT(start);
797 oper.skipTo(start);
798 oper.skipToEndBracket('(');
799 int parens = 0;
800 do {
801 if ('(' == oper.peek()) {
802 ++parens;
803 } else if (')' == oper.peek()) {
804 --parens;
805 }
806 } while (!oper.eof() && oper.next() && parens > 0);
807 operatorName += string(start, oper.fChar - start);
808 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
809 }
810 }
811 if (!def) {
812 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
813 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
814 const char* tokenEnd = token.methodEnd();
815 string constructorName = className + "::";
816 constructorName += string(token.fContentStart + skip,
817 tokenEnd - token.fContentStart - skip);
818 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
819 }
820 if (!def && 0 == token.fName.find("SK_")) {
821 string incName = token.fName + "()";
822 string macroName = className + "::" + incName;
823 def = root->find(macroName, RootDefinition::AllowParens::kYes);
824 if (def) {
825 if (def->fName == incName) {
826 def->fVisited = true;
827 if ("SK_TO_STRING_NONVIRT" == token.fName) {
828 def = root->find(className + "::toString",
829 RootDefinition::AllowParens::kYes);
830 if (def) {
831 def->fVisited = true;
832 } else {
833 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
834 fFailed = true;
835 }
836 }
837 break;
838 } else {
839 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
840 fFailed = true;
841 }
842 }
843 }
844 if (!def) {
845 bool allLower = true;
846 for (size_t index = 0; index < token.fName.length(); ++index) {
847 if (!islower(token.fName[index])) {
848 allLower = false;
849 break;
850 }
851 }
852 if (allLower) {
853 string lowerName = className + "::" + token.fName + "()";
854 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
855 }
856 }
857 if (!def) {
858 if (0 == token.fName.find("SkDEBUGCODE")) {
859 break;
860 }
861 }
862 if (!def) {
863 // simple method names inside nested classes have a bug and are missing trailing parens
864 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
865 def = root->find(withParens, RootDefinition::AllowParens::kNo);
866 }
867 if (!def) {
868 if (!token.fUndocumented) {
869 this->suggestFix(Suggest::kMethodMissing, token, root, nullptr);
870 fFailed = true;
871 }
872 break;
873 }
874 if (token.fUndocumented) {
875 // we can't report an error yet; if bmh documents this unnecessarily,
876 // we'll detect that later. It may be that def points to similar
877 // documented function.
878 break;
879 }
880 if (def->crossCheck2(token)) {
881 def->fVisited = true;
882 } else {
883 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
884 fFailed = true;
885 }
886 } break;
887 case MarkType::kComment:
888 break;
889 case MarkType::kEnumClass:
890 case MarkType::kEnum: {
891 if (!def) {
892 // work backwards from first word to deduce #Enum name
893 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
894 SkAssertResult(firstMember.skipName("enum"));
895 SkAssertResult(firstMember.skipToEndBracket('{'));
896 firstMember.next();
897 firstMember.skipWhiteSpace();
898 SkASSERT('k' == firstMember.peek());
899 const char* savePos = firstMember.fChar;
900 firstMember.skipToNonName();
901 const char* wordEnd = firstMember.fChar;
902 firstMember.fChar = savePos;
903 const char* lastUnderscore = nullptr;
904 do {
905 if (!firstMember.skipToEndBracket('_')) {
906 break;
907 }
908 if (firstMember.fChar > wordEnd) {
909 break;
910 }
911 lastUnderscore = firstMember.fChar;
912 } while (firstMember.next());
913 if (lastUnderscore) {
914 ++lastUnderscore;
915 string enumName(lastUnderscore, wordEnd - lastUnderscore);
916 if (root) {
917 string anonName = className + "::" + enumName + 's';
918 def = root->find(anonName, RootDefinition::AllowParens::kYes);
919 } else {
920 auto enumIter = bmhParser.fEnumMap.find(enumName);
921 if (bmhParser.fEnumMap.end() != enumIter) {
922 RootDefinition* rootDef = &enumIter->second;
923 def = rootDef;
924 }
925 }
926 }
927 if (!def && !root) {
928 auto enumIter = bmhParser.fEnumMap.find(token.fName);
929 if (bmhParser.fEnumMap.end() != enumIter) {
930 def = &enumIter->second;
931 }
932 if (!def) {
933 auto enumClassIter = bmhParser.fClassMap.find(token.fName);
934 if (bmhParser.fClassMap.end() != enumClassIter) {
935 def = &enumClassIter->second;
936 }
937 }
938 }
939 if (!def) {
940 if (!token.fUndocumented) {
941 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
942 fFailed = true;
943 }
944 break;
945 }
946 }
947 def->fVisited = true;
948 bool hasCode = false;
949 bool hasPopulate = true;
950 for (auto& child : def->fChildren) {
951 if (MarkType::kCode == child->fMarkType) {
952 hasPopulate = std::any_of(child->fChildren.begin(),
953 child->fChildren.end(), [](auto grandChild){
954 return MarkType::kPopulate == grandChild->fMarkType; });
955 if (!hasPopulate) {
956 def = child;
957 }
958 hasCode = true;
959 break;
960 }
961 }
962 if (!hasCode && !root) {
963 const Definition* topic = def->topicParent();
964 hasCode = std::any_of(topic->fChildren.begin(), topic->fChildren.end(),
965 [](Definition* def){ return MarkType::kCode == def->fMarkType
966 && def->fChildren.size() > 0 && MarkType::kPopulate ==
967 def->fChildren.front()->fMarkType; });
968 }
969 if (!hasCode) {
970 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
971 fFailed = true;
972 break;
973 }
974 if (!hasPopulate) {
975 if (def->crossCheck(token)) {
976 def->fVisited = true;
977 } else {
978 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
979 fFailed = true;
980 }
981 }
982 for (auto& member : token.fTokens) {
983 if (MarkType::kMember != member.fMarkType) {
984 continue;
985 }
986 string constName = MarkType::kEnumClass == token.fMarkType ?
987 fullName : className;
988 if (root) {
989 constName += "::" + member.fName;
990 def = root->find(constName, RootDefinition::AllowParens::kYes);
991 } else {
992 auto enumMapper = bmhParser.fEnumMap.find(token.fName);
993 if (bmhParser.fEnumMap.end() != enumMapper) {
994 auto& enumDoc = enumMapper->second;
995 auto memberIter = enumDoc.fLeaves.find(member.fName);
996 if (enumDoc.fLeaves.end() != memberIter) {
997 def = &memberIter->second;
998 }
999 }
1000 }
1001 if (!def) {
1002 string innerName = key + "::" + member.fName;
1003 def = root->find(innerName, RootDefinition::AllowParens::kYes);
1004 }
1005 if (!def) {
1006 if (!member.fUndocumented) {
1007 SkDebugf("const missing from bmh: %s\n", constName.c_str());
1008 fFailed = true;
1009 }
1010 } else {
1011 def->fVisited = true;
1012 }
1013 }
1014 } break;
1015 case MarkType::kMember:
1016 if (def) {
1017 def->fVisited = true;
1018 } else {
1019 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
1020 fFailed = true;
1021 }
1022 break;
1023 case MarkType::kTypedef:
1024 if (!def && !root) {
1025 auto typedefIter = bmhParser.fTypedefMap.find(token.fName);
1026 if (bmhParser.fTypedefMap.end() != typedefIter) {
1027 def = &typedefIter->second;
1028 }
1029 }
1030 if (def) {
1031 def->fVisited = true;
1032 } else {
1033 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
1034 fFailed = true;
1035 }
1036 break;
1037 case MarkType::kConst:
1038 if (!def && !root) {
1039 auto constIter = bmhParser.fConstMap.find(token.fName);
1040 if (bmhParser.fConstMap.end() != constIter) {
1041 def = &constIter->second;
1042 }
1043 }
1044 if (def) {
1045 def->fVisited = true;
1046 } else {
1047 if (!token.fUndocumented) {
1048 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
1049 fFailed = true;
1050 }
1051 }
1052 break;
1053 case MarkType::kDefine:
1054 // TODO: incomplete
1055 break;
1056 default:
1057 SkASSERT(0); // unhandled
1058 break;
1059 }
1060 }
1061 }
1062
crossCheck(BmhParser & bmhParser)1063 bool IncludeParser::crossCheck(BmhParser& bmhParser) {
1064 for (auto& classMapper : fIClassMap) {
1065 string className = classMapper.first;
1066 auto finder = bmhParser.fClassMap.find(className);
1067 if (bmhParser.fClassMap.end() == finder) {
1068 SkASSERT(string::npos != className.find("::"));
1069 continue;
1070 }
1071 }
1072 for (auto& classMapper : fIClassMap) {
1073 if (classMapper.second.fUndocumented) {
1074 continue;
1075 }
1076 string className = classMapper.first;
1077 std::istringstream iss(className);
1078 string classStr;
1079 string classBase;
1080 RootDefinition* root = nullptr;
1081 while (std::getline(iss, classStr, ':')) {
1082 if (root) {
1083 if (!classStr.length()) {
1084 continue;
1085 }
1086 classBase += "::" + classStr;
1087 auto finder = root->fBranches.find(classBase);
1088 if (root->fBranches.end() != finder) {
1089 root = finder->second;
1090 } else {
1091 SkASSERT(0);
1092 }
1093 } else {
1094 classBase = classStr;
1095 auto finder = bmhParser.fClassMap.find(classBase);
1096 if (bmhParser.fClassMap.end() != finder) {
1097 root = &finder->second;
1098 } else {
1099 SkASSERT(0);
1100 }
1101 }
1102 }
1103 this->checkTokens(classMapper.second.fTokens, classMapper.first, className, root,
1104 bmhParser);
1105 }
1106 this->checkTokens(fGlobals, "", "", nullptr, bmhParser);
1107 int crossChecks = 0;
1108 string firstCheck;
1109 for (auto& classMapper : fIClassMap) {
1110 string className = classMapper.first;
1111 auto finder = bmhParser.fClassMap.find(className);
1112 if (bmhParser.fClassMap.end() == finder) {
1113 continue;
1114 }
1115 RootDefinition* root = &finder->second;
1116 if (!root->dumpUnVisited()) {
1117 fFailed = true;
1118 }
1119 if (crossChecks) {
1120 SkDebugf(".");
1121 } else {
1122 SkDebugf("cross-check");
1123 firstCheck = className;
1124 }
1125 ++crossChecks;
1126 }
1127 if (crossChecks) {
1128 if (1 == crossChecks) {
1129 SkDebugf(" %s", firstCheck.c_str());
1130 }
1131 SkDebugf("\n");
1132 }
1133 bmhParser.fWroteOut = true;
1134 return !fFailed;
1135 }
1136
defineClass(const Definition & includeDef,string name)1137 IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
1138 string name) {
1139 string className;
1140 const Definition* test = fParent;
1141 while (Definition::Type::kFileType != test->fType) {
1142 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
1143 className = test->fName + "::";
1144 break;
1145 }
1146 test = test->fParent;
1147 }
1148 className += name;
1149 unordered_map<string, IClassDefinition>& map = fIClassMap;
1150 IClassDefinition& markupDef = map[className];
1151 if (markupDef.fStart) {
1152 typedef IClassDefinition* IClassDefPtr;
1153 return INHERITED::reportError<IClassDefPtr>("class already defined");
1154 }
1155 markupDef.fFileName = fFileName;
1156 markupDef.fStart = includeDef.fStart;
1157 markupDef.fContentStart = includeDef.fStart;
1158 markupDef.fName = className;
1159 this->checkName(&markupDef);
1160 markupDef.fContentEnd = includeDef.fContentEnd;
1161 markupDef.fTerminator = includeDef.fTerminator;
1162 markupDef.fParent = fParent;
1163 markupDef.fLineCount = fLineCount;
1164 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
1165 MarkType::kStruct : MarkType::kClass;
1166 markupDef.fKeyWord = includeDef.fKeyWord;
1167 markupDef.fType = Definition::Type::kMark;
1168 auto tokenIter = includeDef.fParent->fTokens.begin();
1169 SkASSERT(includeDef.fParentIndex > 0);
1170 std::advance(tokenIter, includeDef.fParentIndex - 1);
1171 const Definition* priorComment = &*tokenIter;
1172 markupDef.fUndocumented = priorComment->fUndocumented;
1173 fParent = &markupDef;
1174 return &markupDef;
1175 }
1176
dumpClassTokens(IClassDefinition & classDef)1177 void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
1178 auto& tokens = classDef.fTokens;
1179 bool wroteTail = true;
1180 for (auto& token : tokens) {
1181 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
1182 continue;
1183 }
1184 if (wroteTail && MarkType::kMember != token.fMarkType) {
1185 this->writeBlockSeparator();
1186 }
1187 switch (token.fMarkType) {
1188 case MarkType::kConst:
1189 this->dumpConst(token, classDef.fName);
1190 break;
1191 case MarkType::kEnum:
1192 case MarkType::kEnumClass:
1193 this->dumpEnum(token, token.fName);
1194 break;
1195 case MarkType::kMethod:
1196 if (!this->dumpMethod(token, classDef.fName)) {
1197 wroteTail = false;
1198 continue;
1199 }
1200 break;
1201 case MarkType::kMember:
1202 this->dumpMember(token);
1203 continue;
1204 break;
1205 case MarkType::kTypedef:
1206 this->dumpTypedef(token, classDef.fName);
1207 break;
1208 default:
1209 SkASSERT(0);
1210 }
1211 this->dumpCommonTail(token);
1212 wroteTail = true;
1213 }
1214 }
dumpComment(const Definition & token)1215 void IncludeParser::dumpComment(const Definition& token) {
1216 fLineCount = token.fLineCount;
1217 fChar = fLine = token.fContentStart;
1218 fEnd = token.fContentEnd;
1219 if (MarkType::kMethod == token.fMarkType) {
1220 this->lf(2);
1221 this->writeTag("Populate");
1222 this->lf(2);
1223 return;
1224 }
1225 for (const auto& child : token.fTokens) {
1226 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1227 break;
1228 }
1229 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
1230 if (child.fPrivate) {
1231 break;
1232 }
1233 if (child.length() > 1) {
1234 const char* start = child.fContentStart;
1235 ptrdiff_t length = child.fContentEnd - start;
1236 SkASSERT(length >= 0);
1237 while (length && '/' == start[0]) {
1238 start += 1;
1239 --length;
1240 }
1241 while (length && '/' == start[length - 1]) {
1242 length -= 1;
1243 if (length && '*' == start[length - 1]) {
1244 length -= 1;
1245 }
1246 }
1247 if (length) {
1248 this->lf(2);
1249 if ("!< " == string(start, length).substr(0, 3)) {
1250 return;
1251 }
1252 this->writeBlock(length, start);
1253 this->lf(2);
1254 }
1255 }
1256 }
1257 }
1258 }
1259
dumpCommonTail(const Definition & token)1260 void IncludeParser::dumpCommonTail(const Definition& token) {
1261 this->lf(2);
1262 this->writeTag("Example");
1263 this->lf(1);
1264 this->writeString("// incomplete");
1265 this->lf(1);
1266 this->writeEndTag();
1267 this->lf(2);
1268 this->writeTag("SeeAlso");
1269 this->writeSpace();
1270 this->writeString("incomplete");
1271 this->lf(2);
1272 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1273 this->lf(2);
1274 }
1275
dumpConst(const Definition & token,string className)1276 void IncludeParser::dumpConst(const Definition& token, string className) {
1277 this->writeTag("Const");
1278 this->writeSpace();
1279 this->writeString(token.fName);
1280 this->writeTagTable("Line", "incomplete");
1281 this->lf(2);
1282 this->dumpComment(token);
1283 }
1284
dumpDefine(const Definition & token)1285 void IncludeParser::dumpDefine(const Definition& token) {
1286 this->writeTag("Define", token.fName);
1287 this->lf(2);
1288 this->writeTag("Code");
1289 this->lfAlways(1);
1290 this->writeString("###$");
1291 this->lfAlways(1);
1292 this->indentToColumn(4);
1293 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1294 this->lf(1);
1295 this->indentToColumn(0);
1296 this->writeString("$$$#");
1297
1298 this->writeEndTag();
1299 this->lf(2);
1300 this->dumpComment(token);
1301 for (auto& child : token.fTokens) {
1302 if (MarkType::kComment == child.fMarkType) {
1303 continue;
1304 }
1305 this->writeTag("Param", child.fName);
1306 this->writeSpace();
1307 this->writeString("incomplete");
1308 this->writeSpace();
1309 this->writeString("##");
1310 this->lf(1);
1311 }
1312 }
1313
dumpEnum(const Definition & token,string name)1314 void IncludeParser::dumpEnum(const Definition& token, string name) {
1315 string tagType(MarkType::kEnum == token.fMarkType ? "Enum" : "EnumClass");
1316 this->writeTag(tagType.c_str(), token.fName);
1317 this->lf(2);
1318 this->writeTag("Code");
1319 this->writeTag("Populate");
1320 this->writeEndTag();
1321 this->lf(2);
1322 this->dumpComment(token);
1323 string prior;
1324 for (auto& child : token.fTokens) {
1325 if (MarkType::kComment == child.fMarkType) {
1326 prior = string(child.fContentStart, child.length());
1327 }
1328 if (MarkType::kMember != child.fMarkType) {
1329 continue;
1330 }
1331 this->writeTag("Const");
1332 this->writeSpace();
1333 this->writeString(child.fName);
1334 this->writeSpace(2);
1335 this->writeString("0 # incomplete; replace '0' with member value");
1336 this->lf(1);
1337 this->writeTagNoLF("Line", "#");
1338 this->writeSpace();
1339 if ("/!< " == prior.substr(0, 4)) {
1340 this->writeString(prior.substr(4));
1341 } else {
1342 this->writeString("incomplete");
1343 }
1344 this->writeSpace();
1345 this->writeString("##");
1346 this->lf(1);
1347 this->writeString("# incomplete; add description or delete");
1348 this->writeEndTag();
1349 }
1350 this->lf(2);
1351 this->writeString("# incomplete; add description or delete");
1352 this->lf(2);
1353 }
1354
dumpGlobals(string * globalFileName,long int * globalTell)1355 bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1356 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1357 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1358 if (!hasGlobals) {
1359 return true;
1360 }
1361 size_t lastBSlash = fFileName.rfind('\\');
1362 size_t lastSlash = fFileName.rfind('/');
1363 size_t lastDotH = fFileName.rfind(".h");
1364 SkASSERT(string::npos != lastDotH);
1365 if (string::npos != lastBSlash && (string::npos == lastSlash
1366 || lastBSlash < lastSlash)) {
1367 lastSlash = lastBSlash;
1368 } else if (string::npos == lastSlash) {
1369 lastSlash = -1;
1370 }
1371 lastSlash += 1;
1372 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1373 string fileName = globalsName + "_Reference.bmh";
1374 *globalFileName = fileName;
1375 fOut = fopen(fileName.c_str(), "wb");
1376 if (!fOut) {
1377 SkDebugf("could not open output file %s\n", globalsName.c_str());
1378 return false;
1379 }
1380 string prefixName = globalsName.substr(0, 2);
1381 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1382 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1383 this->writeTagNoLF("Topic", topicName);
1384 this->writeEndTag("Alias", topicName + "_Reference");
1385 this->lf(2);
1386 if (!fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1387 || !fITemplateMap.empty() || !fITypedefMap.empty() || !fIUnionMap.empty()) {
1388 this->writeTag("Code");
1389 this->writeTag("Populate");
1390 this->writeEndTag();
1391 this->lf(2);
1392 }
1393 std::map<int, Definition*> sortedDefs;
1394 for (const auto& entry : fIDefineMap) {
1395 sortedDefs[entry.second->fLineCount] = entry.second;
1396 }
1397 for (const auto& entry : fIFunctionMap) {
1398 sortedDefs[entry.second->fLineCount] = entry.second;
1399 }
1400 for (const auto& entry : fIEnumMap) {
1401 if (string::npos == entry.first.find("::")) {
1402 sortedDefs[entry.second->fLineCount] = entry.second;
1403 }
1404 }
1405 for (const auto& entry : fITemplateMap) {
1406 sortedDefs[entry.second->fLineCount] = entry.second;
1407 }
1408 for (const auto& entry : fITypedefMap) {
1409 sortedDefs[entry.second->fLineCount] = entry.second;
1410 }
1411 for (const auto& entry : fIUnionMap) {
1412 sortedDefs[entry.second->fLineCount] = entry.second;
1413 }
1414 for (const auto& entry : sortedDefs) {
1415 const Definition* def = entry.second;
1416 this->writeBlockSeparator();
1417 switch (def->fMarkType) {
1418 case MarkType::kDefine:
1419 this->dumpDefine(*def);
1420 break;
1421 case MarkType::kMethod:
1422 if (!this->dumpMethod(*def, globalsName)) {
1423 continue;
1424 }
1425 break;
1426 case MarkType::kEnum:
1427 case MarkType::kEnumClass:
1428 this->dumpEnum(*def, globalsName);
1429 break;
1430 case MarkType::kTemplate:
1431 SkASSERT(0); // incomplete
1432 break;
1433 case MarkType::kTypedef: {
1434 this->writeTag("Typedef");
1435 this->writeSpace();
1436 TextParser parser(def);
1437 if (!parser.skipExact("typedef")) {
1438 return false;
1439 }
1440 if (!parser.skipSpace()) {
1441 return false;
1442 }
1443 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1444 this->lf(2);
1445 this->dumpComment(*def);
1446 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1447 this->lf(2);
1448 } continue;
1449 case MarkType::kUnion:
1450 SkASSERT(0); // incomplete
1451 break;
1452 default:
1453 SkASSERT(0);
1454 }
1455 this->dumpCommonTail(*def);
1456 }
1457 *globalTell = ftell(fOut);
1458 this->writeEndTag("Topic", topicName);
1459 this->lfAlways(1);
1460 // fclose(fOut); // defer closing in case class needs to be also written here
1461 return true;
1462 }
1463
isClone(const Definition & token)1464 bool IncludeParser::isClone(const Definition& token) {
1465 string name = token.fName;
1466 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1467 }
1468
isConstructor(const Definition & token,string className)1469 bool IncludeParser::isConstructor(const Definition& token, string className) {
1470 string name = token.fName;
1471 return 0 == name.find(className) || '~' == name[0];
1472 }
1473
isInternalName(const Definition & token)1474 bool IncludeParser::isInternalName(const Definition& token) {
1475 string name = token.fName;
1476 // exception for this SkCanvas function .. for now
1477 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1478 return false;
1479 }
1480 return name.substr(0, 7) == "android"
1481 || 0 == token.fName.find("internal_")
1482 || 0 == token.fName.find("Internal_")
1483 || 0 == token.fName.find("legacy_")
1484 || 0 == token.fName.find("temporary_")
1485 || 0 == token.fName.find("private_");
1486 }
1487
isMember(const Definition & token) const1488 bool IncludeParser::isMember(const Definition& token) const {
1489 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1490 return true;
1491 }
1492 if (!islower(token.fStart[0])) {
1493 return false;
1494 }
1495 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1496 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1497 const Definition* structToken = token.fParent;
1498 if (!structToken) {
1499 return false;
1500 }
1501 if (KeyWord::kStruct != structToken->fKeyWord) {
1502 structToken = token.fParent->fParent;
1503 if (!structToken) {
1504 return false;
1505 }
1506 if (KeyWord::kStruct != structToken->fKeyWord) {
1507 return false;
1508 }
1509 }
1510 SkASSERT(structToken->fTokens.size() > 0);
1511 const Definition& child = structToken->fTokens.front();
1512 string structName(child.fContentStart, child.length());
1513 if ("RunBuffer" != structName) {
1514 return false;
1515 }
1516 string tokenName(token.fContentStart, token.length());
1517 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1518 for (auto allow : allowed) {
1519 if (allow == tokenName) {
1520 return true;
1521 }
1522 }
1523 }
1524 return false;
1525 }
1526
isOperator(const Definition & token)1527 bool IncludeParser::isOperator(const Definition& token) {
1528 return "operator" == token.fName.substr(0, 8);
1529 }
1530
dumpMethod(const Definition & token,string className)1531 bool IncludeParser::dumpMethod(const Definition& token, string className) {
1532 if (std::any_of(token.fTokens.begin(), token.fTokens.end(),
1533 [=](const Definition& def) { return MarkType::kComment == def.fMarkType
1534 && this->isUndocumentable(def.fFileName, def.fContentStart, def.fContentEnd,
1535 def.fLineCount); } )) {
1536 return false;
1537 }
1538 this->writeTag("Method");
1539 this->writeSpace();
1540
1541 string name = string(token.fStart ? token.fStart : token.fContentStart,
1542 token.length());
1543 this->writeBlock((int) name.size(), name.c_str());
1544 string inType;
1545 if (this->isConstructor(token, className)) {
1546 inType = "Constructor";
1547 } else if (this->isOperator(token)) {
1548 inType = "Operator";
1549 } else {
1550 inType = "incomplete";
1551 }
1552 this->writeTag("In", inType);
1553 this->writeTagTable("Line", "incomplete");
1554 this->lf(2);
1555 this->dumpComment(token);
1556 return true;
1557 }
1558
dumpMember(const Definition & token)1559 void IncludeParser::dumpMember(const Definition& token) {
1560 this->writeTag("Member");
1561 this->writeSpace();
1562 this->writeDefinition(token, token.fName, 2);
1563 lf(1);
1564 for (auto child : token.fChildren) {
1565 this->writeDefinition(*child);
1566 }
1567 this->writeEndTag();
1568 lf(2);
1569 }
1570
dumpTokens()1571 bool IncludeParser::dumpTokens() {
1572 string globalFileName;
1573 long int globalTell = 0;
1574 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
1575 return false;
1576 }
1577 for (const auto& member : fIClassMap) {
1578 if (string::npos != member.first.find("::")) {
1579 continue;
1580 }
1581 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
1582 return false;
1583 }
1584 }
1585 if (globalTell) {
1586 fclose(fOut);
1587 SkDebugf("wrote %s\n", globalFileName.c_str());
1588 }
1589 return true;
1590 }
1591
1592 // dump equivalent markup
dumpTokens(string skClassName,string globalFileName,long int * globalTell)1593 bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
1594 string fileName = skClassName + "_Reference.bmh";
1595 if (globalFileName != fileName) {
1596 fOut = fopen(fileName.c_str(), "wb");
1597 if (!fOut) {
1598 SkDebugf("could not open output file %s\n", fileName.c_str());
1599 return false;
1600 }
1601 } else {
1602 fseek(fOut, *globalTell, SEEK_SET);
1603 this->lf(2);
1604 this->writeBlockSeparator();
1605 *globalTell = 0;
1606 }
1607 string prefixName = skClassName.substr(0, 2);
1608 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1609 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
1610 if (globalFileName != fileName) {
1611 this->writeTagNoLF("Topic", topicName);
1612 this->writeEndTag("Alias", topicName + "_Reference");
1613 this->lf(2);
1614 }
1615 auto& classMap = fIClassMap[skClassName];
1616 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1617 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
1618 this->writeTag(containerType, skClassName);
1619 this->lf(2);
1620 auto& tokens = classMap.fTokens;
1621 for (auto& token : tokens) {
1622 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1623 continue;
1624 }
1625 this->writeDefinition(token);
1626 this->lf(1);
1627 }
1628 this->lf(2);
1629 this->writeTag("Code");
1630 this->writeTag("Populate");
1631 this->writeEndTag();
1632 this->lf(2);
1633 for (auto& oneClass : fIClassMap) {
1634 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1635 continue;
1636 }
1637 string innerName = oneClass.first.substr(skClassName.length() + 2);
1638 this->writeBlockSeparator();
1639 KeyWord keyword = oneClass.second.fKeyWord;
1640 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1641 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
1642 this->writeTag(containerType, innerName);
1643 this->writeTagTable("Line", "incomplete");
1644 this->lf(2);
1645 this->writeTag("Code");
1646 this->writeEndTag("ToDo", "fill this in manually");
1647 this->writeEndTag();
1648 this->lf(2);
1649 for (auto& token : oneClass.second.fTokens) {
1650 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1651 continue;
1652 }
1653 this->writeDefinition(token);
1654 }
1655 this->lf(2);
1656 this->dumpClassTokens(oneClass.second);
1657 this->lf(2);
1658 this->writeEndTag(containerType, innerName);
1659 this->lf(2);
1660 }
1661 this->dumpClassTokens(classMap);
1662 this->writeEndTag(containerType, skClassName);
1663 this->lf(2);
1664 this->writeEndTag("Topic", topicName);
1665 this->lfAlways(1);
1666 fclose(fOut);
1667 SkDebugf("wrote %s\n", fileName.c_str());
1668 return true;
1669 }
1670
dumpTypedef(const Definition & token,string className)1671 void IncludeParser::dumpTypedef(const Definition& token, string className) {
1672 this->writeTag("Typedef");
1673 this->writeSpace();
1674 this->writeString(token.fName);
1675 this->writeTagTable("Line", "incomplete");
1676 this->lf(2);
1677 this->dumpComment(token);
1678 }
1679
elidedCodeBlock(const Definition & iDef)1680 string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1681 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1682 || KeyWord::kTemplate == iDef.fKeyWord);
1683 TextParser i(&iDef);
1684 fElided = Elided::kYes;
1685 MarkType markType = MarkType::kClass;
1686 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1687 for (auto child : iDef.fChildren) {
1688 if (MarkType::kMethod == child->fMarkType) {
1689 markType = MarkType::kFunction;
1690 break;
1691 }
1692 }
1693 }
1694 return this->writeCodeBlock(i, markType, 0);
1695 }
1696
filteredBlock(string inContents,string filterContents)1697 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1698 string result;
1699 const unordered_map<string, Definition*>* mapPtr = nullptr;
1700 if ("Constant" == inContents) {
1701 mapPtr = &fIConstMap;
1702 } else {
1703 SkASSERT(0); // only Constant supported for now
1704 }
1705 vector<Definition*> consts;
1706 for (auto entry : *mapPtr) {
1707 if (string::npos == entry.first.find(filterContents)) {
1708 continue;
1709 }
1710 consts.push_back(entry.second);
1711 }
1712 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1713 return def1->fLineCount < def2->fLineCount;
1714 } );
1715 for (auto oneConst : consts) {
1716 result += this->writeCodeBlock(*oneConst);
1717 }
1718 return result;
1719 }
1720
findCommentAfter(const Definition & includeDef,Definition * markupDef)1721 bool IncludeParser::findCommentAfter(const Definition& includeDef, Definition* markupDef) {
1722 this->checkName(markupDef);
1723 const Definition* parent = includeDef.fParent;
1724 int index = includeDef.fParentIndex;
1725 auto wordIter = parent->fTokens.begin();
1726 std::advance(wordIter, index);
1727 SkASSERT(&*wordIter == &includeDef);
1728 size_t commentLine = 0;
1729 do {
1730 wordIter = std::next(wordIter);
1731 if (parent->fTokens.end() == wordIter) {
1732 break;
1733 }
1734 commentLine = wordIter->fLineCount;
1735 } while (Punctuation::kSemicolon != wordIter->fPunctuation);
1736 wordIter = std::next(wordIter);
1737 if (parent->fTokens.end() != wordIter && Bracket::kSlashSlash == wordIter->fBracket
1738 && wordIter->fLineCount == commentLine) {
1739 return this->parseComment(wordIter->fFileName, wordIter->fContentStart,
1740 wordIter->fContentEnd, wordIter->fLineCount, markupDef, &markupDef->fUndocumented);
1741 }
1742 return true;
1743 }
1744
findComments(const Definition & includeDef,Definition * markupDef)1745 bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1746 this->checkName(markupDef);
1747 // add comment preceding class, if any
1748 Definition* parent = includeDef.fParent;
1749 int index = includeDef.fParentIndex;
1750 auto wordIter = parent->fTokens.begin();
1751 std::advance(wordIter, index);
1752 SkASSERT(&*wordIter == &includeDef);
1753 while (parent->fTokens.begin() != wordIter) {
1754 auto testIter = std::prev(wordIter);
1755 if (Definition::Type::kWord != testIter->fType
1756 && Definition::Type::kKeyWord != testIter->fType
1757 && (Definition::Type::kBracket != testIter->fType
1758 || Bracket::kAngle != testIter->fBracket)
1759 && (Definition::Type::kPunctuation != testIter->fType
1760 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1761 break;
1762 }
1763 wordIter = testIter;
1764 }
1765 auto commentIter = wordIter;
1766 while (parent->fTokens.begin() != commentIter) {
1767 auto testIter = std::prev(commentIter);
1768 bool isComment = Definition::Type::kBracket == testIter->fType
1769 && (Bracket::kSlashSlash == testIter->fBracket
1770 || Bracket::kSlashStar == testIter->fBracket);
1771 if (!isComment) {
1772 break;
1773 }
1774 commentIter = testIter;
1775 }
1776 while (commentIter != wordIter) {
1777 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1778 commentIter->fContentEnd, commentIter->fLineCount, markupDef,
1779 &markupDef->fUndocumented)) {
1780 return false;
1781 }
1782 commentIter->fUndocumented = markupDef->fUndocumented;
1783 commentIter = std::next(commentIter);
1784 }
1785 return true;
1786 }
1787
findIncludeObject(const Definition & includeDef,MarkType markType,string typeName)1788 Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1789 string typeName) {
1790 typedef Definition* DefinitionPtr;
1791 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1792 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1793 if (mapIter == fMaps.end()) {
1794 return nullptr;
1795 }
1796 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1797 return reportError<DefinitionPtr>("invalid mark type");
1798 }
1799 string name = this->uniqueName(*mapIter->fInclude, typeName);
1800 Definition& markupDef = *(*mapIter->fInclude)[name];
1801 if (markupDef.fStart) {
1802 return reportError<DefinitionPtr>("definition already defined");
1803 }
1804 markupDef.fFileName = fFileName;
1805 markupDef.fStart = includeDef.fStart;
1806 markupDef.fContentStart = includeDef.fStart;
1807 this->checkName(&markupDef);
1808 markupDef.fName = name;
1809 markupDef.fContentEnd = includeDef.fContentEnd;
1810 markupDef.fTerminator = includeDef.fTerminator;
1811 markupDef.fParent = fParent;
1812 markupDef.fLineCount = includeDef.fLineCount;
1813 markupDef.fMarkType = markType;
1814 markupDef.fKeyWord = includeDef.fKeyWord;
1815 markupDef.fType = Definition::Type::kMark;
1816 return &markupDef;
1817 }
1818
findMethod(const Definition & bmhDef)1819 Definition* IncludeParser::findMethod(const Definition& bmhDef) {
1820 auto doubleColon = bmhDef.fName.rfind("::");
1821 if (string::npos == doubleColon) {
1822 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1823 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1824 return iGlobalMethod->second;
1825 }
1826 string className = bmhDef.fName.substr(0, doubleColon);
1827 const auto& iClass = fIClassMap.find(className);
1828 if (fIClassMap.end() == iClass) {
1829 return nullptr;
1830 }
1831 string methodName = bmhDef.fName.substr(doubleColon + 2);
1832 auto& iTokens = iClass->second.fTokens;
1833 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1834 [methodName](Definition& token) {
1835 return MarkType::kMethod == token.fMarkType
1836 && !token.fUndocumented
1837 && (methodName == token.fName
1838 || methodName == token.fName + "()"); } );
1839 if (iTokens.end() != iMethod) {
1840 return &*iMethod;
1841 }
1842 size_t subClassPos = className.rfind("::");
1843 if (string::npos != subClassPos) {
1844 className = className.substr(subClassPos + 2);
1845 }
1846 // match may be constructor; compare strings to see if this is so
1847 if (string::npos == methodName.find('(')) {
1848 return nullptr;
1849 }
1850 auto stripper = [](string s) -> string {
1851 bool last = false;
1852 string result;
1853 for (char c : s) {
1854 if (' ' >= c) {
1855 if (!last) {
1856 last = true;
1857 result += ' ';
1858 }
1859 continue;
1860 }
1861 result += c;
1862 last = false;
1863 }
1864 return result;
1865 };
1866 string strippedMethodName = stripper(methodName);
1867 if (strippedMethodName == methodName) {
1868 strippedMethodName = "";
1869 }
1870 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1871 [className, methodName, stripper, strippedMethodName](Definition& token) {
1872 if (MarkType::kMethod != token.fMarkType) {
1873 return false;
1874 }
1875 if (token.fUndocumented) {
1876 return false;
1877 }
1878 TextParser parser(&token);
1879 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1880 if (!match) {
1881 return false;
1882 }
1883 parser.skipTo(match);
1884 parser.skipExact(className.c_str());
1885 if ('(' != parser.peek()) {
1886 return false;
1887 }
1888 parser.skipToBalancedEndBracket('(', ')');
1889 string iMethodName(match, parser.fChar - match);
1890 if (methodName == iMethodName) {
1891 return true;
1892 }
1893 if (strippedMethodName.empty()) {
1894 return false;
1895 }
1896 string strippedIName = stripper(iMethodName);
1897 return strippedIName == strippedMethodName;
1898 } );
1899 SkAssertResult(iTokens.end() != cMethod);
1900 return &*cMethod;
1901 }
1902
parentBracket(Definition * parent) const1903 Definition* IncludeParser::parentBracket(Definition* parent) const {
1904 while (parent && Definition::Type::kBracket != parent->fType) {
1905 parent = parent->fParent;
1906 }
1907 return parent;
1908 }
1909
grandParentBracket() const1910 Bracket IncludeParser::grandParentBracket() const {
1911 Definition* parent = parentBracket(fParent);
1912 parent = parentBracket(parent ? parent->fParent : nullptr);
1913 return parent ? parent->fBracket : Bracket::kNone;
1914 }
1915
inAlignAs() const1916 bool IncludeParser::inAlignAs() const {
1917 if (fParent->fTokens.size() < 2) {
1918 return false;
1919 }
1920 auto reverseIter = fParent->fTokens.end();
1921 bool checkForBracket = true;
1922 while (fParent->fTokens.begin() != reverseIter) {
1923 std::advance(reverseIter, -1);
1924 if (checkForBracket) {
1925 if (Definition::Type::kBracket != reverseIter->fType) {
1926 return false;
1927 }
1928 if (Bracket::kParen != reverseIter->fBracket) {
1929 return false;
1930 }
1931 checkForBracket = false;
1932 continue;
1933 }
1934 if (Definition::Type::kKeyWord != reverseIter->fType) {
1935 return false;
1936 }
1937 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1938 }
1939 return false;
1940 }
1941
include(string match) const1942 const Definition* IncludeParser::include(string match) const {
1943 for (auto& entry : fIncludeMap) {
1944 if (string::npos == entry.first.find(match)) {
1945 continue;
1946 }
1947 return &entry.second;
1948 }
1949 SkASSERT(0);
1950 return nullptr;
1951 }
1952
1953 // caller just returns, so report error here
parseClass(Definition * includeDef,IsStruct isStruct)1954 bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1955 SkASSERT(includeDef->fTokens.size() > 0);
1956 // parse class header
1957 auto iter = includeDef->fTokens.begin();
1958 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1959 // todo : documentation is ignoring this for now
1960 iter = std::next(iter);
1961 }
1962 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1963 if (hasAlignAs) {
1964 iter = std::next(iter);
1965 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1966 return includeDef->reportError<bool>("expected alignas argument");
1967 }
1968 iter = std::next(iter);
1969 }
1970 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1971 includeDef->fName = nameStr;
1972 this->checkName(includeDef);
1973 iter = std::next(iter);
1974 if (iter == includeDef->fTokens.end()) {
1975 return true; // forward declaration only
1976 }
1977 do {
1978 if (iter == includeDef->fTokens.end()) {
1979 return includeDef->reportError<bool>("unexpected end");
1980 }
1981 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1982 break;
1983 }
1984 } while (static_cast<void>(iter = std::next(iter)), true);
1985 if (Punctuation::kLeftBrace != iter->fPunctuation) {
1986 return iter->reportError<bool>("expected left brace");
1987 }
1988 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1989 if (!markupDef) {
1990 return iter->reportError<bool>("expected markup definition");
1991 }
1992 markupDef->fStart = iter->fStart;
1993 if (!this->findComments(*includeDef, markupDef)) {
1994 return iter->reportError<bool>("find comments failed");
1995 }
1996 if (markupDef->fUndocumented) {
1997 includeDef->fUndocumented = true;
1998 }
1999 // if (1 != includeDef->fChildren.size()) {
2000 // return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
2001 // }
2002 auto includeDefIter = includeDef->fChildren.begin();
2003 if (hasAlignAs) {
2004 SkASSERT(includeDef->fChildren.end() != includeDefIter);
2005 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2006 std::advance(includeDefIter, 1);
2007 }
2008 if (includeDef->fChildren.end() != includeDefIter
2009 && Bracket::kAngle == (*includeDefIter)->fBracket) {
2010 std::advance(includeDefIter, 1);
2011 }
2012 includeDef = *includeDefIter;
2013 SkASSERT(Bracket::kBrace == includeDef->fBracket);
2014 iter = includeDef->fTokens.begin();
2015 // skip until public
2016 int publicIndex = 0;
2017 if (IsStruct::kNo == isStruct) {
2018 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2019 size_t publicLen = strlen(publicName);
2020 while (iter != includeDef->fTokens.end()
2021 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2022 || strncmp(iter->fStart, publicName, publicLen))) {
2023 iter->fPrivate = true;
2024 iter = std::next(iter);
2025 ++publicIndex;
2026 }
2027 }
2028 int keyIndex = publicIndex;
2029 KeyWord currentKey = KeyWord::kPublic;
2030 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2031 size_t publicLen = strlen(publicName);
2032 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2033 size_t protectedLen = strlen(protectedName);
2034 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2035 size_t privateLen = strlen(privateName);
2036 auto childIter = includeDef->fChildren.begin();
2037 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
2038 std::advance(childIter, 1);
2039 }
2040 while (childIter != includeDef->fChildren.end()) {
2041 Definition* child = *childIter;
2042 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
2043 iter->fPrivate = KeyWord::kPublic != currentKey;
2044 const char* testStart = iter->fStart;
2045 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2046 iter = std::next(iter);
2047 ++keyIndex;
2048 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2049 currentKey = KeyWord::kPublic;
2050 break;
2051 }
2052 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2053 currentKey = KeyWord::kProtected;
2054 break;
2055 }
2056 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2057 currentKey = KeyWord::kPrivate;
2058 break;
2059 }
2060 }
2061 fLastObject = nullptr;
2062 if (KeyWord::kPublic == currentKey) {
2063 if (!this->parseObject(child, markupDef)) {
2064 return false;
2065 }
2066 }
2067 fLastObject = child;
2068 childIter = std::next(childIter);
2069 }
2070 while (iter != includeDef->fTokens.end()) {
2071 iter->fPrivate = KeyWord::kPublic != currentKey;
2072 iter = std::next(iter);
2073 }
2074 SkASSERT(fParent->fParent);
2075 fParent = fParent->fParent;
2076 return true;
2077 }
2078
isUndocumentable(string filename,const char * start,const char * end,int lineCount)2079 bool IncludeParser::isUndocumentable(string filename, const char* start, const char* end,
2080 int lineCount) {
2081 TextParser parser(filename, start, end, lineCount);
2082 const vector<string> skipWords = { "deprecated", "experimental", "private" };
2083 const vector<string> butNot = { "to be deprecated", "may be deprecated" };
2084 const vector<string> alsoNot = { "todo" };
2085 string match = parser.anyWord(skipWords, 0);
2086 if ("" != match) {
2087 if (parser.anyWord(alsoNot, 0).empty()
2088 && ("deprecated" != match || parser.anyWord(butNot, 2).empty())) {
2089 return true;
2090 }
2091 }
2092 return false;
2093 }
2094
parseComment(string filename,const char * start,const char * end,int lineCount,Definition * markupDef,bool * undocumentedPtr)2095 bool IncludeParser::parseComment(string filename, const char* start, const char* end,
2096 int lineCount, Definition* markupDef, bool* undocumentedPtr) {
2097 if (this->isUndocumentable(filename, start, end, lineCount)) {
2098 *undocumentedPtr = true;
2099 }
2100 // parse doxygen if present
2101 TextParser parser(filename, start, end, lineCount);
2102 if (parser.startsWith("**")) {
2103 parser.next();
2104 parser.next();
2105 parser.skipWhiteSpace();
2106 if ('\\' == parser.peek()) {
2107 parser.next();
2108 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2109 if (parser.skipExact("file")) {
2110 if (Definition::Type::kFileType != fParent->fType) {
2111 return reportError<bool>("expected parent is file");
2112 }
2113 string filename = markupDef->fileName();
2114 if (!parser.skipWord(filename.c_str())) {
2115 return reportError<bool>("missing object type");
2116 }
2117 } else if (parser.skipExact("fn")) {
2118 SkASSERT(0); // incomplete
2119 } else {
2120 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2121 return reportError<bool>("missing object type");
2122 }
2123 if (!parser.skipWord(markupDef->fName.c_str()) &&
2124 KeyWord::kEnum != markupDef->fKeyWord) {
2125 return reportError<bool>("missing object name");
2126 }
2127 }
2128 }
2129 }
2130 // remove leading '*' if present
2131 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2132 while (!parser.eof() && parser.skipWhiteSpace()) {
2133 while ('*' == parser.peek()) {
2134 parser.next();
2135 if (parser.eof()) {
2136 break;
2137 }
2138 parser.skipWhiteSpace();
2139 }
2140 if (parser.eof()) {
2141 break;
2142 }
2143 const char* lineEnd = parser.trimmedLineEnd();
2144 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
2145 parser.fLineCount, parent, '\0');
2146 parser.skipToEndBracket('\n');
2147 }
2148 return true;
2149 }
2150
2151 /*
2152 find comment either in front of or after the const def and then extract if the
2153 const is undocumented
2154 */
parseConst(Definition * child,Definition * markupDef)2155 bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
2156 if (!markupDef) {
2157 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2158 child->fLineCount, fParent, '\0');
2159 Definition* globalMarkupChild = &fGlobals.back();
2160 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
2161 globalMarkupChild->fName = globalUniqueName;
2162 if (!this->findComments(*child, globalMarkupChild)) {
2163 return false;
2164 }
2165 if (!this->findCommentAfter(*child, globalMarkupChild)) {
2166 return false;
2167 }
2168 if (globalMarkupChild->fUndocumented) {
2169 child->fUndocumented = true;
2170 } else {
2171 fIConstMap[globalUniqueName] = globalMarkupChild;
2172 }
2173 return true;
2174 }
2175 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2176 child->fLineCount, markupDef, '\0');
2177 Definition* markupChild = &markupDef->fTokens.back();
2178 markupChild->fName = child->fName;
2179 markupChild->fTerminator = markupChild->fContentEnd;
2180 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2181 classDef.fConsts[child->fName] = markupChild;
2182 if (!this->findComments(*child, markupChild)) {
2183 return false;
2184 }
2185 if (!this->findCommentAfter(*child, markupChild)) {
2186 return false;
2187 }
2188 if (markupChild->fUndocumented) {
2189 child->fUndocumented = true;
2190 } else {
2191 fIConstMap[child->fName] = markupChild;
2192 }
2193 return true;
2194 }
2195
parseDefine(Definition * child,Definition * markupDef)2196 bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2197 TextParser parser(child);
2198 if (!parser.skipExact("#define")) {
2199 return false;
2200 }
2201 if (!parser.skipSpace()) {
2202 return false;
2203 }
2204 const char* nameStart = parser.fChar;
2205 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2206 if (parser.eof()) {
2207 return true; // do nothing if #define doesn't define anything
2208 }
2209 string nameStr(nameStart, parser.fChar - nameStart);
2210 struct Param {
2211 const char* fStart;
2212 const char* fEnd;
2213 };
2214 vector<Param> params;
2215 if ('(' == parser.peek()) {
2216 parser.next();
2217 if (!parser.skipSpace()) {
2218 return false;
2219 }
2220 do {
2221 const char* paramStart = parser.fChar;
2222 if (!parser.skipExact("...")) {
2223 parser.skipToNonAlphaNum();
2224 }
2225 if (parser.eof()) {
2226 return false;
2227 }
2228 params.push_back({paramStart, parser.fChar});
2229 if (!parser.skipSpace()) {
2230 return false;
2231 }
2232 if (')' == parser.peek()) {
2233 parser.next();
2234 break;
2235 }
2236 if (',' != parser.next()) {
2237 return false;
2238 }
2239 if (!parser.skipSpace()) {
2240 return false;
2241 }
2242 } while (true);
2243 }
2244 if (!parser.skipSpace()) {
2245 return false;
2246 }
2247 if (!markupDef) {
2248 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2249 child->fLineCount, fParent, '\0');
2250 Definition* globalMarkupChild = &fGlobals.back();
2251 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2252 globalMarkupChild->fName = globalUniqueName;
2253 globalMarkupChild->fTerminator = child->fContentEnd;
2254 if (!this->findComments(*child, globalMarkupChild)) {
2255 return false;
2256 }
2257 if (!globalMarkupChild->fUndocumented) {
2258 fIDefineMap[globalUniqueName] = globalMarkupChild;
2259 }
2260 for (Param param : params) {
2261 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2262 child->fLineCount, globalMarkupChild, '\0');
2263 Definition* paramChild = &globalMarkupChild->fTokens.back();
2264 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2265 this->checkName(paramChild);
2266 paramChild->fTerminator = param.fEnd;
2267 }
2268 return true;
2269 }
2270 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2271 child->fLineCount, markupDef, '\0');
2272 Definition* markupChild = &markupDef->fTokens.back();
2273 markupChild->fName = nameStr;
2274 markupChild->fTerminator = markupChild->fContentEnd;
2275 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2276 if (!this->findComments(*child, markupChild)) {
2277 return false;
2278 }
2279 if (markupChild->fUndocumented) {
2280 child->fUndocumented = true;
2281 } else {
2282 classDef.fDefines[nameStr] = markupChild;
2283 fIDefineMap[nameStr] = markupChild;
2284 }
2285 return true;
2286 }
2287
parseEnum(Definition * child,Definition * markupDef)2288 bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
2289 if (!child->fTokens.size()) {
2290 return true; // if enum is a forward declaration, do nothing
2291 }
2292 bool isEnumClass = false;
2293 Definition* parent = child;
2294 auto token = parent->fTokens.begin();
2295 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2296 isEnumClass = true;
2297 parent = &*token;
2298 token = parent->fTokens.begin();
2299 }
2300 SkASSERT(Definition::Type::kWord == token->fType);
2301 string nameStr = string(token->fStart, token->fContentEnd - token->fStart);
2302 Definition* markupChild;
2303 if (!markupDef) {
2304 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2305 child->fLineCount, fParent, '\0');
2306 markupChild = &fGlobals.back();
2307 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2308 markupChild->fName = globalUniqueName;
2309 markupChild->fTerminator = child->fContentEnd;
2310 if (!markupChild->fUndocumented) {
2311 fIEnumMap[globalUniqueName] = markupChild;
2312 }
2313 } else {
2314 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2315 child->fLineCount, markupDef, '\0');
2316 markupChild = &markupDef->fTokens.back();
2317 }
2318 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2319 markupChild->fKeyWord = KeyWord::kEnum;
2320 if (isEnumClass) {
2321 markupChild->fMarkType = MarkType::kEnumClass;
2322 }
2323 if (markupDef) {
2324 markupChild->fName = markupDef->fName + "::" + nameStr;
2325 }
2326 if (!this->findComments(*child, markupChild)) {
2327 return false;
2328 }
2329 if (markupChild->fUndocumented) {
2330 child->fUndocumented = true;
2331 }
2332 if (!this->parseEnumConst(token, parent->fTokens.end(), markupChild)) {
2333 return false;
2334 }
2335 for (auto outsideMember : child->fChildren) {
2336 if (Definition::Type::kBracket == outsideMember->fType) {
2337 continue;
2338 }
2339 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2340 if (KeyWord::kClass == outsideMember->fKeyWord) {
2341 continue;
2342 }
2343 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2344 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
2345 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
2346 Definition* member = &markupChild->fTokens.back();
2347 member->fName = outsideMember->fName;
2348 this->checkName(member);
2349 // FIXME: ? add comment as well ?
2350 markupChild->fChildren.push_back(member);
2351 }
2352 if (markupDef) {
2353 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2354 SkASSERT(classDef.fStart);
2355 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
2356 string fullName = markupChild->fName;
2357 markupChild->fName = uniqueName;
2358 classDef.fEnums[uniqueName] = markupChild;
2359 if (!markupChild->fUndocumented) {
2360 fIEnumMap[fullName] = markupChild;
2361 }
2362 }
2363 return true;
2364 }
2365
parseOneEnumConst(list<Definition> & constList,Definition * markupChild,bool skipWord)2366 bool IncludeParser::parseOneEnumConst(list<Definition>& constList,
2367 Definition* markupChild, bool skipWord) {
2368 auto memberIter = constList.begin();
2369 const auto memberIterEnd = constList.end();
2370 if (skipWord) {
2371 SkASSERT(Definition::Type::kWord == memberIter->fType);
2372 memberIter = std::next(memberIter);
2373 SkASSERT(memberIterEnd != memberIter);
2374 }
2375 // token array has parse atoms; child array has comments
2376 bool undocumented = false;
2377 while (memberIterEnd != memberIter) {
2378 while (Bracket::kSlashStar == memberIter->fBracket) {
2379 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2380 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2381 return false;
2382 }
2383 memberIter = std::next(memberIter);
2384 if (memberIterEnd == memberIter) {
2385 return false;
2386 }
2387 }
2388 if (Bracket::kPound == memberIter->fBracket) {
2389 KeyWord keyWord = memberIter->fKeyWord;
2390 bool sawIf = KeyWord::kIfdef == keyWord || KeyWord::kIf == keyWord
2391 || KeyWord::kElif == keyWord;
2392 if (sawIf || KeyWord::kElse == keyWord) {
2393 if (!parseOneEnumConst(memberIter->fTokens, markupChild, sawIf)) {
2394 return false;
2395 }
2396 } else {
2397 SkASSERT(KeyWord::kEndif == keyWord || KeyWord::kError == keyWord);
2398 }
2399 memberIter = std::next(memberIter);
2400 if (memberIterEnd == memberIter) {
2401 break;
2402 }
2403 continue;
2404 }
2405 while (Definition::Type::kWord != memberIter->fType) {
2406 memberIter = std::next(memberIter);
2407 if (memberIterEnd == memberIter) {
2408 return false;
2409 }
2410 }
2411 auto memberStart = memberIter;
2412 Definition* memberEnd = nullptr;
2413 const char* last;
2414 do {
2415 last = memberIter->fContentEnd;
2416 memberIter = std::next(memberIter);
2417 if (memberIterEnd == memberIter) {
2418 break;
2419 }
2420 memberEnd = &*memberIter;
2421 } while (string::npos == string(last, memberIter->fContentStart).find(','));
2422 if (!memberEnd) {
2423 return false;
2424 }
2425 if (memberIterEnd != memberIter && Bracket::kSlashSlash == memberIter->fBracket) {
2426 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2427 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2428 return false;
2429 }
2430 memberIter = std::next(memberIter);
2431 }
2432 markupChild->fTokens.emplace_back(MarkType::kMember, memberStart->fContentStart,
2433 memberEnd->fContentEnd, memberStart->fLineCount, markupChild, '\0');
2434 Definition* markupMember = &markupChild->fTokens.back();
2435 string name = string(memberStart->fContentStart, memberStart->length());
2436 memberStart->fName = name;
2437 markupMember->fName = name;
2438 this->checkName(markupMember);
2439 memberStart->fUndocumented = markupMember->fUndocumented;
2440 memberStart->fMarkType = MarkType::kMember;
2441 undocumented = false;
2442 }
2443 return true;
2444 }
2445
parseEnumConst(list<Definition>::iterator & tokenIter,const list<Definition>::iterator & tokenEnd,Definition * markupChild)2446 bool IncludeParser::parseEnumConst(list<Definition>::iterator& tokenIter,
2447 const list<Definition>::iterator& tokenEnd, Definition* markupChild) {
2448 SkASSERT(Definition::Type::kWord == tokenIter->fType); // should be enum name
2449 tokenIter = std::next(tokenIter);
2450 SkASSERT(tokenEnd != tokenIter);
2451 if (Definition::Type::kKeyWord == tokenIter->fType) {
2452 SkASSERT((unsigned) tokenIter->fKeyWord < SK_ARRAY_COUNT(kKeyWords));
2453 SkASSERT(KeyProperty::kNumber == kKeyWords[(int) tokenIter->fKeyWord].fProperty);
2454 tokenIter = std::next(tokenIter);
2455 SkASSERT(tokenEnd != tokenIter);
2456 }
2457 SkASSERT(Punctuation::kLeftBrace == tokenIter->fPunctuation);
2458 tokenIter = std::next(tokenIter);
2459 SkASSERT(tokenEnd != tokenIter);
2460 SkASSERT(Bracket::kBrace == tokenIter->fBracket);
2461 return parseOneEnumConst(tokenIter->fTokens, markupChild, false);
2462 }
2463
parseInclude(string name)2464 bool IncludeParser::parseInclude(string name) {
2465 fParent = &fIncludeMap[name];
2466 fParent->fName = name;
2467 this->checkName(fParent);
2468 fParent->fFileName = fFileName;
2469 fParent->fType = Definition::Type::kFileType;
2470 fParent->fContentStart = fChar;
2471 fParent->fContentEnd = fEnd;
2472 // parse include file into tree
2473 while (fChar < fEnd) {
2474 if (!this->parseChar()) {
2475 return false;
2476 }
2477 }
2478 // parse tree and add named objects to maps
2479 fParent = &fIncludeMap[name];
2480 if (!this->parseObjects(fParent, nullptr)) {
2481 return false;
2482 }
2483 return true;
2484 }
2485
parseMember(Definition * child,Definition * markupDef)2486 bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2487 const char* typeStart = child->fChildren[0]->fContentStart;
2488 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
2489 child->fLineCount, markupDef, '\0');
2490 Definition* markupChild = &markupDef->fTokens.back();
2491 TextParser nameParser(child);
2492 nameParser.skipToNonName();
2493 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2494 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2495 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2496 markupChild->fName = uniqueName;
2497 this->checkName(markupChild);
2498 markupChild->fTerminator = markupChild->fContentEnd;
2499 if (!markupChild->fUndocumented) {
2500 classDef.fMembers[uniqueName] = markupChild;
2501 }
2502 if (child->fParentIndex >= 2) {
2503 auto comment = child->fParent->fTokens.begin();
2504 std::advance(comment, child->fParentIndex - 2);
2505 if (Definition::Type::kBracket == comment->fType
2506 && (Bracket::kSlashStar == comment->fBracket
2507 || Bracket::kSlashSlash == comment->fBracket)) {
2508 TextParser parser(&*comment);
2509 do {
2510 parser.skipToAlpha();
2511 if (parser.eof()) {
2512 break;
2513 }
2514 const char* start = parser.fChar;
2515 const char* end = parser.trimmedBracketEnd('\n');
2516 if (Bracket::kSlashStar == comment->fBracket) {
2517 const char* commentEnd = parser.strnstr("*/", end);
2518 if (commentEnd) {
2519 end = commentEnd;
2520 }
2521 }
2522 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
2523 markupDef, '\0');
2524 Definition* commentChild = &markupDef->fTokens.back();
2525 markupChild->fChildren.emplace_back(commentChild);
2526 parser.skipTo(end);
2527 } while (!parser.eof());
2528 }
2529 }
2530 return true;
2531 }
2532
parseMethod(Definition * child,Definition * markupDef)2533 bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2534 auto tokenIter = child->fParent->fTokens.begin();
2535 std::advance(tokenIter, child->fParentIndex);
2536 tokenIter = std::prev(tokenIter);
2537 const char* nameEnd = tokenIter->fContentEnd;
2538 bool addConst = false;
2539 auto operatorCheck = tokenIter;
2540 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2541 operatorCheck = std::prev(tokenIter);
2542 }
2543 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
2544 auto closeParen = std::next(tokenIter);
2545 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2546 '(' == closeParen->fContentStart[0]);
2547 nameEnd = closeParen->fContentEnd + 1;
2548 closeParen = std::next(closeParen);
2549 if (Definition::Type::kKeyWord == closeParen->fType &&
2550 KeyWord::kConst == closeParen->fKeyWord) {
2551 addConst = true;
2552 }
2553 tokenIter = operatorCheck;
2554 }
2555 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
2556 if (addConst) {
2557 nameStr += " const";
2558 }
2559 while (tokenIter != child->fParent->fTokens.begin()) {
2560 auto testIter = std::prev(tokenIter);
2561 switch (testIter->fType) {
2562 case Definition::Type::kWord:
2563 if (testIter == child->fParent->fTokens.begin() &&
2564 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2565 KeyWord::kIfndef == child->fParent->fKeyWord ||
2566 KeyWord::kIf == child->fParent->fKeyWord)) {
2567 std::next(tokenIter);
2568 break;
2569 }
2570 goto keepGoing;
2571 case Definition::Type::kKeyWord: {
2572 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2573 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2574 goto keepGoing;
2575 }
2576 } break;
2577 case Definition::Type::kBracket:
2578 if (Bracket::kAngle == testIter->fBracket) {
2579 goto keepGoing;
2580 }
2581 break;
2582 case Definition::Type::kPunctuation:
2583 if (Punctuation::kSemicolon == testIter->fPunctuation
2584 || Punctuation::kLeftBrace == testIter->fPunctuation
2585 || Punctuation::kColon == testIter->fPunctuation) {
2586 break;
2587 }
2588 keepGoing:
2589 tokenIter = testIter;
2590 continue;
2591 default:
2592 break;
2593 }
2594 break;
2595 }
2596 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
2597 tokenIter->fMarkType = MarkType::kMethod;
2598 tokenIter->fPrivate = string::npos != nameStr.find("::")
2599 && KeyWord::kTemplate != child->fParent->fKeyWord;
2600 this->checkName(&*tokenIter);
2601 auto testIter = child->fParent->fTokens.begin();
2602 SkASSERT(child->fParentIndex > 0);
2603 std::advance(testIter, child->fParentIndex - 1);
2604 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2605 0 == tokenIter->fParentIndex) {
2606 tokenIter = std::next(tokenIter);
2607 }
2608 const char* start = tokenIter->fContentStart;
2609 const char* end = tokenIter->fContentEnd;
2610 const char kDebugCodeStr[] = "SkDEBUGCODE";
2611 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2612 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2613 std::advance(testIter, 1);
2614 start = testIter->fContentStart + 1;
2615 end = testIter->fContentEnd - 1;
2616 } else {
2617 end = testIter->fContentEnd;
2618 do {
2619 std::advance(testIter, 1);
2620 if (testIter == child->fParent->fTokens.end()) {
2621 break;
2622 }
2623 switch (testIter->fType) {
2624 case Definition::Type::kPunctuation:
2625 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2626 || Punctuation::kLeftBrace == testIter->fPunctuation
2627 || Punctuation::kColon == testIter->fPunctuation);
2628 end = testIter->fStart;
2629 break;
2630 case Definition::Type::kKeyWord: {
2631 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2632 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2633 continue;
2634 }
2635 } break;
2636 default:
2637 continue;
2638 }
2639 break;
2640 } while (true);
2641 }
2642 while (end > start && ' ' >= end[-1]) {
2643 --end;
2644 }
2645 if (!markupDef) {
2646 auto parentIter = child->fParent->fTokens.begin();
2647 SkASSERT(child->fParentIndex > 0);
2648 std::advance(parentIter, child->fParentIndex - 1);
2649 Definition* methodName = &*parentIter;
2650 TextParser nameParser(methodName);
2651 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
2652 return true; // expect this is inline class definition outside of class
2653 }
2654 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2655 fParent, '\0');
2656 Definition* globalMarkupChild = &fGlobals.back();
2657 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2658 globalMarkupChild->fName = globalUniqueName;
2659 if (!this->findComments(*child, globalMarkupChild)) {
2660 return false;
2661 }
2662 if (globalMarkupChild->fUndocumented) {
2663 child->fUndocumented = true;
2664 } else {
2665 fIFunctionMap[globalUniqueName] = globalMarkupChild;
2666 }
2667 return true;
2668 }
2669 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2670 markupDef, '\0');
2671 Definition* markupChild = &markupDef->fTokens.back();
2672 {
2673 auto mapIter = fIClassMap.find(markupDef->fName);
2674 SkASSERT(fIClassMap.end() != mapIter);
2675 IClassDefinition& classDef = mapIter->second;
2676 SkASSERT(classDef.fStart);
2677 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2678 markupChild->fName = uniqueName;
2679 if (!this->findComments(*child, markupChild)) {
2680 return false;
2681 }
2682 if (markupChild->fUndocumented) {
2683 tokenIter->fUndocumented = true;
2684 } else {
2685 classDef.fMethods[uniqueName] = markupChild;
2686 }
2687 }
2688 return true;
2689 }
2690
parseObjects(Definition * parent,Definition * markupDef)2691 bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
2692 fPriorObject = nullptr;
2693 for (auto child : parent->fChildren) {
2694 if (!this->parseObject(child, markupDef)) {
2695 return false;
2696 }
2697 fPriorObject = child;
2698 }
2699 return true;
2700 }
2701
parseObject(Definition * child,Definition * markupDef)2702 bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2703 // set up for error reporting
2704 fLine = fChar = child->fStart;
2705 fEnd = child->fContentEnd;
2706 // todo: put original line number in child as well
2707 switch (child->fType) {
2708 case Definition::Type::kKeyWord:
2709 switch (child->fKeyWord) {
2710 case KeyWord::kClass:
2711 if (!this->parseClass(child, IsStruct::kNo)) {
2712 return false;
2713 }
2714 break;
2715 case KeyWord::kStatic:
2716 case KeyWord::kConst:
2717 case KeyWord::kConstExpr:
2718 if (!this->parseConst(child, markupDef)) {
2719 return child->reportError<bool>("failed to parse const or constexpr");
2720 }
2721 break;
2722 case KeyWord::kEnum:
2723 if (!this->parseEnum(child, markupDef)) {
2724 return child->reportError<bool>("failed to parse enum");
2725 }
2726 break;
2727 case KeyWord::kStruct:
2728 if (!this->parseClass(child, IsStruct::kYes)) {
2729 return child->reportError<bool>("failed to parse struct");
2730 }
2731 break;
2732 case KeyWord::kTemplate:
2733 if (!this->parseTemplate(child, markupDef)) {
2734 return child->reportError<bool>("failed to parse template");
2735 }
2736 break;
2737 case KeyWord::kTypedef:
2738 if (!this->parseTypedef(child, markupDef)) {
2739 return child->reportError<bool>("failed to parse typedef");
2740 }
2741 break;
2742 case KeyWord::kUnion:
2743 if (!this->parseUnion()) {
2744 return child->reportError<bool>("failed to parse union");
2745 }
2746 break;
2747 case KeyWord::kUsing:
2748 if (!this->parseUsing()) {
2749 return child->reportError<bool>("failed to parse using");
2750 }
2751 break;
2752 default:
2753 return child->reportError<bool>("unhandled keyword");
2754 }
2755 break;
2756 case Definition::Type::kBracket:
2757 switch (child->fBracket) {
2758 case Bracket::kParen:
2759 {
2760 auto tokenIter = child->fParent->fTokens.begin();
2761 std::advance(tokenIter, child->fParentIndex);
2762 tokenIter = std::prev(tokenIter);
2763 TextParser previousToken(&*tokenIter);
2764 if (this->isMember(*tokenIter)) {
2765 break;
2766 }
2767 if (Bracket::kPound == child->fParent->fBracket &&
2768 KeyWord::kIf == child->fParent->fKeyWord) {
2769 // TODO: this will skip methods named defined() -- for the
2770 // moment there aren't any
2771 if (previousToken.startsWith("defined")) {
2772 break;
2773 }
2774 }
2775 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2776 break;
2777 }
2778 }
2779 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2780 break;
2781 }
2782 if (!this->parseMethod(child, markupDef)) {
2783 return child->reportError<bool>("failed to parse method");
2784 }
2785 break;
2786 case Bracket::kSlashSlash:
2787 case Bracket::kSlashStar:
2788 // comments are picked up by parsing objects first
2789 break;
2790 case Bracket::kPound:
2791 // special-case the #xxx xxx_DEFINED entries
2792 switch (child->fKeyWord) {
2793 case KeyWord::kIf:
2794 case KeyWord::kIfndef:
2795 case KeyWord::kIfdef:
2796 if (child->boilerplateIfDef()) {
2797 if (!this->parseObjects(child, markupDef)) {
2798 return false;
2799 }
2800 break;
2801 }
2802 goto preproError;
2803 case KeyWord::kDefine:
2804 if (this->parseDefine(child, markupDef)) {
2805 break;
2806 }
2807 goto preproError;
2808 case KeyWord::kEndif:
2809 if (child->boilerplateEndIf()) {
2810 break;
2811 }
2812 case KeyWord::kError:
2813 case KeyWord::kInclude:
2814 // ignored for now
2815 break;
2816 case KeyWord::kElse:
2817 if (!this->parseObjects(child, markupDef)) {
2818 return false;
2819 }
2820 break;
2821 case KeyWord::kElif:
2822 // todo: handle these
2823 break;
2824 default:
2825 preproError:
2826 return child->reportError<bool>("unhandled preprocessor");
2827 }
2828 break;
2829 case Bracket::kAngle:
2830 // pick up templated function pieces when method is found
2831 break;
2832 case Bracket::kDebugCode:
2833 if (!this->parseObjects(child, markupDef)) {
2834 return false;
2835 }
2836 break;
2837 case Bracket::kSquare: {
2838 // check to see if parent is operator, the only case we handle so far
2839 auto prev = child->fParent->fTokens.begin();
2840 std::advance(prev, child->fParentIndex - 1);
2841 if (KeyWord::kOperator != prev->fKeyWord) {
2842 return child->reportError<bool>("expected operator overload");
2843 }
2844 } break;
2845 default:
2846 return child->reportError<bool>("unhandled bracket");
2847 }
2848 break;
2849 case Definition::Type::kWord:
2850 if (MarkType::kMember != child->fMarkType) {
2851 return child->reportError<bool>("unhandled word type");
2852 }
2853 if (!this->parseMember(child, markupDef)) {
2854 return child->reportError<bool>("unparsable member");
2855 }
2856 break;
2857 default:
2858 return child->reportError<bool>("unhandled type");
2859 break;
2860 }
2861 return true;
2862 }
2863
parseTemplate(Definition * child,Definition * markupDef)2864 bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2865 return this->parseObjects(child, markupDef);
2866 }
2867
parseTypedef(Definition * child,Definition * markupDef)2868 bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2869 TextParser typedefParser(child);
2870 typedefParser.skipExact("typedef");
2871 typedefParser.skipWhiteSpace();
2872 string nameStr = typedefParser.typedefName();
2873 if (!markupDef) {
2874 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2875 child->fLineCount, fParent, '\0');
2876 Definition* globalMarkupChild = &fGlobals.back();
2877 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2878 globalMarkupChild->fName = globalUniqueName;
2879 if (!this->findComments(*child, globalMarkupChild)) {
2880 return false;
2881 }
2882 if (globalMarkupChild->fUndocumented) {
2883 child->fUndocumented = true;
2884 } else {
2885 fITypedefMap[globalUniqueName] = globalMarkupChild;
2886 }
2887 child->fName = nameStr;
2888 return true;
2889 }
2890 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2891 child->fLineCount, markupDef, '\0');
2892 Definition* markupChild = &markupDef->fTokens.back();
2893 markupChild->fName = nameStr;
2894 this->checkName(markupChild);
2895 markupChild->fTerminator = markupChild->fContentEnd;
2896 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2897 classDef.fTypedefs[nameStr] = markupChild;
2898 child->fName = markupDef->fName + "::" + nameStr;
2899 this->checkName(child);
2900 fITypedefMap[child->fName] = markupChild;
2901 return true;
2902 }
2903
parseUnion()2904 bool IncludeParser::parseUnion() {
2905 // incomplete
2906 return true;
2907 }
2908
parseUsing()2909 bool IncludeParser::parseUsing() {
2910 // incomplete
2911 return true;
2912 }
2913
parseChar()2914 bool IncludeParser::parseChar() {
2915 char test = *fChar;
2916 if ('\\' == fPrev) {
2917 if ('\n' == test) {
2918 // ++fLineCount;
2919 fLine = fChar + 1;
2920 }
2921 goto done;
2922 }
2923 switch (test) {
2924 case '\n':
2925 // ++fLineCount;
2926 fLine = fChar + 1;
2927 if (fInChar) {
2928 return reportError<bool>("malformed char");
2929 }
2930 if (fInString) {
2931 return reportError<bool>("malformed string");
2932 }
2933 if (!this->checkForWord()) {
2934 return false;
2935 }
2936 if (Bracket::kPound == this->topBracket()) {
2937 KeyWord keyWord = fParent->fKeyWord;
2938 if (KeyWord::kNone == keyWord) {
2939 return this->reportError<bool>("unhandled preprocessor directive");
2940 }
2941 if (fInDefine) {
2942 SkASSERT(KeyWord::kDefine == keyWord);
2943 fInDefine = false;
2944 }
2945 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
2946 this->popBracket();
2947 }
2948 if (fInBrace) {
2949 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2950 fInBrace = nullptr;
2951 }
2952 } else if (Bracket::kSlashSlash == this->topBracket()) {
2953 this->popBracket();
2954 }
2955 break;
2956 case '*':
2957 if (!fInCharCommentString && '/' == fPrev) {
2958 this->pushBracket(Bracket::kSlashStar);
2959 }
2960 if (!this->checkForWord()) {
2961 return false;
2962 }
2963 if (!fInCharCommentString) {
2964 this->addPunctuation(Punctuation::kAsterisk);
2965 }
2966 break;
2967 case '/':
2968 if ('*' == fPrev) {
2969 if (!fInCharCommentString) {
2970 return reportError<bool>("malformed closing comment");
2971 }
2972 if (Bracket::kSlashStar == this->topBracket()) {
2973 TextParserSave save(this);
2974 this->next(); // include close in bracket
2975 this->popBracket();
2976 save.restore(); // put things back so nothing is skipped
2977 }
2978 break;
2979 }
2980 if (!fInCharCommentString && '/' == fPrev) {
2981 this->pushBracket(Bracket::kSlashSlash);
2982 break;
2983 }
2984 if (!this->checkForWord()) {
2985 return false;
2986 }
2987 break;
2988 case '\'':
2989 if (Bracket::kChar == this->topBracket()) {
2990 this->popBracket();
2991 } else if (!fInComment && !fInString) {
2992 if (fIncludeWord) {
2993 return this->reportError<bool>("word then single-quote");
2994 }
2995 this->pushBracket(Bracket::kChar);
2996 }
2997 break;
2998 case '\"':
2999 if (Bracket::kString == this->topBracket()) {
3000 this->popBracket();
3001 } else if (!fInComment && !fInChar) {
3002 if (fIncludeWord) {
3003 return this->reportError<bool>("word then double-quote");
3004 }
3005 this->pushBracket(Bracket::kString);
3006 }
3007 break;
3008 case '(':
3009 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
3010 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
3011 this->pushBracket(Bracket::kDebugCode);
3012 break;
3013 }
3014 case ':':
3015 case '[':
3016 case '{': {
3017 if (fInCharCommentString) {
3018 break;
3019 }
3020 if (fInDefine && fInBrace) {
3021 break;
3022 }
3023 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
3024 break;
3025 }
3026 if (fConstExpr) {
3027 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3028 fConstExpr = nullptr;
3029 }
3030 if (!fInBrace) {
3031 if (!this->checkForWord()) {
3032 return false;
3033 }
3034 if (':' == test && !fInFunction) {
3035 break;
3036 }
3037 if ('{' == test) {
3038 this->addPunctuation(Punctuation::kLeftBrace);
3039 } else if (':' == test) {
3040 this->addPunctuation(Punctuation::kColon);
3041 }
3042 }
3043 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3044 && Bracket::kColon == fInBrace->fBracket) {
3045 Definition* braceParent = fParent->fParent;
3046 braceParent->fChildren.pop_back();
3047 braceParent->fTokens.pop_back();
3048 fParent = braceParent;
3049 fInBrace = nullptr;
3050 }
3051 this->pushBracket(
3052 '(' == test ? Bracket::kParen :
3053 '[' == test ? Bracket::kSquare :
3054 '{' == test ? Bracket::kBrace :
3055 Bracket::kColon);
3056 if (!fInBrace
3057 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3058 && fInFunction) {
3059 fInBrace = fParent;
3060 }
3061 } break;
3062 case '<':
3063 if (fInCharCommentString || fInBrace) {
3064 break;
3065 }
3066 if (!this->checkForWord()) {
3067 return false;
3068 }
3069 if (fInEnum) {
3070 break;
3071 }
3072 this->pushBracket(Bracket::kAngle);
3073 // this angle bracket may be an operator or may be a bracket
3074 // wait for balancing close angle, if any, to decide
3075 break;
3076 case ')':
3077 case ']':
3078 case '}': {
3079 if (fInCharCommentString) {
3080 break;
3081 }
3082 if (fInDefine && fInBrace) {
3083 break;
3084 }
3085 if (!fInBrace) {
3086 if (!this->checkForWord()) {
3087 return false;
3088 }
3089 }
3090 bool popBraceParent = fInBrace == fParent;
3091 Bracket match = ')' == test ? Bracket::kParen :
3092 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3093 if (match == this->topBracket()) {
3094 this->popBracket();
3095 if (!fInFunction) {
3096 fInFunction = ')' == test && !this->inAlignAs();
3097 } else {
3098 fInFunction = '}' != test;
3099 }
3100 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3101 this->popBracket();
3102 } else if (Bracket::kAngle == this->topBracket()
3103 && match == this->grandParentBracket()) {
3104 this->popBracket();
3105 this->popBracket();
3106 } else {
3107 return reportError<bool>("malformed close bracket");
3108 }
3109 if (popBraceParent) {
3110 Definition* braceParent = fInBrace->fParent;
3111 braceParent->fChildren.pop_back();
3112 braceParent->fTokens.pop_back();
3113 fInBrace = nullptr;
3114 }
3115 } break;
3116 case '>':
3117 if (fInCharCommentString || fInBrace) {
3118 break;
3119 }
3120 if (!this->checkForWord()) {
3121 return false;
3122 }
3123 if (fInEnum) {
3124 break;
3125 }
3126 if (Bracket::kPound == this->topBracket()) {
3127 break;
3128 }
3129 if (Bracket::kAngle == this->topBracket()) {
3130 // looks like angle pair are braces, not operators
3131 this->popBracket();
3132 } else {
3133 return reportError<bool>("malformed close angle bracket");
3134 }
3135 break;
3136 case '#': {
3137 if (fInCharCommentString || fInBrace) {
3138 break;
3139 }
3140 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3141 this->pushBracket(Bracket::kPound);
3142 break;
3143 }
3144 case ' ':
3145 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3146 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3147 fInBrace = fParent;
3148 // delimiting brackets are space ... unescaped-linefeed
3149 }
3150 case '&':
3151 case ',':
3152 case '+':
3153 case '-':
3154 case '!':
3155 if (fInCharCommentString || fInBrace) {
3156 break;
3157 }
3158 if (!this->checkForWord()) {
3159 return false;
3160 }
3161 break;
3162 case '=':
3163 if (fInCharCommentString || fInBrace) {
3164 break;
3165 }
3166 if (!this->checkForWord()) {
3167 return false;
3168 }
3169 if (!fParent->fTokens.size()) {
3170 break;
3171 }
3172 {
3173 const Definition& lastToken = fParent->fTokens.back();
3174 if (lastToken.fType != Definition::Type::kWord) {
3175 break;
3176 }
3177 string name(lastToken.fContentStart, lastToken.length());
3178 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3179 break;
3180 }
3181 // find token on start of line
3182 auto lineIter = fParent->fTokens.end();
3183 do {
3184 if (fParent->fTokens.begin() == lineIter) {
3185 break;
3186 }
3187 --lineIter;
3188 } while (lineIter->fContentStart > fLine);
3189 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
3190 ++lineIter;
3191 }
3192 Definition* lineStart = &*lineIter;
3193 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3194 bool sawConst = false;
3195 bool sawStatic = false;
3196 bool sawTemplate = false;
3197 bool sawType = false;
3198 while (&lastToken != &*lineIter) {
3199 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3200 if (sawConst || sawStatic || sawTemplate) {
3201 sawConst = false;
3202 break;
3203 }
3204 if (&lastToken == &*++lineIter) {
3205 break;
3206 }
3207 if (KeyWord::kTypename != lineIter->fKeyWord) {
3208 break;
3209 }
3210 if (&lastToken == &*++lineIter) {
3211 break;
3212 }
3213 if (Definition::Type::kWord != lineIter->fType) {
3214 break;
3215 }
3216 sawTemplate = true;
3217 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3218 if (sawConst || sawStatic) {
3219 sawConst = false;
3220 break;
3221 }
3222 sawStatic = true;
3223 } else if (KeyWord::kConst == lineIter->fKeyWord
3224 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3225 if (sawConst) {
3226 sawConst = false;
3227 break;
3228 }
3229 sawConst = true;
3230 } else {
3231 if (sawType) {
3232 sawType = false;
3233 break;
3234 }
3235 if (Definition::Type::kKeyWord == lineIter->fType
3236 && KeyProperty::kNumber
3237 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3238 sawType = true;
3239 } else if (Definition::Type::kWord == lineIter->fType) {
3240 string typeName(lineIter->fContentStart, lineIter->length());
3241 if ("Sk" != name.substr(0, 2)) {
3242 sawType = true;
3243 }
3244 }
3245 }
3246 ++lineIter;
3247 }
3248 if (sawType && sawConst) {
3249 // if found, name first
3250 lineStart->fName = name;
3251 lineStart->fMarkType = MarkType::kConst;
3252 this->checkName(lineStart);
3253 fParent->fChildren.emplace_back(lineStart);
3254 fConstExpr = lineStart;
3255 }
3256 }
3257 break;
3258 case ';':
3259 if (fInCharCommentString || fInBrace) {
3260 break;
3261 }
3262 if (!this->checkForWord()) {
3263 return false;
3264 }
3265 if (fConstExpr) {
3266 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3267 fConstExpr = nullptr;
3268 }
3269 if (Definition::Type::kKeyWord == fParent->fType
3270 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
3271 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3272 if (parentIsClass && fParent->fParent &&
3273 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3274 this->popObject();
3275 }
3276 if (KeyWord::kEnum == fParent->fKeyWord) {
3277 fInEnum = false;
3278 }
3279 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
3280 this->popObject();
3281 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3282 this->popObject();
3283 }
3284 fPriorEnum = nullptr;
3285 } else if (Definition::Type::kBracket == fParent->fType
3286 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3287 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3288 list<Definition>::iterator baseIter = fParent->fTokens.end();
3289 list<Definition>::iterator namedIter = fParent->fTokens.end();
3290 for (auto tokenIter = fParent->fTokens.end();
3291 fParent->fTokens.begin() != tokenIter; ) {
3292 --tokenIter;
3293 if (tokenIter->fLineCount == fLineCount) {
3294 if (this->isMember(*tokenIter)) {
3295 if (namedIter != fParent->fTokens.end()) {
3296 return reportError<bool>("found two named member tokens");
3297 }
3298 namedIter = tokenIter;
3299 }
3300 baseIter = tokenIter;
3301 } else {
3302 break;
3303 }
3304 }
3305 // FIXME: if a member definition spans multiple lines, this won't work
3306 if (namedIter != fParent->fTokens.end()) {
3307 if (baseIter == namedIter) {
3308 return this->reportError<bool>("expected type before named token");
3309 }
3310 Definition* member = &*namedIter;
3311 member->fMarkType = MarkType::kMember;
3312 if (!member->fTerminator) {
3313 member->fTerminator = member->fContentEnd;
3314 }
3315 fParent->fChildren.push_back(member);
3316 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3317 member->fChildren.push_back(&*nameType);
3318 }
3319 }
3320 fPriorEnum = nullptr;
3321 } else if (fParent->fChildren.size() > 0) {
3322 auto lastIter = fParent->fChildren.end();
3323 Definition* priorEnum = fPriorEnum;
3324 fPriorEnum = nullptr;
3325 if (!priorEnum) {
3326 while (fParent->fChildren.begin() != lastIter) {
3327 std::advance(lastIter, -1);
3328 priorEnum = *lastIter;
3329 if (Definition::Type::kBracket != priorEnum->fType ||
3330 (Bracket::kSlashSlash != priorEnum->fBracket
3331 && Bracket::kSlashStar != priorEnum->fBracket)) {
3332 break;
3333 }
3334 }
3335 fPriorIndex = priorEnum->fParentIndex;
3336 }
3337 if (Definition::Type::kKeyWord == priorEnum->fType
3338 && KeyWord::kEnum == priorEnum->fKeyWord) {
3339 auto tokenWalker = fParent->fTokens.begin();
3340 std::advance(tokenWalker, fPriorIndex);
3341 while (tokenWalker != fParent->fTokens.end()) {
3342 std::advance(tokenWalker, 1);
3343 ++fPriorIndex;
3344 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3345 break;
3346 }
3347 }
3348 while (tokenWalker != fParent->fTokens.end()) {
3349 std::advance(tokenWalker, 1);
3350 const Definition* test = &*tokenWalker;
3351 if (Definition::Type::kBracket != test->fType ||
3352 (Bracket::kSlashSlash != test->fBracket
3353 && Bracket::kSlashStar != test->fBracket)) {
3354 break;
3355 }
3356 }
3357 auto saveTokenWalker = tokenWalker;
3358 Definition* start = &*tokenWalker;
3359 bool foundExpected = true;
3360 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3361 const Definition* test = &*tokenWalker;
3362 if (expected != test->fKeyWord) {
3363 foundExpected = false;
3364 break;
3365 }
3366 if (tokenWalker == fParent->fTokens.end()) {
3367 break;
3368 }
3369 std::advance(tokenWalker, 1);
3370 }
3371 if (!foundExpected) {
3372 foundExpected = true;
3373 tokenWalker = saveTokenWalker;
3374 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3375 const Definition* test = &*tokenWalker;
3376 if (expected != test->fKeyWord) {
3377 foundExpected = false;
3378 break;
3379 }
3380 if (tokenWalker == fParent->fTokens.end()) {
3381 break;
3382 }
3383 if (KeyWord::kNone != expected) {
3384 std::advance(tokenWalker, 1);
3385 }
3386 }
3387 if (foundExpected) {
3388 auto nameToken = priorEnum->fTokens.begin();
3389 string enumName = string(nameToken->fContentStart,
3390 nameToken->fContentEnd - nameToken->fContentStart);
3391 const Definition* test = &*tokenWalker;
3392 string constType = string(test->fContentStart,
3393 test->fContentEnd - test->fContentStart);
3394 if (enumName != constType) {
3395 foundExpected = false;
3396 } else {
3397 std::advance(tokenWalker, 1);
3398 }
3399 }
3400 }
3401 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3402 const char* nameStart = tokenWalker->fStart;
3403 std::advance(tokenWalker, 1);
3404 if (tokenWalker != fParent->fTokens.end()) {
3405 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
3406 tp.skipToNonName();
3407 start->fName = string(nameStart, tp.fChar - nameStart);
3408 this->checkName(start);
3409 start->fContentEnd = fChar;
3410 priorEnum->fChildren.emplace_back(start);
3411 fPriorEnum = priorEnum;
3412 }
3413 }
3414 }
3415 }
3416 this->addPunctuation(Punctuation::kSemicolon);
3417 fInFunction = false;
3418 break;
3419 case '~':
3420 if (fInEnum) {
3421 break;
3422 }
3423 case '0': case '1': case '2': case '3': case '4':
3424 case '5': case '6': case '7': case '8': case '9':
3425 // TODO: don't want to parse numbers, but do need to track for enum defs
3426 // break;
3427 case 'A': case 'B': case 'C': case 'D': case 'E':
3428 case 'F': case 'G': case 'H': case 'I': case 'J':
3429 case 'K': case 'L': case 'M': case 'N': case 'O':
3430 case 'P': case 'Q': case 'R': case 'S': case 'T':
3431 case 'U': case 'V': case 'W': case 'X': case 'Y':
3432 case 'Z': case '_':
3433 case 'a': case 'b': case 'c': case 'd': case 'e':
3434 case 'f': case 'g': case 'h': case 'i': case 'j':
3435 case 'k': case 'l': case 'm': case 'n': case 'o':
3436 case 'p': case 'q': case 'r': case 's': case 't':
3437 case 'u': case 'v': case 'w': case 'x': case 'y':
3438 case 'z':
3439 if (fInCharCommentString || fInBrace) {
3440 break;
3441 }
3442 if (!fIncludeWord) {
3443 fIncludeWord = fChar;
3444 }
3445 break;
3446 }
3447 done:
3448 fPrev = test;
3449 this->next();
3450 return true;
3451 }
3452
validate() const3453 void IncludeParser::validate() const {
3454 IncludeParser::ValidateKeyWords();
3455 }
3456
references(const SkString & file) const3457 bool IncludeParser::references(const SkString& file) const {
3458 // if includes weren't passed one at a time, assume all references are valid
3459 if (fIncludeMap.empty()) {
3460 return true;
3461 }
3462 SkASSERT(file.endsWith(".bmh") );
3463 string root(file.c_str(), file.size() - 4);
3464 string kReference("_Reference");
3465 if (string::npos != root.find(kReference)) {
3466 root = root.substr(0, root.length() - kReference.length());
3467 }
3468 if (fIClassMap.end() != fIClassMap.find(root)) {
3469 return true;
3470 }
3471 if (fIStructMap.end() != fIStructMap.find(root)) {
3472 return true;
3473 }
3474 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3475 return true;
3476 }
3477 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3478 return true;
3479 }
3480 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3481 return true;
3482 }
3483 return false;
3484 }
3485
RemoveFile(const char * docs,const char * includes)3486 void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3487 if (!sk_isdir(includes)) {
3488 IncludeParser::RemoveOneFile(docs, includes);
3489 } else {
3490 SkOSFile::Iter it(includes, ".h");
3491 for (SkString file; it.next(&file); ) {
3492 SkString p = SkOSPath::Join(includes, file.c_str());
3493 const char* hunk = p.c_str();
3494 if (!SkStrEndsWith(hunk, ".h")) {
3495 continue;
3496 }
3497 IncludeParser::RemoveOneFile(docs, hunk);
3498 }
3499 }
3500 }
3501
RemoveOneFile(const char * docs,const char * includesFile)3502 void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3503 const char* lastForward = strrchr(includesFile, '/');
3504 const char* lastBackward = strrchr(includesFile, '\\');
3505 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3506 if (!last) {
3507 last = includesFile;
3508 } else {
3509 last += 1;
3510 }
3511 SkString baseName(last);
3512 SkASSERT(baseName.endsWith(".h"));
3513 baseName.remove(baseName.size() - 2, 2);
3514 baseName.append("_Reference.bmh");
3515 SkString fullName = docs ? SkOSPath::Join(docs, baseName.c_str()) : baseName;
3516 remove(fullName.c_str());
3517 }
3518
3519 static const char kMethodMissingStr[] =
3520 "If the method requires documentation, add to "
3521 "%s at minimum:\n" // path to bmh file
3522 "\n"
3523 "#Method %s\n" // method declaration less implementation details
3524 "#In SomeSubtopicName\n"
3525 "#Line # add a one line description here ##\n"
3526 "#Populate\n"
3527 "#NoExample\n"
3528 "// or better yet, use #Example and put C++ code here\n"
3529 "##\n"
3530 "#SeeAlso optional related symbols\n"
3531 "#Method ##\n"
3532 "\n"
3533 "Add to %s, at minimum:\n" // path to include
3534 "\n"
3535 "/** (description) Starts with present tense action verb\n"
3536 " and end with a period.\n"
3537 "%s" // @param, @return if needed go here
3538 "*/\n"
3539 "%s ...\n" // method declaration
3540 "\n"
3541 "If the method does not require documentation,\n"
3542 "add \"private\" or \"experimental\", as in:\n"
3543 "\n"
3544 "/** Experimental, do not use. And so on...\n"
3545 "*/\n"
3546 "%s ...\n" // method declaration
3547 "\n"
3548 ;
3549
3550 // bDef does not have #Populate
3551 static const char kMethodDiffersNoPopStr[] =
3552 "In %s:\n" // path to bmh file
3553 "#Method %s\n" // method declaration less implementation details
3554 "does not match doxygen comment of:\n"
3555 "%s.\n" // method declaration
3556 "\n"
3557 ;
3558
3559 static const char kMethodDiffersStr[] =
3560 "In %s:\n" // path to include
3561 "%s\n" // method declaration
3562 "does not match doxygen comment.\n"
3563 "\n"
3564 ;
3565
suggestFix(Suggest suggest,const Definition & iDef,const RootDefinition * root,const Definition * bDef)3566 void IncludeParser::suggestFix(Suggest suggest, const Definition& iDef,
3567 const RootDefinition* root, const Definition* bDef) {
3568 string methodNameStr(iDef.fContentStart, iDef.length());
3569 const char* methodName = methodNameStr.c_str();
3570 TextParser lessImplParser(&iDef);
3571 if (lessImplParser.skipExact("static")) {
3572 lessImplParser.skipWhiteSpace();
3573 }
3574 // TODO : handle debug wrapper
3575 /* bool inDebugWrapper = */ Definition::SkipImplementationWords(lessImplParser);
3576 string lessImplStr(lessImplParser.fChar, lessImplParser.fEnd - lessImplParser.fChar);
3577 const char* methodNameLessImpl = lessImplStr.c_str();
3578 // return result, if any is substr from 0 to location of iDef.fName
3579 size_t namePos = methodNameStr.find(iDef.fName);
3580 SkASSERT(string::npos != namePos);
3581 size_t funcEnd = namePos;
3582 while (funcEnd > 0 && ' ' >= methodNameStr[funcEnd - 1]) {
3583 funcEnd -= 1;
3584 }
3585 string funcRet = methodNameStr.substr(0, funcEnd);
3586 // parameters, if any, are delimited by () and separate by ,
3587 TextParser parser(&iDef);
3588 parser.fChar += namePos + iDef.fName.length();
3589 const char* start = parser.fChar;
3590 vector<string> paramStrs;
3591 if ('(' == start[0]) {
3592 parser.skipToBalancedEndBracket('(', ')');
3593 TextParser params(&iDef);
3594 params.fChar = start + 1;
3595 params.fEnd = parser.fChar;
3596 while (!params.eof()) {
3597 const char* paramEnd = params.anyOf("=,)");
3598 const char* paramStart = paramEnd;
3599 while (paramStart > params.fChar && ' ' >= paramStart[-1]) {
3600 paramStart -= 1;
3601 }
3602 while (paramStart > params.fChar && (isalnum(paramStart[-1])
3603 || '_' == paramStart[-1])) {
3604 paramStart -= 1;
3605 }
3606 string param(paramStart, paramEnd - paramStart);
3607 paramStrs.push_back(param);
3608 params.fChar = params.anyOf(",)") + 1;
3609 }
3610 }
3611 string bmhFile = root ? root->fFileName : bDef ? bDef->fFileName : "a *.bmh file";
3612 bool hasFuncReturn = "" != funcRet && "void" != funcRet;
3613 switch(suggest) {
3614 case Suggest::kMethodMissing: {
3615 // if include @param, @return are missing, request them as well
3616 string paramDox;
3617 bool firstParam = true;
3618 for (auto paramStr : paramStrs) {
3619 if (firstParam) {
3620 paramDox += "\n";
3621 firstParam = false;
3622 }
3623 paramDox += " @param " + paramStr + " descriptive phrase\n";
3624 }
3625 if (hasFuncReturn) {
3626 paramDox += "\n";
3627 paramDox += " @return descriptive phrase\n";
3628 }
3629 SkDebugf(kMethodMissingStr, bmhFile.c_str(), methodNameLessImpl, iDef.fFileName.c_str(),
3630 paramDox.c_str(), methodName, methodName);
3631 } break;
3632 case Suggest::kMethodDiffers: {
3633 bool hasPop = std::any_of(bDef->fChildren.begin(), bDef->fChildren.end(),
3634 [](Definition* def) { return MarkType::kPopulate == def->fMarkType; });
3635 if (!hasPop) {
3636 SkDebugf(kMethodDiffersNoPopStr, bmhFile.c_str(), methodNameLessImpl, methodName);
3637 }
3638 SkDebugf(kMethodDiffersStr, iDef.fFileName.c_str(), methodName);
3639 } break;
3640 default:
3641 SkASSERT(0);
3642 }
3643 }
3644
topBracket() const3645 Bracket IncludeParser::topBracket() const {
3646 Definition* parent = this->parentBracket(fParent);
3647 return parent ? parent->fBracket : Bracket::kNone;
3648 }
3649