blob: 50ba723ea1a7801454a9adef28cfb0b7c2714117 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
Cary Clark2dc84ad2018-01-26 12:56:22 -05009#include "SkOSFile.h"
10#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040011
Cary Clark89b14562018-03-19 09:04:10 -040012const char IncludeParser::gAttrDeprecated[] = "SK_ATTR_DEPRECATED";
13const size_t IncludeParser::kAttrDeprecatedLen = sizeof(gAttrDeprecated) - 1;
14
Cary Clark8032b982017-07-28 11:04:54 -040015const IncludeKey kKeyWords[] = {
16 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040017 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040018 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clarkd2ca79c2018-08-10 13:09:13 -040019 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040020 { "bool", KeyWord::kBool, KeyProperty::kNumber },
21 { "char", KeyWord::kChar, KeyProperty::kNumber },
22 { "class", KeyWord::kClass, KeyProperty::kObject },
23 { "const", KeyWord::kConst, KeyProperty::kModifier },
24 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
25 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
26 { "double", KeyWord::kDouble, KeyProperty::kNumber },
27 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
28 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
29 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
30 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050031 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040032 { "float", KeyWord::kFloat, KeyProperty::kNumber },
33 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
34 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
35 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
36 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
37 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
38 { "inline", KeyWord::kInline, KeyProperty::kModifier },
39 { "int", KeyWord::kInt, KeyProperty::kNumber },
40 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
41 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
42 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
43 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
44 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
45 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
46 { "static", KeyWord::kStatic, KeyProperty::kModifier },
47 { "struct", KeyWord::kStruct, KeyProperty::kObject },
48 { "template", KeyWord::kTemplate, KeyProperty::kObject },
49 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clark0d225392018-06-07 09:59:07 -040050 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040051 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040052 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040053 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
54 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040055 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040056 { "union", KeyWord::kUnion, KeyProperty::kObject },
57 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
Cary Clark61313f32018-10-08 14:57:48 -040058 { "using", KeyWord::kUsing, KeyProperty::kObject },
Cary Clark8032b982017-07-28 11:04:54 -040059 { "void", KeyWord::kVoid, KeyProperty::kNumber },
60};
61
62const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
63
64KeyWord IncludeParser::FindKey(const char* start, const char* end) {
65 int ch = 0;
66 for (size_t index = 0; index < kKeyWordCount; ) {
67 if (start[ch] > kKeyWords[index].fName[ch]) {
68 ++index;
Cary Clark61313f32018-10-08 14:57:48 -040069 if (ch > 0 && (index == kKeyWordCount ||
70 kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
Cary Clark8032b982017-07-28 11:04:54 -040071 return KeyWord::kNone;
72 }
73 continue;
74 }
75 if (start[ch] < kKeyWords[index].fName[ch]) {
76 return KeyWord::kNone;
77 }
78 ++ch;
79 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040080 if (end - start < (int) strlen(kKeyWords[index].fName)) {
81 return KeyWord::kNone;
82 }
Cary Clark8032b982017-07-28 11:04:54 -040083 return kKeyWords[index].fKeyWord;
84 }
85 }
86 return KeyWord::kNone;
87}
88
89void IncludeParser::ValidateKeyWords() {
90 for (size_t index = 1; index < kKeyWordCount; ++index) {
91 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
92 == (int) kKeyWords[index].fKeyWord);
93 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
94 }
95}
96
97void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050098 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040099 fIncludeWord = nullptr;
100 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
101 Definition* def = &fParent->fTokens.back();
102 this->addDefinition(def);
103 if (KeyWord::kEnum == fParent->fKeyWord) {
104 fInEnum = true;
105 }
106 }
107}
108
Cary Clark61313f32018-10-08 14:57:48 -0400109static bool looks_like_method(const TextParser& tp) {
110 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
111 t.skipSpace();
112 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
113 && !t.skipExact("enum")) {
114 return true;
115 }
116 t.skipSpace();
117 if (t.skipExact("SK_API")) {
118 t.skipSpace();
119 }
120 if (!isupper(t.peek())) {
121 return true;
122 }
123 return nullptr != t.strnchr('(', t.fEnd);
124}
125
126static bool looks_like_forward_declaration(const TextParser& tp) {
127 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
128 t.skipSpace();
129 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
130 && !t.skipExact("enum")) {
131 return false;
132 }
133 t.skipSpace();
134 if (t.skipExact("SK_API")) {
135 t.skipSpace();
136 }
137 if (!isupper(t.peek())) {
138 return false;
139 }
140 t.skipToNonAlphaNum();
141 if (t.eof() || ';' != t.next()) {
142 return false;
143 }
144 if (t.eof() || '\n' != t.next()) {
145 return false;
146 }
147 return t.eof();
148}
149
150static bool looks_like_constructor(const TextParser& tp) {
151 TextParser t(tp.fFileName, tp.fLine, tp.lineEnd(), tp.fLineCount);
152 t.skipSpace();
153 if (!isupper(t.peek())) {
154 if (':' == t.next() && ' ' >= t.peek()) {
155 return true;
156 }
157 return false;
158 }
159 t.skipToNonAlphaNum();
160 if ('(' != t.peek()) {
161 return false;
162 }
163 if (!t.skipToEndBracket(')')) {
164 return false;
165 }
166 SkAssertResult(')' == t.next());
167 t.skipSpace();
168 return tp.fChar == t.fChar;
169}
170
171static bool looks_like_class_decl(const TextParser& tp) {
172 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
173 t.skipSpace();
174 if (!t.skipExact("class")) {
175 return false;
176 }
177 t.skipSpace();
178 if (t.skipExact("SK_API")) {
179 t.skipSpace();
180 }
181 if (!isupper(t.peek())) {
182 return false;
183 }
184 t.skipToNonAlphaNum();
185 return !t.skipToEndBracket('(');
186}
187
188static bool looks_like_const(const TextParser& tp) {
189 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
190 if (!t.startsWith("static constexpr ")) {
191 return false;
192 }
193 if (t.skipToEndBracket(" k")) {
194 SkAssertResult(t.skipExact(" k"));
195 } else if (t.skipToEndBracket(" SK_")) {
196 SkAssertResult(t.skipExact(" SK_"));
197 } else {
198 return false;
199 }
200 if (!isupper(t.peek())) {
201 return false;
202 }
203 return t.skipToEndBracket(" = ");
204}
205
206static bool looks_like_member(const TextParser& tp) {
207 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
208 const char* end = t.anyOf("(;");
209 if (!end || '(' == *end) {
210 return false;
211 }
212 bool foundMember = false;
213 do {
214 const char* next = t.anyOf(" ;");
215 if (';' == *next) {
216 break;
217 }
218 t.skipTo(next);
219 t.skipSpace();
220 foundMember = 'f' == t.fChar[0] && isupper(t.fChar[1]);
221 } while (true);
222 return foundMember;
223}
224
225static void skip_constructor_initializers(TextParser& t) {
226 SkAssertResult(':' == t.next());
227 do {
228 t.skipWhiteSpace();
229 t.skipToNonAlphaNum();
230 t.skipWhiteSpace();
231 if ('{' == t.peek()) {
232 t.skipToBalancedEndBracket('{', '}');
233 }
234 do {
235 const char* limiter = t.anyOf("(,{");
236 t.skipTo(limiter);
237 if ('(' != t.peek()) {
238 break;
239 }
240 t.skipToBalancedEndBracket('(', ')');
241 } while (true);
242 if ('{' == t.peek()) {
243 return;
244 }
245 SkAssertResult(',' == t.next());
246 } while (true);
247}
248
249static const char kInline[] = "inline ";
250static const char kSK_API[] = "SK_API ";
251static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
252
253bool IncludeParser::advanceInclude(TextParser& i) {
254 i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn);
255 if (fCheck.fPrivateBrace) {
256 if (i.startsWith("};")) {
257 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
258 fCheck.fPrivateBrace = 0;
259 fCheck.fDoubleReturn = true;
260 } else {
261 i.skipExact("};");
262 fCheck.fBraceCount -= 1;
263 }
264 return false;
265 }
266 if (i.startsWith("public:")) {
267 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
268 fCheck.fPrivateBrace = 0;
269 if (fCheck.fPrivateProtected) {
270 i.skipExact("public:");
271 }
272 } else {
273 i.skipExact("public:");
274 }
275 } else {
276 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
277 }
278 return false;
279 } else if (i.startsWith("};")) {
280 fCheck.fDoubleReturn = 2;
281 }
282 if (i.skipExact(kInline)) {
283 fCheck.fSkipInline = true;
284 return false;
285 }
286 if (i.skipExact(kSK_API)) {
287 fCheck.fSkipAPI = true;
288 return false;
289 }
290 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
291 fCheck.fSkipWarnUnused = true;
292 return false;
293 }
294 if (i.skipExact("SK_ATTR_DEPRECATED")) {
295 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
296 return false;
297 }
298 if (i.skipExact("SkDEBUGCODE")) {
299 i.skipWhiteSpace();
300 if ('(' != i.peek()) {
301 i.reportError("expected open paren");
302 }
303 TextParserSave save(&i);
304 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
305 fCheck.fInDebugCode = i.fChar - 1;
306 save.restore();
307 SkAssertResult('(' == i.next());
308 }
309 if ('{' == i.peek()) {
310 if (looks_like_method(i)) {
311 fCheck.fState = CheckCode::State::kMethod;
312 if (!i.skipToBalancedEndBracket('{', '}')) {
313 i.reportError("unbalanced open brace");
314 }
315 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
316 return false;
317 } else if (looks_like_class_decl(i)) {
318 fCheck.fState = CheckCode::State::kClassDeclaration;
319 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
320 fCheck.fPrivateProtected = false;
321 }
322 }
323 if (':' == i.peek() && looks_like_constructor(i)) {
324 fCheck.fState = CheckCode::State::kConstructor;
325 skip_constructor_initializers(i);
326 return false;
327 }
328 if ('#' == i.peek()) {
329 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
330 return false;
331 }
332 if (i.startsWith("//")) {
333 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
334 return false;
335 }
336 if (i.startsWith("/*")) {
337 i.skipToEndBracket("*/");
338 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
339 return false;
340 }
341 if (looks_like_forward_declaration(i)) {
342 fCheck.fState = CheckCode::State::kForwardDeclaration;
343 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
344 return false;
345 }
346 if (i.skipExact("private:") || i.skipExact("protected:")) {
347 if (!fCheck.fBraceCount) {
348 i.reportError("expect private in brace");
349 }
350 fCheck.fPrivateBrace = fCheck.fBraceCount;
351 fCheck.fPrivateProtected = true;
352 return false;
353 }
354 const char* funcEnd = i.anyOf("(\n");
355 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
356 && (i.contains("internal_", funcEnd, nullptr)
357 || i.contains("private_", funcEnd, nullptr)
358 || i.contains("legacy_", funcEnd, nullptr)
359 || i.contains("temporary_", funcEnd, nullptr))) {
360 i.skipTo(funcEnd);
361 if (!i.skipToBalancedEndBracket('(', ')')) {
362 i.reportError("unbalanced open parent");
363 }
364 i.skipSpace();
365 i.skipExact("const ");
366 i.skipSpace();
367 if (';' == i.peek()) {
368 i.next();
369 }
370 fCheck.fState = CheckCode::State::kNone;
371 return false;
372 }
373 return true;
374}
375
376void IncludeParser::codeBlockAppend(string& result, char ch) const {
377 if (Elided::kYes == fElided && fCheck.fBraceCount) {
378 return;
379 }
380 this->stringAppend(result, ch);
381}
382
383void IncludeParser::codeBlockSpaces(string& result, int indent) const {
384 if (!indent) {
385 return;
386 }
387 if (Elided::kYes == fElided && fCheck.fBraceCount) {
388 return;
389 }
390 SkASSERT(indent > 0);
391 if (fDebugWriteCodeBlock) {
392 SkDebugf("%*c", indent, ' ');
393 }
394 result.append(indent, ' ');
395}
396
397string IncludeParser::writeCodeBlock(const Definition& iDef, MarkType markType) {
398 TextParser i(&iDef);
399 fElided = Elided::kNo;
400 const char* before = iDef.fContentStart;
401 while (' ' == *--before)
402 ;
403 int startIndent = iDef.fContentStart - before - 1;
404 return writeCodeBlock(i, markType, startIndent);
405}
406
407string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
408 string result;
409 char last;
410 int lastIndent = 0;
411 bool lastDoubleMeUp = false;
412 fCheck.reset();
413 this->codeBlockSpaces(result, startIndent);
414 do {
415 if (!this->advanceInclude(i)) {
416 continue;
417 }
418 do {
419 last = i.peek();
420 SkASSERT(' ' < last);
421 if (fCheck.fInDebugCode == i.fChar) {
422 fCheck.fInDebugCode = nullptr;
423 i.next(); // skip close paren
424 break;
425 }
426 if (CheckCode::State::kMethod == fCheck.fState) {
427 this->codeBlockAppend(result, ';');
428 fCheck.fState = CheckCode::State::kNone;
429 }
430 if (fCheck.fWriteReturn) {
431 this->codeBlockAppend(result, '\n');
432 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
433 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
434 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
435 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
436 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
437 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
438 this->codeBlockAppend(result, '\n');
439 }
440 fCheck.fTypedefReturn = false;
441 lastDoubleMeUp = doubleMeUp;
442 }
443 if (doubleMeUp) {
444 fCheck.fTypedefReturn = true;
445 }
446 lastIndent = fCheck.fIndent;
447 }
448 if (fCheck.fIndent) {
449 size_t indent = fCheck.fIndent;
450 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
451 indent -= sizeof(kInline) - 1;
452 }
453 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
454 indent -= sizeof(kSK_API) - 1;
455 }
456 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
457 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
458 }
459
460 this->codeBlockSpaces(result, indent);
461 }
462 this->codeBlockAppend(result, last);
463 fCheck.fWriteReturn = false;
464 fCheck.fIndent = 0;
465 fCheck.fBraceCount += '{' == last;
466 fCheck.fBraceCount -= '}' == last;
467 if (';' == last) {
468 fCheck.fSkipInline = false;
469 fCheck.fSkipAPI = false;
470 fCheck.fSkipWarnUnused = false;
471 }
472 if (fCheck.fBraceCount < 0) {
473 i.reportError("unbalanced close brace");
474 return result;
475 }
476 i.next();
477 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
478 } while (!i.eof());
479 if (CheckCode::State::kMethod == fCheck.fState) {
480 this->codeBlockAppend(result, ';');
481 }
482 bool elidedTemplate = Elided::kYes == fElided && !strncmp(i.fStart, "template ", 9);
483 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
484 if (fCheck.fWriteReturn || elidedTClass) {
485 this->codeBlockAppend(result, '\n');
486 }
487 if ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass) {
488 this->codeBlockAppend(result, '}');
489 }
490 this->codeBlockAppend(result, ';');
491 if (MarkType::kFunction != markType || elidedTemplate) {
492 this->codeBlockAppend(result, '\n');
493 }
494 return result;
495}
496
Ben Wagner63fd7602017-10-09 15:45:33 -0400497void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400498 const vector<string>& foundParams) {
499 for (auto& methodParam : methodParams) {
500 bool found = false;
501 for (auto& foundParam : foundParams) {
502 if (methodParam == foundParam) {
503 found = true;
504 break;
505 }
506 }
507 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400508 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400509 }
510 }
511 for (auto& foundParam : foundParams) {
512 bool found = false;
513 for (auto& methodParam : methodParams) {
514 if (methodParam == foundParam) {
515 found = true;
516 break;
517 }
518 }
519 if (!found) {
520 this->reportError("doxygen param does not match method declaration");
521 }
522 }
523}
524
525bool IncludeParser::checkForWord() {
526 if (!fIncludeWord) {
527 return true;
528 }
529 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400530 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
531 Bracket bracket = this->topBracket();
532 if (Bracket::kParen == bracket) {
533 return true;
534 }
535 }
Cary Clark8032b982017-07-28 11:04:54 -0400536 if (KeyWord::kNone != keyWord) {
537 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
538 this->addKeyword(keyWord);
539 return true;
540 }
541 } else {
542 this->addWord();
543 return true;
544 }
545 Definition* poundDef = fParent;
546 if (!fParent) {
547 return reportError<bool>("expected parent");
548 }
549 if (Definition::Type::kBracket != poundDef->fType) {
550 return reportError<bool>("expected bracket");
551 }
552 if (Bracket::kPound != poundDef->fBracket) {
553 return reportError<bool>("expected preprocessor");
554 }
555 if (KeyWord::kNone != poundDef->fKeyWord) {
556 return reportError<bool>("already found keyword");
557 }
558 poundDef->fKeyWord = keyWord;
559 fIncludeWord = nullptr;
560 switch (keyWord) {
561 // these do not link to other # directives
562 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400563 if (!fInBrace) {
564 SkASSERT(!fInDefine);
565 fInDefine = true;
566 }
Cary Clark8032b982017-07-28 11:04:54 -0400567 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500568 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400569 break;
570 // these start a # directive link
571 case KeyWord::kIf:
572 case KeyWord::kIfdef:
573 case KeyWord::kIfndef:
574 break;
575 // these continue a # directive link
576 case KeyWord::kElif:
577 case KeyWord::kElse: {
578 this->popObject(); // pop elif
579 if (Bracket::kPound != fParent->fBracket) {
580 return this->reportError<bool>("expected preprocessor directive");
581 }
582 this->popBracket(); // pop if
583 poundDef->fParent = fParent;
584 this->addDefinition(poundDef); // push elif back
585 } break;
586 // this ends a # directive link
587 case KeyWord::kEndif:
588 // FIXME : should this be calling popBracket() instead?
589 this->popObject(); // pop endif
590 if (Bracket::kPound != fParent->fBracket) {
591 return this->reportError<bool>("expected preprocessor directive");
592 }
593 this->popBracket(); // pop if/else
594 break;
595 default:
596 SkASSERT(0);
597 }
598 return true;
599}
600
601string IncludeParser::className() const {
602 string name(fParent->fName);
603 size_t slash = name.find_last_of("/");
604 if (string::npos == slash) {
605 slash = name.find_last_of("\\");
606 }
607 SkASSERT(string::npos != slash);
608 string result = name.substr(slash);
609 result = result.substr(1, result.size() - 3);
610 return result;
611}
612
Cary Clark61313f32018-10-08 14:57:48 -0400613void IncludeParser::writeCodeBlock(const BmhParser& bmhParser) {
614 for (auto& classMapper : fIClassMap) {
615 string className = classMapper.first;
616 auto finder = bmhParser.fClassMap.find(className);
617 if (bmhParser.fClassMap.end() != finder) {
618 classMapper.second.fCode = this->writeCodeBlock(classMapper.second, MarkType::kClass);
619 continue;
620 }
621 SkASSERT(string::npos != className.find("::"));
622 }
623 for (auto& enumMapper : fIEnumMap) {
624 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second,
625 enumMapper.second->fMarkType);
626 }
627}
628
Cary Clark884dd7d2017-10-11 10:37:52 -0400629#include <sstream>
630#include <iostream>
631
Cary Clark8032b982017-07-28 11:04:54 -0400632bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400633 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400634 string className = classMapper.first;
635 auto finder = bmhParser.fClassMap.find(className);
636 if (bmhParser.fClassMap.end() == finder) {
637 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400638 continue;
639 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400640 }
641 for (auto& classMapper : fIClassMap) {
642 string className = classMapper.first;
643 std::istringstream iss(className);
644 string classStr;
645 string classBase;
646 RootDefinition* root = nullptr;
647 while (std::getline(iss, classStr, ':')) {
648 if (root) {
649 if (!classStr.length()) {
650 continue;
651 }
652 classBase += "::" + classStr;
653 auto finder = root->fBranches.find(classBase);
654 if (root->fBranches.end() != finder) {
655 root = finder->second;
656 } else {
657 SkASSERT(0);
658 }
659 } else {
660 classBase = classStr;
661 auto finder = bmhParser.fClassMap.find(classBase);
662 if (bmhParser.fClassMap.end() != finder) {
663 root = &finder->second;
664 } else {
665 SkASSERT(0);
666 }
667 }
668 }
Cary Clark8032b982017-07-28 11:04:54 -0400669 auto& classMap = classMapper.second;
670 auto& tokens = classMap.fTokens;
671 for (const auto& token : tokens) {
672 if (token.fPrivate) {
673 continue;
674 }
675 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400676 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400677 switch (token.fMarkType) {
678 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400679 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400680 continue;
681 }
Cary Clark8032b982017-07-28 11:04:54 -0400682 if (!def) {
683 string paramName = className + "::";
684 paramName += string(token.fContentStart,
685 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400686 if (string::npos != paramName.find('\n')) {
687 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
688 paramName.end());
689 }
Cary Clarkce101242017-09-01 15:51:02 -0400690 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400691 if (!def && 0 == token.fName.find("operator")) {
692 string operatorName = className + "::";
693 TextParser oper("", token.fStart, token.fContentEnd, 0);
694 const char* start = oper.strnstr("operator", token.fContentEnd);
695 SkASSERT(start);
696 oper.skipTo(start);
697 oper.skipToEndBracket('(');
698 int parens = 0;
699 do {
700 if ('(' == oper.peek()) {
701 ++parens;
702 } else if (')' == oper.peek()) {
703 --parens;
704 }
705 } while (!oper.eof() && oper.next() && parens > 0);
706 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400707 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400708 }
709 }
710 if (!def) {
711 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
712 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400713 const char* tokenEnd = token.methodEnd();
Cary Clark8032b982017-07-28 11:04:54 -0400714 string constructorName = className + "::";
715 constructorName += string(token.fContentStart + skip,
Cary Clarkab5c9af2018-07-12 16:24:53 -0400716 tokenEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400717 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400718 }
719 if (!def && 0 == token.fName.find("SK_")) {
720 string incName = token.fName + "()";
721 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400722 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400723 if (def) {
724 if (def->fName == incName) {
725 def->fVisited = true;
726 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400727 def = root->find(className + "::toString",
728 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400729 if (def) {
730 def->fVisited = true;
731 } else {
732 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500733 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400734 }
735 }
736 break;
737 } else {
738 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500739 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400740 }
741 }
742 }
743 if (!def) {
744 bool allLower = true;
745 for (size_t index = 0; index < token.fName.length(); ++index) {
746 if (!islower(token.fName[index])) {
747 allLower = false;
748 break;
749 }
750 }
751 if (allLower) {
752 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400753 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400754 }
755 }
756 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400757 if (gAttrDeprecated == token.fName) {
758 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400759 break;
760 }
761 if (0 == token.fName.find("SkDEBUGCODE")) {
762 break;
763 }
764 }
765 if (!def) {
766 // simple method names inside nested classes have a bug and are missing trailing parens
767 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400768 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400769 }
770 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500771 if (!root->fDeprecated) {
772 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
773 fFailed = true;
774 }
Cary Clark8032b982017-07-28 11:04:54 -0400775 break;
776 }
Cary Clark73fa9722017-08-29 17:36:51 -0400777 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400778 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400779 if (token.fDeprecated && !def->fDeprecated) {
780 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
781 }
Cary Clark8032b982017-07-28 11:04:54 -0400782 } else {
783 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500784 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400785 }
786 } break;
787 case MarkType::kComment:
788 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400789 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400790 case MarkType::kEnum: {
791 if (!def) {
792 // work backwards from first word to deduce #Enum name
793 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
794 SkAssertResult(firstMember.skipName("enum"));
795 SkAssertResult(firstMember.skipToEndBracket('{'));
796 firstMember.next();
797 firstMember.skipWhiteSpace();
798 SkASSERT('k' == firstMember.peek());
799 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400800 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400801 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400802 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400803 const char* lastUnderscore = nullptr;
804 do {
805 if (!firstMember.skipToEndBracket('_')) {
806 break;
807 }
808 if (firstMember.fChar > wordEnd) {
809 break;
810 }
811 lastUnderscore = firstMember.fChar;
812 } while (firstMember.next());
813 if (lastUnderscore) {
814 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400815 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400816 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400817 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400818 }
819 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500820 if (!root->fDeprecated) {
821 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
822 fFailed = true;
823 }
Cary Clark8032b982017-07-28 11:04:54 -0400824 break;
825 }
826 }
827 def->fVisited = true;
Cary Clark61313f32018-10-08 14:57:48 -0400828 bool hasCode = false;
829 bool hasPopulate = true;
Cary Clark8032b982017-07-28 11:04:54 -0400830 for (auto& child : def->fChildren) {
831 if (MarkType::kCode == child->fMarkType) {
Cary Clark61313f32018-10-08 14:57:48 -0400832 hasPopulate = std::any_of(child->fChildren.begin(),
833 child->fChildren.end(), [](auto grandChild){
834 return MarkType::kPopulate == grandChild->fMarkType; });
835 if (!hasPopulate) {
836 def = child;
837 }
838 hasCode = true;
Cary Clark8032b982017-07-28 11:04:54 -0400839 break;
840 }
841 }
Cary Clark61313f32018-10-08 14:57:48 -0400842 if (!hasCode) {
Cary Clark56356312018-02-08 14:45:18 -0500843 if (!root->fDeprecated) {
844 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
845 fFailed = true;
846 }
Cary Clark8032b982017-07-28 11:04:54 -0400847 break;
848 }
Cary Clark61313f32018-10-08 14:57:48 -0400849 if (!hasPopulate) {
850 if (def->crossCheck(token)) {
851 def->fVisited = true;
852 } else {
853 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
854 fFailed = true;
855 }
Cary Clark8032b982017-07-28 11:04:54 -0400856 }
857 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400858 string constName = MarkType::kEnumClass == token.fMarkType ?
859 fullName : className;
860 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400861 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400862 if (!def) {
863 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400864 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400865 }
866 if (!def) {
867 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500868 if (!root->fDeprecated) {
869 SkDebugf("const missing from bmh: %s\n", constName.c_str());
870 fFailed = true;
871 }
Cary Clark8032b982017-07-28 11:04:54 -0400872 }
873 } else {
874 def->fVisited = true;
875 }
876 }
877 } break;
878 case MarkType::kMember:
879 if (def) {
880 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500881 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400882 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500883 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400884 }
885 break;
Cary Clark2f466242017-12-11 16:03:17 -0500886 case MarkType::kTypedef:
887 if (def) {
888 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500889 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500890 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500891 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500892 }
893 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400894 case MarkType::kConst:
895 if (def) {
896 def->fVisited = true;
897 } else if (!root->fDeprecated) {
898 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
899 fFailed = true;
900 }
901 break;
Cary Clark8032b982017-07-28 11:04:54 -0400902 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400903 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400904 break;
905 }
906 }
907 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500908 int crossChecks = 0;
909 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400910 for (auto& classMapper : fIClassMap) {
911 string className = classMapper.first;
912 auto finder = bmhParser.fClassMap.find(className);
913 if (bmhParser.fClassMap.end() == finder) {
914 continue;
915 }
916 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500917 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500918 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400919 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500920 if (crossChecks) {
921 SkDebugf(".");
922 } else {
923 SkDebugf("cross-check");
924 firstCheck = className;
925 }
926 ++crossChecks;
927 }
928 if (crossChecks) {
929 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500930 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500931 }
932 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400933 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400934 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500935 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400936}
937
938IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400939 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400940 string className;
941 const Definition* test = fParent;
942 while (Definition::Type::kFileType != test->fType) {
943 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
944 className = test->fName + "::";
945 break;
946 }
947 test = test->fParent;
948 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400949 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400950 unordered_map<string, IClassDefinition>& map = fIClassMap;
951 IClassDefinition& markupDef = map[className];
952 if (markupDef.fStart) {
953 typedef IClassDefinition* IClassDefPtr;
954 return INHERITED::reportError<IClassDefPtr>("class already defined");
955 }
956 markupDef.fFileName = fFileName;
957 markupDef.fStart = includeDef.fStart;
958 markupDef.fContentStart = includeDef.fStart;
959 markupDef.fName = className;
960 markupDef.fContentEnd = includeDef.fContentEnd;
961 markupDef.fTerminator = includeDef.fTerminator;
962 markupDef.fParent = fParent;
963 markupDef.fLineCount = fLineCount;
964 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
965 MarkType::kStruct : MarkType::kClass;
966 markupDef.fKeyWord = includeDef.fKeyWord;
967 markupDef.fType = Definition::Type::kMark;
968 fParent = &markupDef;
969 return &markupDef;
970}
971
972void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
973 auto& tokens = classDef.fTokens;
974 for (auto& token : tokens) {
975 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
976 continue;
977 }
978 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400979 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400980 }
981 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400982 case MarkType::kConst:
983 this->dumpConst(token, classDef.fName);
984 break;
Cary Clark8032b982017-07-28 11:04:54 -0400985 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500986 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500987 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400988 break;
989 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400990 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400991 break;
992 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400993 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400994 continue;
995 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400996 case MarkType::kTypedef:
997 this->dumpTypedef(token, classDef.fName);
998 break;
Cary Clark8032b982017-07-28 11:04:54 -0400999 default:
1000 SkASSERT(0);
1001 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001002 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -04001003 }
1004}
Cary Clark9174bda2017-09-19 17:39:32 -04001005void IncludeParser::dumpComment(const Definition& token) {
1006 fLineCount = token.fLineCount;
1007 fChar = fLine = token.fContentStart;
1008 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001009 bool sawParam = false;
1010 bool multiline = false;
1011 bool sawReturn = false;
1012 bool sawComment = false;
1013 bool methodHasReturn = false;
1014 vector<string> methodParams;
1015 vector<string> foundParams;
1016 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -04001017 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
1018 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001019 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -04001020 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001021 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -04001022 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -04001023 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -05001024 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -04001025 && !methodParser.strnchr('~', methodParser.fEnd);
1026 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
1027 const char* nextEnd = paren;
1028 do {
1029 string paramName;
1030 methodParser.fChar = nextEnd + 1;
1031 methodParser.skipSpace();
1032 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
1033 continue;
1034 }
1035 methodParams.push_back(paramName);
1036 } while (')' != nextEnd[0]);
1037 }
Cary Clark9174bda2017-09-19 17:39:32 -04001038 for (const auto& child : token.fTokens) {
1039 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1040 break;
1041 }
Cary Clark8032b982017-07-28 11:04:54 -04001042 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001043 if (child.fPrivate) {
1044 break;
1045 }
Cary Clark8032b982017-07-28 11:04:54 -04001046 if ('@' == child.fContentStart[0]) {
1047 TextParser parser(&child);
1048 do {
1049 parser.next();
1050 if (parser.startsWith("param ")) {
1051 parser.skipWord("param");
1052 const char* parmStart = parser.fChar;
1053 parser.skipToSpace();
1054 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
1055 parser.skipWhiteSpace();
1056 do {
1057 size_t nextComma = parmName.find(',');
1058 string piece;
1059 if (string::npos == nextComma) {
1060 piece = parmName;
1061 parmName = "";
1062 } else {
1063 piece = parmName.substr(0, nextComma);
1064 parmName = parmName.substr(nextComma + 1);
1065 }
1066 if (sawParam) {
1067 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001068 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001069 }
Cary Clark9174bda2017-09-19 17:39:32 -04001070 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001071 } else {
1072 if (sawComment) {
1073 this->nl();
1074 }
1075 this->lf(2);
1076 }
1077 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -04001078 this->writeTag("Param", piece);
1079 this->writeSpace(2);
1080 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1081 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001082 sawParam = true;
1083 sawComment = false;
1084 } while (parmName.length());
1085 parser.skipTo(parser.fEnd);
1086 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
1087 parser.skipWord("return");
1088 if ('s' == parser.peek()) {
1089 parser.next();
1090 }
1091 if (sawParam) {
1092 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001093 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001094 }
Cary Clark9174bda2017-09-19 17:39:32 -04001095 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001096 }
1097 this->checkForMissingParams(methodParams, foundParams);
1098 sawParam = false;
1099 sawComment = false;
1100 multiline = false;
1101 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001102 this->writeTag("Return");
1103 this->writeSpace(2);
1104 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1105 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001106 sawReturn = true;
1107 parser.skipTo(parser.fEnd);
1108 } else {
1109 this->reportError("unexpected doxygen directive");
1110 }
1111 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -04001112 } else if (child.length() > 1) {
1113 const char* start = child.fContentStart;
1114 ptrdiff_t length = child.fContentEnd - start;
1115 SkASSERT(length >= 0);
1116 while (length && '/' == start[0]) {
1117 start += 1;
1118 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001119 }
Cary Clark9174bda2017-09-19 17:39:32 -04001120 while (length && '/' == start[length - 1]) {
1121 length -= 1;
1122 if (length && '*' == start[length - 1]) {
1123 length -= 1;
1124 }
1125 }
1126 if (length) {
1127 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
1128 if (sawParam || sawReturn) {
1129 this->indentToColumn(8);
1130 }
1131 this->writeBlock(length, start);
1132 this->writeSpace();
1133 sawComment = true;
1134 if (sawParam || sawReturn) {
1135 multiline = true;
1136 }
Cary Clark8032b982017-07-28 11:04:54 -04001137 }
1138 }
1139 }
1140 }
1141 if (sawParam || sawReturn) {
1142 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001143 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001144 }
Cary Clark9174bda2017-09-19 17:39:32 -04001145 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001146 }
1147 if (!sawReturn) {
1148 if (!sawParam) {
1149 if (sawComment) {
1150 this->nl();
1151 }
1152 this->lf(2);
1153 }
1154 this->checkForMissingParams(methodParams, foundParams);
1155 }
1156 if (methodHasReturn != sawReturn) {
1157 if (!methodHasReturn) {
1158 this->reportError("unexpected doxygen return");
1159 } else {
1160 if (sawComment) {
1161 this->nl();
1162 }
1163 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001164 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -04001165 }
1166 }
1167}
1168
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001169void IncludeParser::dumpCommonTail(const Definition& token) {
1170 this->lf(2);
1171 this->writeTag("Example");
1172 this->lf(1);
1173 this->writeString("// incomplete");
1174 this->lf(1);
1175 this->writeEndTag();
1176 this->lf(2);
1177 this->writeTag("SeeAlso");
1178 this->writeSpace();
1179 this->writeString("incomplete");
1180 this->lf(2);
1181 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1182 this->lf(2);
1183}
1184
Cary Clark224c7002018-06-27 11:00:21 -04001185void IncludeParser::dumpConst(const Definition& token, string className) {
1186 this->writeTag("Const");
1187 this->writeSpace();
1188 this->writeString(token.fName);
1189 this->writeTagTable("Line", "incomplete");
1190 this->lf(2);
1191 this->dumpComment(token);
1192}
1193
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001194void IncludeParser::dumpDefine(const Definition& token) {
1195 this->writeTag("Define", token.fName);
1196 this->lf(2);
1197 this->writeTag("Code");
1198 this->lfAlways(1);
1199 this->writeString("###$");
1200 this->lfAlways(1);
1201 this->indentToColumn(4);
1202 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1203 this->lf(1);
1204 this->indentToColumn(0);
1205 this->writeString("$$$#");
1206
1207 this->writeEndTag();
1208 this->lf(2);
1209 this->dumpComment(token);
1210 for (auto& child : token.fTokens) {
1211 if (MarkType::kComment == child.fMarkType) {
1212 continue;
1213 }
1214 this->writeTag("Param", child.fName);
1215 this->writeSpace();
1216 this->writeString("incomplete");
1217 this->writeSpace();
1218 this->writeString("##");
1219 this->lf(1);
1220 }
1221}
1222
1223void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001224 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -04001225 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001226 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -04001227 this->lfAlways(1);
1228 this->indentToColumn(4);
1229 this->writeString("enum");
1230 this->writeSpace();
1231 if ("_anonymous" != token.fName.substr(0, 10)) {
1232 this->writeString(token.fName);
1233 this->writeSpace();
1234 }
1235 this->writeString("{");
1236 this->lfAlways(1);
1237 for (auto& child : token.fChildren) {
1238 this->indentToColumn(8);
1239 this->writeString(child->fName);
1240 if (child->length()) {
1241 this->writeSpace();
1242 this->writeBlock(child->length(), child->fContentStart);
1243 }
1244 if (',' != fLastChar) {
1245 this->writeString(",");
1246 }
1247 this->lfAlways(1);
1248 }
1249 this->indentToColumn(4);
1250 this->writeString("};");
1251 this->lf(1);
1252 this->writeString("##");
1253 this->lf(2);
1254 this->dumpComment(token);
1255 for (auto& child : token.fChildren) {
Cary Clark61313f32018-10-08 14:57:48 -04001256 // TODO: get comments before or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001257 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001258 this->writeSpace();
1259 this->writeString(child->fName);
1260 TextParser val(child);
1261 if (!val.eof()) {
1262 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
1263 val.next();
1264 val.skipSpace();
1265 const char* valEnd = val.anyOf(",\n");
1266 if (!valEnd) {
1267 valEnd = val.fEnd;
1268 }
1269 this->writeSpace();
1270 this->writeBlock(valEnd - val.fStart, val.fStart);
1271 } else {
1272 this->writeSpace();
1273 this->writeDefinition(*child);
1274 }
1275 }
1276 this->lf(1);
1277 for (auto comment : child->fChildren) {
1278 if (MarkType::kComment == comment->fMarkType) {
1279 TextParser parser(comment);
1280 parser.skipExact("*");
1281 parser.skipExact("*");
1282 while (!parser.eof() && parser.skipWhiteSpace()) {
1283 parser.skipExact("*");
1284 parser.skipWhiteSpace();
1285 const char* start = parser.fChar;
1286 parser.skipToEndBracket('\n');
1287 this->lf(1);
1288 this->writeBlock(parser.fChar - start, start);
1289 }
1290 }
1291 }
1292 this->writeEndTag();
1293 }
1294 this->lf(2);
1295}
1296
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001297bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1298 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1299 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1300 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001301 return true;
1302 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001303 size_t lastBSlash = fFileName.rfind('\\');
1304 size_t lastSlash = fFileName.rfind('/');
1305 size_t lastDotH = fFileName.rfind(".h");
1306 SkASSERT(string::npos != lastDotH);
1307 if (string::npos != lastBSlash && (string::npos == lastSlash
1308 || lastBSlash < lastSlash)) {
1309 lastSlash = lastBSlash;
1310 } else if (string::npos == lastSlash) {
1311 lastSlash = -1;
1312 }
1313 lastSlash += 1;
1314 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1315 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001316 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001317 fOut = fopen(fileName.c_str(), "wb");
1318 if (!fOut) {
1319 SkDebugf("could not open output file %s\n", globalsName.c_str());
1320 return false;
1321 }
1322 string prefixName = globalsName.substr(0, 2);
1323 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1324 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1325 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001326 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001327 this->lf(2);
1328 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001329 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001330 this->writeEndTag();
1331 this->lf(2);
1332 if (!fIDefineMap.empty()) {
1333 this->writeTag("Subtopic", "Define");
1334 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -04001335 this->writeEndTag();
1336 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001337 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001338 if (!fIFunctionMap.empty()) {
1339 this->writeTag("Subtopic", "Function");
1340 this->writeTag("Populate");
1341 this->writeEndTag();
1342 this->lf(2);
1343 }
1344 if (!fIEnumMap.empty()) {
1345 this->writeTag("Subtopic", "Enum");
1346 this->writeTag("Populate");
1347 this->writeEndTag();
1348 this->lf(2);
1349 }
1350 if (!fITemplateMap.empty()) {
1351 this->writeTag("Subtopic", "Template");
1352 this->writeTag("Populate");
1353 this->writeEndTag();
1354 this->lf(2);
1355 }
1356 if (!fITypedefMap.empty()) {
1357 this->writeTag("Subtopic", "Typedef");
1358 this->writeTag("Populate");
1359 this->writeEndTag();
1360 this->lf(2);
1361 }
1362 if (!fIUnionMap.empty()) {
1363 this->writeTag("Subtopic", "Union");
1364 this->writeTag("Populate");
1365 this->writeEndTag();
1366 this->lf(2);
1367 }
1368 std::map<int, Definition*> sortedDefs;
1369 for (const auto& entry : fIDefineMap) {
1370 sortedDefs[entry.second->fLineCount] = entry.second;
1371 }
1372 for (const auto& entry : fIFunctionMap) {
1373 sortedDefs[entry.second->fLineCount] = entry.second;
1374 }
1375 for (const auto& entry : fIEnumMap) {
1376 sortedDefs[entry.second->fLineCount] = entry.second;
1377 }
1378 for (const auto& entry : fITemplateMap) {
1379 sortedDefs[entry.second->fLineCount] = entry.second;
1380 }
1381 for (const auto& entry : fITypedefMap) {
1382 sortedDefs[entry.second->fLineCount] = entry.second;
1383 }
1384 for (const auto& entry : fIUnionMap) {
1385 sortedDefs[entry.second->fLineCount] = entry.second;
1386 }
1387 for (const auto& entry : sortedDefs) {
1388 const Definition* def = entry.second;
1389 this->writeBlockSeparator();
1390 switch (def->fMarkType) {
1391 case MarkType::kDefine:
1392 this->dumpDefine(*def);
1393 break;
1394 case MarkType::kMethod:
1395 this->dumpMethod(*def, globalsName);
1396 break;
1397 case MarkType::kEnum:
1398 case MarkType::kEnumClass:
1399 this->dumpEnum(*def, globalsName);
1400 break;
1401 case MarkType::kTemplate:
1402 SkASSERT(0); // incomplete
1403 break;
1404 case MarkType::kTypedef: {
1405 this->writeTag("Typedef");
1406 this->writeSpace();
1407 TextParser parser(def);
1408 if (!parser.skipExact("typedef")) {
1409 return false;
1410 }
1411 if (!parser.skipSpace()) {
1412 return false;
1413 }
1414 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1415 this->lf(2);
1416 this->dumpComment(*def);
1417 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1418 this->lf(2);
1419 } continue;
1420 case MarkType::kUnion:
1421 SkASSERT(0); // incomplete
1422 break;
1423 default:
1424 SkASSERT(0);
1425 }
1426 this->dumpCommonTail(*def);
1427 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001428 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001429 this->writeEndTag("Topic", topicName);
1430 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001431// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001432 SkDebugf("wrote %s\n", fileName.c_str());
1433 return true;
1434}
1435
1436bool IncludeParser::isClone(const Definition& token) {
1437 string name = token.fName;
1438 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1439}
1440
1441bool IncludeParser::isConstructor(const Definition& token, string className) {
1442 string name = token.fName;
1443 return 0 == name.find(className) || '~' == name[0];
1444}
1445
1446bool IncludeParser::isInternalName(const Definition& token) {
1447 string name = token.fName;
1448 // exception for this SkCanvas function .. for now
1449 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1450 return false;
1451 }
1452 return name.substr(0, 7) == "android"
1453 || 0 == token.fName.find("internal_")
1454 || 0 == token.fName.find("Internal_")
1455 || 0 == token.fName.find("legacy_")
1456 || 0 == token.fName.find("temporary_")
1457 || 0 == token.fName.find("private_");
1458}
1459
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001460bool IncludeParser::isMember(const Definition& token) const {
1461 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1462 return true;
1463 }
1464 if (!islower(token.fStart[0])) {
1465 return false;
1466 }
1467 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1468 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1469 const Definition* structToken = token.fParent;
1470 if (!structToken) {
1471 return false;
1472 }
1473 if (KeyWord::kStruct != structToken->fKeyWord) {
1474 structToken = token.fParent->fParent;
1475 if (!structToken) {
1476 return false;
1477 }
1478 if (KeyWord::kStruct != structToken->fKeyWord) {
1479 return false;
1480 }
1481 }
1482 SkASSERT(structToken->fTokens.size() > 0);
1483 const Definition& child = structToken->fTokens.front();
1484 string structName(child.fContentStart, child.length());
1485 if ("RunBuffer" != structName) {
1486 return false;
1487 }
1488 string tokenName(token.fContentStart, token.length());
1489 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1490 for (auto allow : allowed) {
1491 if (allow == tokenName) {
1492 return true;
1493 }
1494 }
1495 }
1496 return false;
1497}
1498
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001499bool IncludeParser::isOperator(const Definition& token) {
1500 return "operator" == token.fName.substr(0, 8);
1501}
1502
1503void IncludeParser::dumpMethod(const Definition& token, string className) {
1504 this->writeTag("Method");
1505 this->writeSpace();
1506
1507 string name = string(token.fStart ? token.fStart : token.fContentStart,
1508 token.length());
1509 if (this->isOperator(token)) {
1510 string spaceConst(" const");
1511 size_t constPos = name.rfind(spaceConst);
1512 if (name.length() - spaceConst.length() == constPos) {
1513 name = name.substr(0, constPos) + "_const";
1514 }
1515 }
Cary Clark224c7002018-06-27 11:00:21 -04001516 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001517 string inType;
1518 if (this->isConstructor(token, className)) {
1519 inType = "Constructor";
1520 } else if (this->isOperator(token)) {
1521 inType = "Operator";
1522 } else {
1523 inType = "incomplete";
1524 }
1525 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001526 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001527 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001528 this->dumpComment(token);
1529}
1530
1531void IncludeParser::dumpMember(const Definition& token) {
1532 this->writeTag("Member");
1533 this->writeSpace();
1534 this->writeDefinition(token, token.fName, 2);
1535 lf(1);
1536 for (auto child : token.fChildren) {
1537 this->writeDefinition(*child);
1538 }
1539 this->writeEndTag();
1540 lf(2);
1541}
1542
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001543bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001544 string globalFileName;
1545 long int globalTell = 0;
1546 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001547 return false;
1548 }
Cary Clark9174bda2017-09-19 17:39:32 -04001549 for (const auto& member : fIClassMap) {
1550 if (string::npos != member.first.find("::")) {
1551 continue;
1552 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001553 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001554 return false;
1555 }
1556 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001557 if (globalTell) {
1558 fclose(fOut);
1559 }
Cary Clark9174bda2017-09-19 17:39:32 -04001560 return true;
1561}
1562
Ben Wagner63fd7602017-10-09 15:45:33 -04001563 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001564bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001565 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001566 if (globalFileName != fileName) {
1567 fOut = fopen(fileName.c_str(), "wb");
1568 if (!fOut) {
1569 SkDebugf("could not open output file %s\n", fileName.c_str());
1570 return false;
1571 }
1572 } else {
1573 fseek(fOut, *globalTell, SEEK_SET);
1574 this->lf(2);
1575 this->writeBlockSeparator();
1576 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001577 }
1578 string prefixName = skClassName.substr(0, 2);
1579 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1580 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001581 if (globalFileName != fileName) {
1582 this->writeTagNoLF("Topic", topicName);
1583 this->writeEndTag("Alias", topicName + "_Reference");
1584 this->lf(2);
1585 }
Cary Clark8032b982017-07-28 11:04:54 -04001586 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001587 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1588 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001589 this->writeTag(containerType, skClassName);
1590 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001591 auto& tokens = classMap.fTokens;
1592 for (auto& token : tokens) {
1593 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1594 continue;
1595 }
Cary Clark9174bda2017-09-19 17:39:32 -04001596 this->writeDefinition(token);
1597 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001598 }
1599 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001600 bool hasClass = false;
1601 bool hasConst = !fIEnumMap.empty();
1602 bool hasConstructor = false;
1603 bool hasMember = false;
1604 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001605 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001606 for (const auto& oneClass : fIClassMap) {
1607 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1608 continue;
1609 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001610 hasClass = true;
1611 break;
Cary Clark8032b982017-07-28 11:04:54 -04001612 }
Cary Clark224c7002018-06-27 11:00:21 -04001613 for (const auto& oneStruct : fIStructMap) {
1614 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1615 continue;
1616 }
1617 hasStruct = true;
1618 break;
1619 }
Cary Clark8032b982017-07-28 11:04:54 -04001620 for (const auto& token : classMap.fTokens) {
1621 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1622 continue;
1623 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001624 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001625 continue;
1626 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001627 if (this->isConstructor(token, skClassName)) {
1628 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001629 continue;
1630 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001631 if (this->isOperator(token)) {
1632 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001633 continue;
1634 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001635 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001636 continue;
1637 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001638 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001639 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001640 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001641 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001642 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001643 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001644
1645 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001646 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001647 this->writeTag("Populate");
1648 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001649 this->lf(2);
1650 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001651 if (hasConst) {
1652 this->writeTag("Subtopic", "Constant");
1653 this->writeTag("Populate");
1654 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001655 this->lf(2);
1656 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001657 if (hasConstructor) {
1658 this->writeTag("Subtopic", "Constructor");
1659 this->writeTag("Populate");
1660 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001661 this->lf(2);
1662 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001663 if (hasOperator) {
1664 this->writeTag("Subtopic", "Operator");
1665 this->writeTag("Populate");
1666 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001667 this->lf(2);
1668 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001669 if (hasMember) {
1670 this->writeTag("Subtopic", "Member_Function");
1671 this->writeTag("Populate");
1672 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001673 this->lf(2);
1674 }
Cary Clark224c7002018-06-27 11:00:21 -04001675 if (hasStruct) {
1676 this->writeTag("Subtopic", "Struct");
1677 this->writeTag("Populate");
1678 this->writeEndTag();
1679 this->lf(2);
1680 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001681 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001682 this->writeBlockSeparator();
1683 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001684 this->lf(2);
1685 this->writeTag("Example");
1686 this->lfcr();
1687 this->writeString("// incomplete");
1688 this->writeEndTag();
1689 this->lf(2);
1690 this->writeTag("SeeAlso", "incomplete");
1691 this->lf(2);
1692 this->writeEndTag("Enum", oneEnum.first);
1693 this->lf(2);
1694 }
Cary Clark8032b982017-07-28 11:04:54 -04001695 for (auto& oneClass : fIClassMap) {
1696 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1697 continue;
1698 }
1699 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001700 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001701 KeyWord keyword = oneClass.second.fKeyWord;
1702 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1703 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001704 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001705 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001706 this->lf(2);
1707 this->writeTag("Code");
1708 this->writeEndTag("ToDo", "fill this in manually");
1709 this->writeEndTag();
1710 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001711 for (auto& token : oneClass.second.fTokens) {
1712 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1713 continue;
1714 }
Cary Clark9174bda2017-09-19 17:39:32 -04001715 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001716 }
1717 this->lf(2);
1718 this->dumpClassTokens(oneClass.second);
1719 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001720 this->writeEndTag(containerType, innerName);
1721 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001722 }
1723 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001724 this->writeEndTag(containerType, skClassName);
1725 this->lf(2);
1726 this->writeEndTag("Topic", topicName);
1727 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001728 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001729 SkDebugf("wrote %s\n", fileName.c_str());
1730 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001731}
1732
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001733void IncludeParser::dumpTypedef(const Definition& token, string className) {
1734 this->writeTag("Typedef");
1735 this->writeSpace();
1736 this->writeString(token.fName);
1737 this->writeTagTable("Line", "incomplete");
1738 this->lf(2);
1739 this->dumpComment(token);
1740}
1741
Cary Clark61313f32018-10-08 14:57:48 -04001742string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1743 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1744 || KeyWord::kTemplate == iDef.fKeyWord);
1745 TextParser i(&iDef);
1746 fElided = Elided::kYes;
1747 MarkType markType = MarkType::kClass;
1748 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1749 for (auto child : iDef.fChildren) {
1750 if (MarkType::kMethod == child->fMarkType) {
1751 markType = MarkType::kFunction;
1752 break;
1753 }
1754 }
1755 }
1756 return this->writeCodeBlock(i, markType, 0);
1757}
1758
Cary Clark8032b982017-07-28 11:04:54 -04001759bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1760 // add comment preceding class, if any
1761 const Definition* parent = includeDef.fParent;
1762 int index = includeDef.fParentIndex;
1763 auto wordIter = parent->fTokens.begin();
1764 std::advance(wordIter, index);
1765 SkASSERT(&*wordIter == &includeDef);
1766 while (parent->fTokens.begin() != wordIter) {
1767 auto testIter = std::prev(wordIter);
1768 if (Definition::Type::kWord != testIter->fType
1769 && Definition::Type::kKeyWord != testIter->fType
1770 && (Definition::Type::kBracket != testIter->fType
1771 || Bracket::kAngle != testIter->fBracket)
1772 && (Definition::Type::kPunctuation != testIter->fType
1773 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1774 break;
1775 }
1776 wordIter = testIter;
1777 }
1778 auto commentIter = wordIter;
1779 while (parent->fTokens.begin() != commentIter) {
1780 auto testIter = std::prev(commentIter);
1781 bool isComment = Definition::Type::kBracket == testIter->fType
1782 && (Bracket::kSlashSlash == testIter->fBracket
1783 || Bracket::kSlashStar == testIter->fBracket);
1784 if (!isComment) {
1785 break;
1786 }
1787 commentIter = testIter;
1788 }
1789 while (commentIter != wordIter) {
1790 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1791 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1792 return false;
1793 }
1794 commentIter = std::next(commentIter);
1795 }
1796 return true;
1797}
1798
Cary Clark0d225392018-06-07 09:59:07 -04001799Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1800 string typeName) {
1801 typedef Definition* DefinitionPtr;
1802 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1803 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1804 if (mapIter == fMaps.end()) {
1805 return nullptr;
1806 }
1807 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1808 return reportError<DefinitionPtr>("invalid mark type");
1809 }
1810 string name = this->uniqueName(*mapIter->fInclude, typeName);
1811 Definition& markupDef = *(*mapIter->fInclude)[name];
1812 if (markupDef.fStart) {
1813 return reportError<DefinitionPtr>("definition already defined");
1814 }
1815 markupDef.fFileName = fFileName;
1816 markupDef.fStart = includeDef.fStart;
1817 markupDef.fContentStart = includeDef.fStart;
1818 markupDef.fName = name;
1819 markupDef.fContentEnd = includeDef.fContentEnd;
1820 markupDef.fTerminator = includeDef.fTerminator;
1821 markupDef.fParent = fParent;
1822 markupDef.fLineCount = includeDef.fLineCount;
1823 markupDef.fMarkType = markType;
1824 markupDef.fKeyWord = includeDef.fKeyWord;
1825 markupDef.fType = Definition::Type::kMark;
1826 return &markupDef;
1827}
1828
Cary Clark224c7002018-06-27 11:00:21 -04001829Definition* IncludeParser::parentBracket(Definition* parent) const {
1830 while (parent && Definition::Type::kBracket != parent->fType) {
1831 parent = parent->fParent;
1832 }
1833 return parent;
1834}
1835
1836Bracket IncludeParser::grandParentBracket() const {
1837 Definition* parent = parentBracket(fParent);
1838 parent = parentBracket(parent ? parent->fParent : nullptr);
1839 return parent ? parent->fBracket : Bracket::kNone;
1840}
1841
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001842bool IncludeParser::inAlignAs() const {
1843 if (fParent->fTokens.size() < 2) {
1844 return false;
1845 }
1846 auto reverseIter = fParent->fTokens.end();
1847 bool checkForBracket = true;
1848 while (fParent->fTokens.begin() != reverseIter) {
1849 std::advance(reverseIter, -1);
1850 if (checkForBracket) {
1851 if (Definition::Type::kBracket != reverseIter->fType) {
1852 return false;
1853 }
1854 if (Bracket::kParen != reverseIter->fBracket) {
1855 return false;
1856 }
1857 checkForBracket = false;
1858 continue;
1859 }
1860 if (Definition::Type::kKeyWord != reverseIter->fType) {
1861 return false;
1862 }
1863 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1864 }
1865 return false;
1866}
1867
Cary Clark61313f32018-10-08 14:57:48 -04001868const Definition* IncludeParser::include(string match) const {
1869 for (auto& entry : fIncludeMap) {
1870 if (string::npos == entry.first.find(match)) {
1871 continue;
1872 }
1873 return &entry.second;
1874 }
1875 SkASSERT(0);
1876 return nullptr;
1877}
1878
Cary Clark137b8742018-05-30 09:21:49 -04001879// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001880bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1881 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001882 // parse class header
1883 auto iter = includeDef->fTokens.begin();
1884 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1885 // todo : documentation is ignoring this for now
1886 iter = std::next(iter);
1887 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001888 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1889 if (hasAlignAs) {
1890 iter = std::next(iter);
1891 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1892 return includeDef->reportError<bool>("expected alignas argument");
1893 }
1894 iter = std::next(iter);
1895 }
Cary Clark8032b982017-07-28 11:04:54 -04001896 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1897 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001898 iter = std::next(iter);
1899 if (iter == includeDef->fTokens.end()) {
1900 return true; // forward declaration only
1901 }
Cary Clark8032b982017-07-28 11:04:54 -04001902 do {
1903 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001904 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001905 }
1906 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1907 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001908 }
Cary Clark8032b982017-07-28 11:04:54 -04001909 } while (static_cast<void>(iter = std::next(iter)), true);
1910 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001911 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001912 }
1913 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1914 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001915 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001916 }
1917 markupDef->fStart = iter->fStart;
1918 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001919 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001920 }
1921// if (1 != includeDef->fChildren.size()) {
1922// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1923// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001924 auto includeDefIter = includeDef->fChildren.begin();
1925 if (hasAlignAs) {
1926 SkASSERT(includeDef->fChildren.end() != includeDefIter);
1927 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
1928 std::advance(includeDefIter, 1);
1929 }
1930 if (includeDef->fChildren.end() != includeDefIter
1931 && Bracket::kAngle == (*includeDefIter)->fBracket) {
1932 std::advance(includeDefIter, 1);
1933 }
1934 includeDef = *includeDefIter;
1935 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001936 iter = includeDef->fTokens.begin();
1937 // skip until public
1938 int publicIndex = 0;
1939 if (IsStruct::kNo == isStruct) {
1940 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1941 size_t publicLen = strlen(publicName);
1942 while (iter != includeDef->fTokens.end()
1943 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1944 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001945 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001946 iter = std::next(iter);
1947 ++publicIndex;
1948 }
1949 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001950 int keyIndex = publicIndex;
1951 KeyWord currentKey = KeyWord::kPublic;
1952 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1953 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001954 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1955 size_t protectedLen = strlen(protectedName);
1956 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1957 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001958 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001959 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04001960 std::advance(childIter, 1);
1961 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001962 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001963 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001964 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001965 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001966 const char* testStart = iter->fStart;
1967 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1968 iter = std::next(iter);
1969 ++keyIndex;
1970 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1971 currentKey = KeyWord::kPublic;
1972 break;
1973 }
1974 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1975 currentKey = KeyWord::kProtected;
1976 break;
1977 }
1978 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1979 currentKey = KeyWord::kPrivate;
1980 break;
1981 }
1982 }
1983 fLastObject = nullptr;
1984 if (KeyWord::kPublic == currentKey) {
1985 if (!this->parseObject(child, markupDef)) {
1986 return false;
1987 }
Cary Clark8032b982017-07-28 11:04:54 -04001988 }
Cary Clark73fa9722017-08-29 17:36:51 -04001989 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001990 childIter = std::next(childIter);
1991 }
Cary Clark137b8742018-05-30 09:21:49 -04001992 while (iter != includeDef->fTokens.end()) {
1993 iter->fPrivate = KeyWord::kPublic != currentKey;
1994 iter = std::next(iter);
1995 }
Cary Clark8032b982017-07-28 11:04:54 -04001996 SkASSERT(fParent->fParent);
1997 fParent = fParent->fParent;
1998 return true;
1999}
2000
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002001bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04002002 int lineCount, Definition* markupDef) {
2003 TextParser parser(filename, start, end, lineCount);
2004 // parse doxygen if present
2005 if (parser.startsWith("**")) {
2006 parser.next();
2007 parser.next();
2008 parser.skipWhiteSpace();
2009 if ('\\' == parser.peek()) {
2010 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002011 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2012 if (parser.skipExact("file")) {
2013 if (Definition::Type::kFileType != fParent->fType) {
2014 return reportError<bool>("expected parent is file");
2015 }
2016 string filename = markupDef->fileName();
2017 if (!parser.skipWord(filename.c_str())) {
2018 return reportError<bool>("missing object type");
2019 }
2020 } else if (parser.skipExact("fn")) {
2021 SkASSERT(0); // incomplete
2022 } else {
2023 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2024 return reportError<bool>("missing object type");
2025 }
2026 if (!parser.skipWord(markupDef->fName.c_str()) &&
2027 KeyWord::kEnum != markupDef->fKeyWord) {
2028 return reportError<bool>("missing object name");
2029 }
Cary Clark8032b982017-07-28 11:04:54 -04002030 }
Cary Clark8032b982017-07-28 11:04:54 -04002031 }
2032 }
2033 // remove leading '*' if present
2034 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2035 while (!parser.eof() && parser.skipWhiteSpace()) {
2036 while ('*' == parser.peek()) {
2037 parser.next();
2038 if (parser.eof()) {
2039 break;
2040 }
2041 parser.skipWhiteSpace();
2042 }
2043 if (parser.eof()) {
2044 break;
2045 }
2046 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002047 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002048 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002049 parser.skipToEndBracket('\n');
2050 }
2051 return true;
2052}
2053
Cary Clarkd98f78c2018-04-26 08:32:37 -04002054bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002055 if (!markupDef) {
2056 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2057 child->fLineCount, fParent, '\0');
2058 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002059 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002060 globalMarkupChild->fName = globalUniqueName;
2061 if (!this->findComments(*child, globalMarkupChild)) {
2062 return false;
2063 }
2064 fIConstMap[globalUniqueName] = globalMarkupChild;
2065 return true;
2066 }
2067 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2068 child->fLineCount, markupDef, '\0');
2069 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002070 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002071 markupChild->fTerminator = markupChild->fContentEnd;
2072 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002073 classDef.fConsts[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002074 return true;
2075}
2076
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002077bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2078 TextParser parser(child);
2079 if (!parser.skipExact("#define")) {
2080 return false;
2081 }
2082 if (!parser.skipSpace()) {
2083 return false;
2084 }
2085 const char* nameStart = parser.fChar;
2086 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2087 if (parser.eof()) {
2088 return true; // do nothing if #define doesn't define anything
2089 }
2090 string nameStr(nameStart, parser.fChar - nameStart);
2091 struct Param {
2092 const char* fStart;
2093 const char* fEnd;
2094 };
2095 vector<Param> params;
2096 if ('(' == parser.peek()) {
2097 parser.next();
2098 if (!parser.skipSpace()) {
2099 return false;
2100 }
2101 do {
2102 const char* paramStart = parser.fChar;
2103 if (!parser.skipExact("...")) {
2104 parser.skipToNonAlphaNum();
2105 }
2106 if (parser.eof()) {
2107 return false;
2108 }
2109 params.push_back({paramStart, parser.fChar});
2110 if (!parser.skipSpace()) {
2111 return false;
2112 }
2113 if (')' == parser.peek()) {
2114 parser.next();
2115 break;
2116 }
2117 if (',' != parser.next()) {
2118 return false;
2119 }
2120 if (!parser.skipSpace()) {
2121 return false;
2122 }
2123 } while (true);
2124 }
2125 if (!parser.skipSpace()) {
2126 return false;
2127 }
2128 if (!markupDef) {
2129 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2130 child->fLineCount, fParent, '\0');
2131 Definition* globalMarkupChild = &fGlobals.back();
2132 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2133 globalMarkupChild->fName = globalUniqueName;
2134 globalMarkupChild->fTerminator = child->fContentEnd;
2135 if (!this->findComments(*child, globalMarkupChild)) {
2136 return false;
2137 }
2138 fIDefineMap[globalUniqueName] = globalMarkupChild;
2139 for (Param param : params) {
2140 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2141 child->fLineCount, globalMarkupChild, '\0');
2142 Definition* paramChild = &globalMarkupChild->fTokens.back();
2143 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2144 paramChild->fTerminator = param.fEnd;
2145 }
2146 return true;
2147 }
2148 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2149 child->fLineCount, markupDef, '\0');
2150 Definition* markupChild = &markupDef->fTokens.back();
2151 markupChild->fName = nameStr;
2152 markupChild->fTerminator = markupChild->fContentEnd;
2153 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2154 if (!this->findComments(*child, markupChild)) {
2155 return false;
2156 }
2157 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002158 return true;
2159}
2160
2161bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002162 TextParser parser(child);
2163 parser.skipToEndBracket('{');
2164 if (parser.eof()) {
2165 return true; // if enum is a forward declaration, do nothing
2166 }
2167 parser.next();
2168 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002169 if (child->fTokens.size() > 0) {
2170 auto token = child->fTokens.begin();
2171 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2172 token = token->fTokens.begin();
2173 }
2174 if (Definition::Type::kWord == token->fType) {
2175 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
2176 }
2177 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002178 Definition* markupChild;
2179 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002180 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2181 child->fLineCount, fParent, '\0');
2182 markupChild = &fGlobals.back();
2183 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2184 markupChild->fName = globalUniqueName;
2185 markupChild->fTerminator = child->fContentEnd;
2186 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002187 } else {
2188 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002189 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002190 markupChild = &markupDef->fTokens.back();
2191 }
Cary Clark8032b982017-07-28 11:04:54 -04002192 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2193 markupChild->fKeyWord = KeyWord::kEnum;
2194 TextParser enumName(child);
2195 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05002196 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002197 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002198 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002199 markupChild->fMarkType = MarkType::kEnumClass;
2200 }
Cary Clark8032b982017-07-28 11:04:54 -04002201 const char* nameStart = enumName.fChar;
2202 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05002203 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04002204 markupChild->fName = markupDef->fName + "::" +
2205 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05002206 }
Cary Clark8032b982017-07-28 11:04:54 -04002207 if (!this->findComments(*child, markupChild)) {
2208 return false;
2209 }
Cary Clark8032b982017-07-28 11:04:54 -04002210 const char* dataEnd;
2211 do {
Cary Clark8032b982017-07-28 11:04:54 -04002212 parser.skipWhiteSpace();
2213 if ('}' == parser.peek()) {
2214 break;
2215 }
2216 Definition* comment = nullptr;
2217 // note that comment, if any, can be before or after (on the same line, though) as member
2218 if ('#' == parser.peek()) {
2219 // fixme: handle preprecessor, but just skip it for now
2220 parser.skipToLineStart();
2221 }
2222 while (parser.startsWith("/*") || parser.startsWith("//")) {
2223 parser.next();
2224 const char* start = parser.fChar;
2225 const char* end;
2226 if ('*' == parser.peek()) {
2227 end = parser.strnstr("*/", parser.fEnd);
2228 parser.fChar = end;
2229 parser.next();
2230 parser.next();
2231 } else {
2232 end = parser.trimmedLineEnd();
2233 parser.skipToLineStart();
2234 }
2235 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002236 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002237 comment = &markupChild->fTokens.back();
2238 comment->fTerminator = end;
2239 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
2240 return false;
2241 }
2242 parser.skipWhiteSpace();
2243 }
2244 parser.skipWhiteSpace();
2245 const char* memberStart = parser.fChar;
2246 if ('}' == memberStart[0]) {
2247 break;
2248 }
Cary Clark9174bda2017-09-19 17:39:32 -04002249 // if there's comment on same the line as member def, output first as if it was before
2250
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002251 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002252 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04002253 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04002254 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04002255 }
Cary Clark8032b982017-07-28 11:04:54 -04002256 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04002257 if ('=' == parser.peek()) {
2258 parser.skipToEndBracket(',');
2259 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002260 if (!parser.eof() && '#' == parser.peek()) {
2261 // fixme: handle preprecessor, but just skip it for now
2262 continue;
2263 }
Cary Clark9174bda2017-09-19 17:39:32 -04002264 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04002265 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04002266 }
2267 dataEnd = parser.fChar;
2268 const char* start = parser.anyOf("/\n");
2269 SkASSERT(start);
2270 parser.skipTo(start);
2271 if ('/' == parser.next()) {
2272 char slashStar = parser.next();
2273 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04002274 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04002275 char doxCheck = parser.next();
2276 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
2277 save.restore();
2278 }
2279 }
2280 parser.skipWhiteSpace();
2281 const char* commentStart = parser.fChar;
2282 if ('/' == slashStar) {
2283 parser.skipToEndBracket('\n');
2284 } else {
2285 parser.skipToEndBracket("*/");
2286 }
2287 SkASSERT(!parser.eof());
2288 const char* commentEnd = parser.fChar;
2289 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002290 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04002291 comment = &markupChild->fTokens.back();
2292 comment->fTerminator = commentEnd;
2293 }
Cary Clark8032b982017-07-28 11:04:54 -04002294 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002295 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002296 Definition* member = &markupChild->fTokens.back();
2297 member->fName = memberName;
2298 if (comment) {
2299 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04002300 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002301 }
2302 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04002303 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002304 for (auto outsideMember : child->fChildren) {
2305 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002306 continue;
2307 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002308 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2309 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002310 continue;
2311 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002312 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2313 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002314 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002315 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002316 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002317 // FIXME: ? add comment as well ?
2318 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002319 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002320 if (markupDef) {
2321 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2322 SkASSERT(classDef.fStart);
2323 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002324 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002325 markupChild->fName = uniqueName;
2326 classDef.fEnums[uniqueName] = markupChild;
Cary Clark61313f32018-10-08 14:57:48 -04002327 fIEnumMap[fullName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002328 }
Cary Clark8032b982017-07-28 11:04:54 -04002329 return true;
2330}
2331
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002332bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002333 fParent = &fIncludeMap[name];
2334 fParent->fName = name;
2335 fParent->fFileName = fFileName;
2336 fParent->fType = Definition::Type::kFileType;
2337 fParent->fContentStart = fChar;
2338 fParent->fContentEnd = fEnd;
2339 // parse include file into tree
2340 while (fChar < fEnd) {
2341 if (!this->parseChar()) {
2342 return false;
2343 }
2344 }
2345 // parse tree and add named objects to maps
2346 fParent = &fIncludeMap[name];
2347 if (!this->parseObjects(fParent, nullptr)) {
2348 return false;
2349 }
2350 return true;
2351}
2352
2353bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2354 const char* typeStart = child->fChildren[0]->fContentStart;
2355 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002356 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002357 Definition* markupChild = &markupDef->fTokens.back();
2358 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002359 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002360 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2361 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2362 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2363 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002364 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04002365 classDef.fMembers[uniqueName] = markupChild;
2366 if (child->fParentIndex >= 2) {
2367 auto comment = child->fParent->fTokens.begin();
2368 std::advance(comment, child->fParentIndex - 2);
2369 if (Definition::Type::kBracket == comment->fType
2370 && (Bracket::kSlashStar == comment->fBracket
2371 || Bracket::kSlashSlash == comment->fBracket)) {
2372 TextParser parser(&*comment);
2373 do {
2374 parser.skipToAlpha();
2375 if (parser.eof()) {
2376 break;
2377 }
2378 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002379 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002380 if (Bracket::kSlashStar == comment->fBracket) {
2381 const char* commentEnd = parser.strnstr("*/", end);
2382 if (commentEnd) {
2383 end = commentEnd;
2384 }
2385 }
2386 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002387 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002388 Definition* commentChild = &markupDef->fTokens.back();
2389 markupChild->fChildren.emplace_back(commentChild);
2390 parser.skipTo(end);
2391 } while (!parser.eof());
2392 }
2393 }
2394 return true;
2395}
2396
2397bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2398 auto tokenIter = child->fParent->fTokens.begin();
2399 std::advance(tokenIter, child->fParentIndex);
2400 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002401 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002402 bool addConst = false;
2403 auto operatorCheck = tokenIter;
2404 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2405 operatorCheck = std::prev(tokenIter);
2406 }
2407 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002408 auto closeParen = std::next(tokenIter);
2409 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2410 '(' == closeParen->fContentStart[0]);
2411 nameEnd = closeParen->fContentEnd + 1;
2412 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002413 if (Definition::Type::kKeyWord == closeParen->fType &&
2414 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002415 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002416 }
Cary Clarka560c472017-11-27 10:44:06 -05002417 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002418 }
2419 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002420 if (addConst) {
2421 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002422 }
Cary Clark8032b982017-07-28 11:04:54 -04002423 while (tokenIter != child->fParent->fTokens.begin()) {
2424 auto testIter = std::prev(tokenIter);
2425 switch (testIter->fType) {
2426 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002427 if (testIter == child->fParent->fTokens.begin() &&
2428 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2429 KeyWord::kIfndef == child->fParent->fKeyWord ||
2430 KeyWord::kIf == child->fParent->fKeyWord)) {
2431 std::next(tokenIter);
2432 break;
2433 }
Cary Clark8032b982017-07-28 11:04:54 -04002434 goto keepGoing;
2435 case Definition::Type::kKeyWord: {
2436 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2437 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2438 goto keepGoing;
2439 }
2440 } break;
2441 case Definition::Type::kBracket:
2442 if (Bracket::kAngle == testIter->fBracket) {
2443 goto keepGoing;
2444 }
2445 break;
2446 case Definition::Type::kPunctuation:
2447 if (Punctuation::kSemicolon == testIter->fPunctuation
2448 || Punctuation::kLeftBrace == testIter->fPunctuation
2449 || Punctuation::kColon == testIter->fPunctuation) {
2450 break;
2451 }
2452 keepGoing:
2453 tokenIter = testIter;
2454 continue;
2455 default:
2456 break;
2457 }
2458 break;
2459 }
Cary Clark224c7002018-06-27 11:00:21 -04002460 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002461 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002462 tokenIter->fPrivate = string::npos != nameStr.find("::")
2463 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002464 auto testIter = child->fParent->fTokens.begin();
2465 SkASSERT(child->fParentIndex > 0);
2466 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002467 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2468 0 == tokenIter->fParentIndex) {
2469 tokenIter = std::next(tokenIter);
2470 }
Cary Clark8032b982017-07-28 11:04:54 -04002471 const char* start = tokenIter->fContentStart;
2472 const char* end = tokenIter->fContentEnd;
2473 const char kDebugCodeStr[] = "SkDEBUGCODE";
2474 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2475 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2476 std::advance(testIter, 1);
2477 start = testIter->fContentStart + 1;
2478 end = testIter->fContentEnd - 1;
2479 } else {
2480 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002481 do {
2482 std::advance(testIter, 1);
2483 if (testIter == child->fParent->fTokens.end()) {
2484 break;
2485 }
Cary Clark8032b982017-07-28 11:04:54 -04002486 switch (testIter->fType) {
2487 case Definition::Type::kPunctuation:
2488 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2489 || Punctuation::kLeftBrace == testIter->fPunctuation
2490 || Punctuation::kColon == testIter->fPunctuation);
2491 end = testIter->fStart;
2492 break;
2493 case Definition::Type::kKeyWord: {
2494 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2495 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2496 continue;
2497 }
2498 } break;
2499 default:
2500 continue;
2501 }
2502 break;
Cary Clark61313f32018-10-08 14:57:48 -04002503 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002504 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002505 while (end > start && ' ' >= end[-1]) {
2506 --end;
2507 }
Cary Clark9174bda2017-09-19 17:39:32 -04002508 if (!markupDef) {
2509 auto parentIter = child->fParent->fTokens.begin();
2510 SkASSERT(child->fParentIndex > 0);
2511 std::advance(parentIter, child->fParentIndex - 1);
2512 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002513 TextParser nameParser(methodName);
2514 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002515 return true; // expect this is inline class definition outside of class
2516 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002517 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2518 fParent, '\0');
2519 Definition* globalMarkupChild = &fGlobals.back();
2520 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2521 globalMarkupChild->fName = globalUniqueName;
2522 if (!this->findComments(*child, globalMarkupChild)) {
2523 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002524 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002525 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002526 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002527 }
Cary Clark8032b982017-07-28 11:04:54 -04002528 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002529 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002530 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002531 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
2532 {
2533 auto mapIter = fIClassMap.find(markupDef->fName);
2534 SkASSERT(fIClassMap.end() != mapIter);
2535 IClassDefinition& classDef = mapIter->second;
2536 SkASSERT(classDef.fStart);
2537 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2538 markupChild->fName = uniqueName;
2539 if (!this->findComments(*child, markupChild)) {
2540 return false;
2541 }
2542 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002543 }
Cary Clark8032b982017-07-28 11:04:54 -04002544 return true;
2545}
2546
Cary Clark8032b982017-07-28 11:04:54 -04002547bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002548 fPriorObject = nullptr;
2549 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002550 if (!this->parseObject(child, markupDef)) {
2551 return false;
2552 }
Cary Clark0d225392018-06-07 09:59:07 -04002553 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002554 }
2555 return true;
2556}
2557
2558bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2559 // set up for error reporting
2560 fLine = fChar = child->fStart;
2561 fEnd = child->fContentEnd;
2562 // todo: put original line number in child as well
2563 switch (child->fType) {
2564 case Definition::Type::kKeyWord:
2565 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002566 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002567 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002568 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002569 }
2570 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002571 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002572 case KeyWord::kConst:
2573 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002574 if (!this->parseConst(child, markupDef)) {
2575 return child->reportError<bool>("failed to parse const or constexpr");
2576 }
2577 break;
Cary Clark8032b982017-07-28 11:04:54 -04002578 case KeyWord::kEnum:
2579 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002580 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002581 }
2582 break;
2583 case KeyWord::kStruct:
2584 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002585 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002586 }
2587 break;
2588 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002589 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002590 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002591 }
2592 break;
2593 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002594 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002595 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002596 }
2597 break;
2598 case KeyWord::kUnion:
2599 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002600 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002601 }
2602 break;
Cary Clark61313f32018-10-08 14:57:48 -04002603 case KeyWord::kUsing:
2604 if (!this->parseUsing()) {
2605 return child->reportError<bool>("failed to parse using");
2606 }
2607 break;
Cary Clark8032b982017-07-28 11:04:54 -04002608 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002609 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002610 }
2611 break;
2612 case Definition::Type::kBracket:
2613 switch (child->fBracket) {
2614 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04002615 if (fLastObject) {
2616 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
2617 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04002618 if (!checkDeprecated.eof()) {
2619 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002620 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2621 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002622 break;
2623 }
2624 }
2625 }
2626 {
2627 auto tokenIter = child->fParent->fTokens.begin();
2628 std::advance(tokenIter, child->fParentIndex);
2629 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002630 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002631 if (previousToken.startsWith(gAttrDeprecated)) {
2632 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002633 break;
2634 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002635 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002636 break;
2637 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002638 if (Bracket::kPound == child->fParent->fBracket &&
2639 KeyWord::kIf == child->fParent->fKeyWord) {
2640 // TODO: this will skip methods named defined() -- for the
2641 // moment there aren't any
2642 if (previousToken.startsWith("defined")) {
2643 break;
2644 }
2645 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002646 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2647 break;
2648 }
Cary Clark73fa9722017-08-29 17:36:51 -04002649 }
Cary Clark0d225392018-06-07 09:59:07 -04002650 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2651 break;
2652 }
Cary Clark8032b982017-07-28 11:04:54 -04002653 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002654 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002655 }
Cary Clark89b14562018-03-19 09:04:10 -04002656 if (fAttrDeprecated) {
2657 Definition* lastMethod = &markupDef->fTokens.back();
2658 lastMethod->fDeprecated = true;
2659 fAttrDeprecated = nullptr;
2660 }
Cary Clark73fa9722017-08-29 17:36:51 -04002661 break;
Cary Clark8032b982017-07-28 11:04:54 -04002662 case Bracket::kSlashSlash:
2663 case Bracket::kSlashStar:
2664 // comments are picked up by parsing objects first
2665 break;
2666 case Bracket::kPound:
2667 // special-case the #xxx xxx_DEFINED entries
2668 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002669 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002670 case KeyWord::kIfndef:
2671 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002672 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002673 if (!this->parseObjects(child, markupDef)) {
2674 return false;
2675 }
2676 break;
2677 }
2678 goto preproError;
2679 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002680 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002681 break;
2682 }
2683 goto preproError;
2684 case KeyWord::kEndif:
2685 if (child->boilerplateEndIf()) {
2686 break;
2687 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002688 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002689 case KeyWord::kInclude:
2690 // ignored for now
2691 break;
2692 case KeyWord::kElse:
2693 case KeyWord::kElif:
2694 // todo: handle these
2695 break;
2696 default:
2697 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002698 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002699 }
2700 break;
2701 case Bracket::kAngle:
2702 // pick up templated function pieces when method is found
2703 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002704 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002705 if (!this->parseObjects(child, markupDef)) {
2706 return false;
2707 }
Cary Clark73fa9722017-08-29 17:36:51 -04002708 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002709 case Bracket::kSquare: {
2710 // check to see if parent is operator, the only case we handle so far
2711 auto prev = child->fParent->fTokens.begin();
2712 std::advance(prev, child->fParentIndex - 1);
2713 if (KeyWord::kOperator != prev->fKeyWord) {
2714 return child->reportError<bool>("expected operator overload");
2715 }
2716 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002717 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002718 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002719 }
2720 break;
2721 case Definition::Type::kWord:
2722 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002723 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002724 }
2725 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002726 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002727 }
2728 break;
2729 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002730 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002731 break;
2732 }
2733 return true;
2734}
2735
Cary Clarkbbfda252018-03-09 15:32:01 -05002736bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2737 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002738}
2739
Cary Clark2f466242017-12-11 16:03:17 -05002740bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2741 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002742 typedefParser.skipExact("typedef");
2743 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002744 string nameStr = typedefParser.typedefName();
2745 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002746 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2747 child->fLineCount, fParent, '\0');
2748 Definition* globalMarkupChild = &fGlobals.back();
2749 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2750 globalMarkupChild->fName = globalUniqueName;
2751 if (!this->findComments(*child, globalMarkupChild)) {
2752 return false;
2753 }
2754 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002755 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002756 return true;
2757 }
2758 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002759 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002760 Definition* markupChild = &markupDef->fTokens.back();
2761 markupChild->fName = nameStr;
2762 markupChild->fTerminator = markupChild->fContentEnd;
2763 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2764 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002765 child->fName = markupDef->fName + "::" + nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002766 return true;
2767}
2768
2769bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002770 // incomplete
2771 return true;
2772}
Cary Clark8032b982017-07-28 11:04:54 -04002773
Cary Clark61313f32018-10-08 14:57:48 -04002774bool IncludeParser::parseUsing() {
2775 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002776 return true;
2777}
2778
2779bool IncludeParser::parseChar() {
2780 char test = *fChar;
2781 if ('\\' == fPrev) {
2782 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002783// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002784 fLine = fChar + 1;
2785 }
2786 goto done;
2787 }
2788 switch (test) {
2789 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002790// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002791 fLine = fChar + 1;
2792 if (fInChar) {
2793 return reportError<bool>("malformed char");
2794 }
2795 if (fInString) {
2796 return reportError<bool>("malformed string");
2797 }
2798 if (!this->checkForWord()) {
2799 return false;
2800 }
2801 if (Bracket::kPound == this->topBracket()) {
2802 KeyWord keyWord = fParent->fKeyWord;
2803 if (KeyWord::kNone == keyWord) {
2804 return this->reportError<bool>("unhandled preprocessor directive");
2805 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002806 if (fInDefine) {
2807 SkASSERT(KeyWord::kDefine == keyWord);
2808 fInDefine = false;
2809 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002810 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002811 this->popBracket();
2812 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002813 if (fInBrace) {
2814 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2815 fInBrace = nullptr;
2816 }
Cary Clark8032b982017-07-28 11:04:54 -04002817 } else if (Bracket::kSlashSlash == this->topBracket()) {
2818 this->popBracket();
2819 }
2820 break;
2821 case '*':
2822 if (!fInCharCommentString && '/' == fPrev) {
2823 this->pushBracket(Bracket::kSlashStar);
2824 }
2825 if (!this->checkForWord()) {
2826 return false;
2827 }
2828 if (!fInCharCommentString) {
2829 this->addPunctuation(Punctuation::kAsterisk);
2830 }
2831 break;
2832 case '/':
2833 if ('*' == fPrev) {
2834 if (!fInCharCommentString) {
2835 return reportError<bool>("malformed closing comment");
2836 }
2837 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002838 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002839 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002840 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002841 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002842 }
2843 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002844 }
Cary Clark8032b982017-07-28 11:04:54 -04002845 if (!fInCharCommentString && '/' == fPrev) {
2846 this->pushBracket(Bracket::kSlashSlash);
2847 break;
2848 }
2849 if (!this->checkForWord()) {
2850 return false;
2851 }
2852 break;
2853 case '\'':
2854 if (Bracket::kChar == this->topBracket()) {
2855 this->popBracket();
2856 } else if (!fInComment && !fInString) {
2857 if (fIncludeWord) {
2858 return this->reportError<bool>("word then single-quote");
2859 }
2860 this->pushBracket(Bracket::kChar);
2861 }
2862 break;
2863 case '\"':
2864 if (Bracket::kString == this->topBracket()) {
2865 this->popBracket();
2866 } else if (!fInComment && !fInChar) {
2867 if (fIncludeWord) {
2868 return this->reportError<bool>("word then double-quote");
2869 }
2870 this->pushBracket(Bracket::kString);
2871 }
2872 break;
Cary Clark8032b982017-07-28 11:04:54 -04002873 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002874 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002875 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2876 this->pushBracket(Bracket::kDebugCode);
2877 break;
2878 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002879 case ':':
2880 case '[':
2881 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002882 if (fInCharCommentString) {
2883 break;
2884 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002885 if (fInDefine && fInBrace) {
2886 break;
2887 }
Cary Clark8032b982017-07-28 11:04:54 -04002888 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2889 break;
2890 }
Cary Clark0d225392018-06-07 09:59:07 -04002891 if (fConstExpr) {
2892 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2893 fConstExpr = nullptr;
2894 }
Cary Clark8032b982017-07-28 11:04:54 -04002895 if (!fInBrace) {
2896 if (!this->checkForWord()) {
2897 return false;
2898 }
2899 if (':' == test && !fInFunction) {
2900 break;
2901 }
2902 if ('{' == test) {
2903 this->addPunctuation(Punctuation::kLeftBrace);
2904 } else if (':' == test) {
2905 this->addPunctuation(Punctuation::kColon);
2906 }
2907 }
2908 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2909 && Bracket::kColon == fInBrace->fBracket) {
2910 Definition* braceParent = fParent->fParent;
2911 braceParent->fChildren.pop_back();
2912 braceParent->fTokens.pop_back();
2913 fParent = braceParent;
2914 fInBrace = nullptr;
2915 }
2916 this->pushBracket(
2917 '(' == test ? Bracket::kParen :
2918 '[' == test ? Bracket::kSquare :
2919 '{' == test ? Bracket::kBrace :
2920 Bracket::kColon);
2921 if (!fInBrace
2922 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2923 && fInFunction) {
2924 fInBrace = fParent;
2925 }
2926 } break;
2927 case '<':
2928 if (fInCharCommentString || fInBrace) {
2929 break;
2930 }
2931 if (!this->checkForWord()) {
2932 return false;
2933 }
2934 if (fInEnum) {
2935 break;
2936 }
2937 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002938 // this angle bracket may be an operator or may be a bracket
2939 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002940 break;
2941 case ')':
2942 case ']':
2943 case '}': {
2944 if (fInCharCommentString) {
2945 break;
2946 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002947 if (fInDefine && fInBrace) {
2948 break;
2949 }
Cary Clark8032b982017-07-28 11:04:54 -04002950 if (!fInBrace) {
2951 if (!this->checkForWord()) {
2952 return false;
2953 }
2954 }
2955 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002956 Bracket match = ')' == test ? Bracket::kParen :
2957 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2958 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002959 this->popBracket();
2960 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002961 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04002962 } else {
2963 fInFunction = '}' != test;
2964 }
Cary Clark73fa9722017-08-29 17:36:51 -04002965 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2966 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002967 } else if (Bracket::kAngle == this->topBracket()
2968 && match == this->grandParentBracket()) {
2969 this->popBracket();
2970 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002971 } else {
2972 return reportError<bool>("malformed close bracket");
2973 }
2974 if (popBraceParent) {
2975 Definition* braceParent = fInBrace->fParent;
2976 braceParent->fChildren.pop_back();
2977 braceParent->fTokens.pop_back();
2978 fInBrace = nullptr;
2979 }
2980 } break;
2981 case '>':
2982 if (fInCharCommentString || fInBrace) {
2983 break;
2984 }
2985 if (!this->checkForWord()) {
2986 return false;
2987 }
2988 if (fInEnum) {
2989 break;
2990 }
Cary Clarka560c472017-11-27 10:44:06 -05002991 if (Bracket::kPound == this->topBracket()) {
2992 break;
2993 }
Cary Clark8032b982017-07-28 11:04:54 -04002994 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04002995 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04002996 this->popBracket();
2997 } else {
2998 return reportError<bool>("malformed close angle bracket");
2999 }
3000 break;
3001 case '#': {
3002 if (fInCharCommentString || fInBrace) {
3003 break;
3004 }
3005 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3006 this->pushBracket(Bracket::kPound);
3007 break;
3008 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003009 case ' ':
3010 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3011 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3012 fInBrace = fParent;
3013 // delimiting brackets are space ... unescaped-linefeed
3014 }
Cary Clark8032b982017-07-28 11:04:54 -04003015 case '&':
3016 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003017 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003018 case '-':
3019 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003020 if (fInCharCommentString || fInBrace) {
3021 break;
3022 }
3023 if (!this->checkForWord()) {
3024 return false;
3025 }
3026 break;
Cary Clark0d225392018-06-07 09:59:07 -04003027 case '=':
3028 if (fInCharCommentString || fInBrace) {
3029 break;
3030 }
3031 if (!this->checkForWord()) {
3032 return false;
3033 }
Cary Clarkd7895502018-07-18 15:10:08 -04003034 if (!fParent->fTokens.size()) {
3035 break;
3036 }
Cary Clark0d225392018-06-07 09:59:07 -04003037 {
3038 const Definition& lastToken = fParent->fTokens.back();
3039 if (lastToken.fType != Definition::Type::kWord) {
3040 break;
3041 }
3042 string name(lastToken.fContentStart, lastToken.length());
3043 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3044 break;
3045 }
3046 // find token on start of line
3047 auto lineIter = fParent->fTokens.end();
3048 do {
Cary Clark80247e52018-07-11 16:18:41 -04003049 if (fParent->fTokens.begin() == lineIter) {
3050 break;
3051 }
Cary Clark0d225392018-06-07 09:59:07 -04003052 --lineIter;
3053 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003054 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003055 ++lineIter;
3056 }
3057 Definition* lineStart = &*lineIter;
3058 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3059 bool sawConst = false;
3060 bool sawStatic = false;
3061 bool sawTemplate = false;
3062 bool sawType = false;
3063 while (&lastToken != &*lineIter) {
3064 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3065 if (sawConst || sawStatic || sawTemplate) {
3066 sawConst = false;
3067 break;
3068 }
3069 if (&lastToken == &*++lineIter) {
3070 break;
3071 }
3072 if (KeyWord::kTypename != lineIter->fKeyWord) {
3073 break;
3074 }
3075 if (&lastToken == &*++lineIter) {
3076 break;
3077 }
3078 if (Definition::Type::kWord != lineIter->fType) {
3079 break;
3080 }
3081 sawTemplate = true;
3082 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3083 if (sawConst || sawStatic) {
3084 sawConst = false;
3085 break;
3086 }
3087 sawStatic = true;
3088 } else if (KeyWord::kConst == lineIter->fKeyWord
3089 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3090 if (sawConst) {
3091 sawConst = false;
3092 break;
3093 }
3094 sawConst = true;
3095 } else {
3096 if (sawType) {
3097 sawType = false;
3098 break;
3099 }
3100 if (Definition::Type::kKeyWord == lineIter->fType
3101 && KeyProperty::kNumber
3102 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3103 sawType = true;
3104 } else if (Definition::Type::kWord == lineIter->fType) {
3105 string typeName(lineIter->fContentStart, lineIter->length());
3106 if ("Sk" != name.substr(0, 2)) {
3107 sawType = true;
3108 }
3109 }
3110 }
3111 ++lineIter;
3112 }
3113 if (sawType && sawConst) {
3114 // if found, name first
3115 lineStart->fName = name;
3116 lineStart->fMarkType = MarkType::kConst;
3117 fParent->fChildren.emplace_back(lineStart);
3118 fConstExpr = lineStart;
3119 }
3120 }
3121 break;
Cary Clark8032b982017-07-28 11:04:54 -04003122 case ';':
3123 if (fInCharCommentString || fInBrace) {
3124 break;
3125 }
3126 if (!this->checkForWord()) {
3127 return false;
3128 }
Cary Clark0d225392018-06-07 09:59:07 -04003129 if (fConstExpr) {
3130 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3131 fConstExpr = nullptr;
3132 }
Cary Clark8032b982017-07-28 11:04:54 -04003133 if (Definition::Type::kKeyWord == fParent->fType
3134 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003135 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3136 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003137 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3138 this->popObject();
3139 }
Cary Clark8032b982017-07-28 11:04:54 -04003140 if (KeyWord::kEnum == fParent->fKeyWord) {
3141 fInEnum = false;
3142 }
Cary Clark61313f32018-10-08 14:57:48 -04003143 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003144 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003145 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3146 this->popObject();
3147 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003148 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003149 } else if (Definition::Type::kBracket == fParent->fType
3150 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3151 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3152 list<Definition>::iterator baseIter = fParent->fTokens.end();
3153 list<Definition>::iterator namedIter = fParent->fTokens.end();
3154 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003155 fParent->fTokens.begin() != tokenIter; ) {
3156 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003157 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003158 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003159 if (namedIter != fParent->fTokens.end()) {
3160 return reportError<bool>("found two named member tokens");
3161 }
3162 namedIter = tokenIter;
3163 }
3164 baseIter = tokenIter;
3165 } else {
3166 break;
3167 }
3168 }
3169 // FIXME: if a member definition spans multiple lines, this won't work
3170 if (namedIter != fParent->fTokens.end()) {
3171 if (baseIter == namedIter) {
3172 return this->reportError<bool>("expected type before named token");
3173 }
3174 Definition* member = &*namedIter;
3175 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003176 if (!member->fTerminator) {
3177 member->fTerminator = member->fContentEnd;
3178 }
Cary Clark8032b982017-07-28 11:04:54 -04003179 fParent->fChildren.push_back(member);
3180 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3181 member->fChildren.push_back(&*nameType);
3182 }
Cary Clark8032b982017-07-28 11:04:54 -04003183 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003184 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003185 } else if (fParent->fChildren.size() > 0) {
3186 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003187 Definition* priorEnum = fPriorEnum;
3188 fPriorEnum = nullptr;
3189 if (!priorEnum) {
3190 while (fParent->fChildren.begin() != lastIter) {
3191 std::advance(lastIter, -1);
3192 priorEnum = *lastIter;
3193 if (Definition::Type::kBracket != priorEnum->fType ||
3194 (Bracket::kSlashSlash != priorEnum->fBracket
3195 && Bracket::kSlashStar != priorEnum->fBracket)) {
3196 break;
3197 }
Cary Clark8032b982017-07-28 11:04:54 -04003198 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003199 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003200 }
3201 if (Definition::Type::kKeyWord == priorEnum->fType
3202 && KeyWord::kEnum == priorEnum->fKeyWord) {
3203 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003204 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003205 while (tokenWalker != fParent->fTokens.end()) {
3206 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003207 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003208 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3209 break;
3210 }
3211 }
3212 while (tokenWalker != fParent->fTokens.end()) {
3213 std::advance(tokenWalker, 1);
3214 const Definition* test = &*tokenWalker;
3215 if (Definition::Type::kBracket != test->fType ||
3216 (Bracket::kSlashSlash != test->fBracket
3217 && Bracket::kSlashStar != test->fBracket)) {
3218 break;
3219 }
3220 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003221 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003222 Definition* start = &*tokenWalker;
3223 bool foundExpected = true;
3224 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3225 const Definition* test = &*tokenWalker;
3226 if (expected != test->fKeyWord) {
3227 foundExpected = false;
3228 break;
3229 }
3230 if (tokenWalker == fParent->fTokens.end()) {
3231 break;
3232 }
3233 std::advance(tokenWalker, 1);
3234 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003235 if (!foundExpected) {
3236 foundExpected = true;
3237 tokenWalker = saveTokenWalker;
3238 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3239 const Definition* test = &*tokenWalker;
3240 if (expected != test->fKeyWord) {
3241 foundExpected = false;
3242 break;
3243 }
3244 if (tokenWalker == fParent->fTokens.end()) {
3245 break;
3246 }
3247 if (KeyWord::kNone != expected) {
3248 std::advance(tokenWalker, 1);
3249 }
3250 }
3251 if (foundExpected) {
3252 auto nameToken = priorEnum->fTokens.begin();
3253 string enumName = string(nameToken->fContentStart,
3254 nameToken->fContentEnd - nameToken->fContentStart);
3255 const Definition* test = &*tokenWalker;
3256 string constType = string(test->fContentStart,
3257 test->fContentEnd - test->fContentStart);
3258 if (enumName != constType) {
3259 foundExpected = false;
3260 } else {
3261 std::advance(tokenWalker, 1);
3262 }
3263 }
3264 }
Cary Clark8032b982017-07-28 11:04:54 -04003265 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3266 const char* nameStart = tokenWalker->fStart;
3267 std::advance(tokenWalker, 1);
3268 if (tokenWalker != fParent->fTokens.end()) {
3269 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003270 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003271 start->fName = string(nameStart, tp.fChar - nameStart);
3272 start->fContentEnd = fChar;
3273 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003274 fPriorEnum = priorEnum;
3275 }
Cary Clark8032b982017-07-28 11:04:54 -04003276 }
3277 }
3278 }
3279 this->addPunctuation(Punctuation::kSemicolon);
3280 fInFunction = false;
3281 break;
3282 case '~':
3283 if (fInEnum) {
3284 break;
3285 }
3286 case '0': case '1': case '2': case '3': case '4':
3287 case '5': case '6': case '7': case '8': case '9':
3288 // TODO: don't want to parse numbers, but do need to track for enum defs
3289 // break;
3290 case 'A': case 'B': case 'C': case 'D': case 'E':
3291 case 'F': case 'G': case 'H': case 'I': case 'J':
3292 case 'K': case 'L': case 'M': case 'N': case 'O':
3293 case 'P': case 'Q': case 'R': case 'S': case 'T':
3294 case 'U': case 'V': case 'W': case 'X': case 'Y':
3295 case 'Z': case '_':
3296 case 'a': case 'b': case 'c': case 'd': case 'e':
3297 case 'f': case 'g': case 'h': case 'i': case 'j':
3298 case 'k': case 'l': case 'm': case 'n': case 'o':
3299 case 'p': case 'q': case 'r': case 's': case 't':
3300 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003301 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003302 if (fInCharCommentString || fInBrace) {
3303 break;
3304 }
3305 if (!fIncludeWord) {
3306 fIncludeWord = fChar;
3307 }
3308 break;
3309 }
3310done:
3311 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003312 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003313 return true;
3314}
3315
3316void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003317 IncludeParser::ValidateKeyWords();
3318}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003319
Cary Clark186d08f2018-04-03 08:43:27 -04003320bool IncludeParser::references(const SkString& file) const {
3321 // if includes weren't passed one at a time, assume all references are valid
3322 if (fIncludeMap.empty()) {
3323 return true;
3324 }
3325 SkASSERT(file.endsWith(".bmh") );
3326 string root(file.c_str(), file.size() - 4);
3327 string kReference("_Reference");
3328 if (string::npos != root.find(kReference)) {
3329 root = root.substr(0, root.length() - kReference.length());
3330 }
3331 if (fIClassMap.end() != fIClassMap.find(root)) {
3332 return true;
3333 }
3334 if (fIStructMap.end() != fIStructMap.find(root)) {
3335 return true;
3336 }
Cary Clark224c7002018-06-27 11:00:21 -04003337 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3338 return true;
3339 }
3340 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3341 return true;
3342 }
Cary Clark186d08f2018-04-03 08:43:27 -04003343 return false;
3344}
3345
Cary Clark2dc84ad2018-01-26 12:56:22 -05003346void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3347 if (!sk_isdir(includes)) {
3348 IncludeParser::RemoveOneFile(docs, includes);
3349 } else {
3350 SkOSFile::Iter it(includes, ".h");
3351 for (SkString file; it.next(&file); ) {
3352 SkString p = SkOSPath::Join(includes, file.c_str());
3353 const char* hunk = p.c_str();
3354 if (!SkStrEndsWith(hunk, ".h")) {
3355 continue;
3356 }
3357 IncludeParser::RemoveOneFile(docs, hunk);
3358 }
3359 }
3360}
3361
3362void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3363 const char* lastForward = strrchr(includesFile, '/');
3364 const char* lastBackward = strrchr(includesFile, '\\');
3365 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3366 if (!last) {
3367 last = includesFile;
3368 } else {
3369 last += 1;
3370 }
3371 SkString baseName(last);
3372 SkASSERT(baseName.endsWith(".h"));
3373 baseName.remove(baseName.size() - 2, 2);
3374 baseName.append("_Reference.bmh");
3375 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3376 remove(fullName.c_str());
3377}
Cary Clark224c7002018-06-27 11:00:21 -04003378
3379Bracket IncludeParser::topBracket() const {
3380 Definition* parent = this->parentBracket(fParent);
3381 return parent ? parent->fBracket : Bracket::kNone;
3382}