blob: a9ec3db4d4d02c9834911e66bd531dd88e01a8f4 [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
Cary Clark2dc84ad2018-01-26 12:56:22 -05008#include "SkOSFile.h"
9#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040010
Cary Clark2da9fb82018-11-01 09:29:36 -040011#include "bmhParser.h"
12#include "includeParser.h"
13
Cary Clark8032b982017-07-28 11:04:54 -040014const IncludeKey kKeyWords[] = {
15 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040016 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040017 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clarkd2ca79c2018-08-10 13:09:13 -040018 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040019 { "bool", KeyWord::kBool, KeyProperty::kNumber },
20 { "char", KeyWord::kChar, KeyProperty::kNumber },
21 { "class", KeyWord::kClass, KeyProperty::kObject },
22 { "const", KeyWord::kConst, KeyProperty::kModifier },
23 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
24 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
25 { "double", KeyWord::kDouble, KeyProperty::kNumber },
26 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
27 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
28 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
29 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050030 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040031 { "float", KeyWord::kFloat, KeyProperty::kNumber },
32 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
33 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
34 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
35 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
36 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
37 { "inline", KeyWord::kInline, KeyProperty::kModifier },
38 { "int", KeyWord::kInt, KeyProperty::kNumber },
39 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
40 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
41 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
42 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
43 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
44 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
45 { "static", KeyWord::kStatic, KeyProperty::kModifier },
46 { "struct", KeyWord::kStruct, KeyProperty::kObject },
47 { "template", KeyWord::kTemplate, KeyProperty::kObject },
48 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clark0d225392018-06-07 09:59:07 -040049 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040050 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040051 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040052 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
53 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040054 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040055 { "union", KeyWord::kUnion, KeyProperty::kObject },
56 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
Cary Clark61313f32018-10-08 14:57:48 -040057 { "using", KeyWord::kUsing, KeyProperty::kObject },
Cary Clark8032b982017-07-28 11:04:54 -040058 { "void", KeyWord::kVoid, KeyProperty::kNumber },
59};
60
61const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
62
63KeyWord IncludeParser::FindKey(const char* start, const char* end) {
64 int ch = 0;
65 for (size_t index = 0; index < kKeyWordCount; ) {
66 if (start[ch] > kKeyWords[index].fName[ch]) {
67 ++index;
Cary Clark61313f32018-10-08 14:57:48 -040068 if (ch > 0 && (index == kKeyWordCount ||
69 kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
Cary Clark8032b982017-07-28 11:04:54 -040070 return KeyWord::kNone;
71 }
72 continue;
73 }
74 if (start[ch] < kKeyWords[index].fName[ch]) {
75 return KeyWord::kNone;
76 }
77 ++ch;
78 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040079 if (end - start < (int) strlen(kKeyWords[index].fName)) {
80 return KeyWord::kNone;
81 }
Cary Clark8032b982017-07-28 11:04:54 -040082 return kKeyWords[index].fKeyWord;
83 }
84 }
85 return KeyWord::kNone;
86}
87
88void IncludeParser::ValidateKeyWords() {
89 for (size_t index = 1; index < kKeyWordCount; ++index) {
90 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
91 == (int) kKeyWords[index].fKeyWord);
92 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
93 }
94}
95
96void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050097 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040098 fIncludeWord = nullptr;
99 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
100 Definition* def = &fParent->fTokens.back();
101 this->addDefinition(def);
102 if (KeyWord::kEnum == fParent->fKeyWord) {
103 fInEnum = true;
104 }
105 }
106}
107
Cary Clark61313f32018-10-08 14:57:48 -0400108static bool looks_like_method(const TextParser& tp) {
109 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
110 t.skipSpace();
111 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
112 && !t.skipExact("enum")) {
113 return true;
114 }
115 t.skipSpace();
116 if (t.skipExact("SK_API")) {
117 t.skipSpace();
118 }
119 if (!isupper(t.peek())) {
120 return true;
121 }
122 return nullptr != t.strnchr('(', t.fEnd);
123}
124
125static bool looks_like_forward_declaration(const TextParser& tp) {
126 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
127 t.skipSpace();
128 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
129 && !t.skipExact("enum")) {
130 return false;
131 }
132 t.skipSpace();
133 if (t.skipExact("SK_API")) {
134 t.skipSpace();
135 }
136 if (!isupper(t.peek())) {
137 return false;
138 }
139 t.skipToNonAlphaNum();
140 if (t.eof() || ';' != t.next()) {
141 return false;
142 }
143 if (t.eof() || '\n' != t.next()) {
144 return false;
145 }
146 return t.eof();
147}
148
149static bool looks_like_constructor(const TextParser& tp) {
150 TextParser t(tp.fFileName, tp.fLine, tp.lineEnd(), tp.fLineCount);
151 t.skipSpace();
152 if (!isupper(t.peek())) {
153 if (':' == t.next() && ' ' >= t.peek()) {
154 return true;
155 }
156 return false;
157 }
158 t.skipToNonAlphaNum();
159 if ('(' != t.peek()) {
160 return false;
161 }
162 if (!t.skipToEndBracket(')')) {
163 return false;
164 }
165 SkAssertResult(')' == t.next());
166 t.skipSpace();
167 return tp.fChar == t.fChar;
168}
169
170static bool looks_like_class_decl(const TextParser& tp) {
171 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
172 t.skipSpace();
173 if (!t.skipExact("class")) {
174 return false;
175 }
176 t.skipSpace();
177 if (t.skipExact("SK_API")) {
178 t.skipSpace();
179 }
180 if (!isupper(t.peek())) {
181 return false;
182 }
183 t.skipToNonAlphaNum();
184 return !t.skipToEndBracket('(');
185}
186
187static bool looks_like_const(const TextParser& tp) {
188 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
189 if (!t.startsWith("static constexpr ")) {
190 return false;
191 }
192 if (t.skipToEndBracket(" k")) {
193 SkAssertResult(t.skipExact(" k"));
194 } else if (t.skipToEndBracket(" SK_")) {
195 SkAssertResult(t.skipExact(" SK_"));
196 } else {
197 return false;
198 }
199 if (!isupper(t.peek())) {
200 return false;
201 }
202 return t.skipToEndBracket(" = ");
203}
204
205static bool looks_like_member(const TextParser& tp) {
206 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
207 const char* end = t.anyOf("(;");
208 if (!end || '(' == *end) {
209 return false;
210 }
211 bool foundMember = false;
212 do {
213 const char* next = t.anyOf(" ;");
214 if (';' == *next) {
215 break;
216 }
217 t.skipTo(next);
218 t.skipSpace();
219 foundMember = 'f' == t.fChar[0] && isupper(t.fChar[1]);
220 } while (true);
221 return foundMember;
222}
223
224static void skip_constructor_initializers(TextParser& t) {
225 SkAssertResult(':' == t.next());
226 do {
227 t.skipWhiteSpace();
228 t.skipToNonAlphaNum();
229 t.skipWhiteSpace();
230 if ('{' == t.peek()) {
231 t.skipToBalancedEndBracket('{', '}');
232 }
233 do {
234 const char* limiter = t.anyOf("(,{");
235 t.skipTo(limiter);
236 if ('(' != t.peek()) {
237 break;
238 }
239 t.skipToBalancedEndBracket('(', ')');
240 } while (true);
241 if ('{' == t.peek()) {
242 return;
243 }
244 SkAssertResult(',' == t.next());
245 } while (true);
246}
247
248static const char kInline[] = "inline ";
249static const char kSK_API[] = "SK_API ";
250static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
251
252bool IncludeParser::advanceInclude(TextParser& i) {
253 i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn);
254 if (fCheck.fPrivateBrace) {
255 if (i.startsWith("};")) {
256 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
257 fCheck.fPrivateBrace = 0;
258 fCheck.fDoubleReturn = true;
259 } else {
260 i.skipExact("};");
261 fCheck.fBraceCount -= 1;
262 }
263 return false;
264 }
265 if (i.startsWith("public:")) {
266 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
267 fCheck.fPrivateBrace = 0;
268 if (fCheck.fPrivateProtected) {
269 i.skipExact("public:");
270 }
271 } else {
272 i.skipExact("public:");
273 }
274 } else {
275 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
276 }
277 return false;
278 } else if (i.startsWith("};")) {
279 fCheck.fDoubleReturn = 2;
280 }
281 if (i.skipExact(kInline)) {
282 fCheck.fSkipInline = true;
283 return false;
284 }
285 if (i.skipExact(kSK_API)) {
286 fCheck.fSkipAPI = true;
287 return false;
288 }
289 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
290 fCheck.fSkipWarnUnused = true;
291 return false;
292 }
293 if (i.skipExact("SK_ATTR_DEPRECATED")) {
294 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
295 return false;
296 }
297 if (i.skipExact("SkDEBUGCODE")) {
298 i.skipWhiteSpace();
299 if ('(' != i.peek()) {
300 i.reportError("expected open paren");
301 }
302 TextParserSave save(&i);
303 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
304 fCheck.fInDebugCode = i.fChar - 1;
305 save.restore();
306 SkAssertResult('(' == i.next());
307 }
308 if ('{' == i.peek()) {
309 if (looks_like_method(i)) {
310 fCheck.fState = CheckCode::State::kMethod;
311 if (!i.skipToBalancedEndBracket('{', '}')) {
312 i.reportError("unbalanced open brace");
313 }
314 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
315 return false;
316 } else if (looks_like_class_decl(i)) {
317 fCheck.fState = CheckCode::State::kClassDeclaration;
318 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
319 fCheck.fPrivateProtected = false;
320 }
321 }
322 if (':' == i.peek() && looks_like_constructor(i)) {
323 fCheck.fState = CheckCode::State::kConstructor;
324 skip_constructor_initializers(i);
325 return false;
326 }
327 if ('#' == i.peek()) {
328 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
329 return false;
330 }
331 if (i.startsWith("//")) {
332 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
333 return false;
334 }
335 if (i.startsWith("/*")) {
336 i.skipToEndBracket("*/");
337 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
338 return false;
339 }
340 if (looks_like_forward_declaration(i)) {
341 fCheck.fState = CheckCode::State::kForwardDeclaration;
342 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
343 return false;
344 }
345 if (i.skipExact("private:") || i.skipExact("protected:")) {
346 if (!fCheck.fBraceCount) {
347 i.reportError("expect private in brace");
348 }
349 fCheck.fPrivateBrace = fCheck.fBraceCount;
350 fCheck.fPrivateProtected = true;
351 return false;
352 }
353 const char* funcEnd = i.anyOf("(\n");
354 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
355 && (i.contains("internal_", funcEnd, nullptr)
356 || i.contains("private_", funcEnd, nullptr)
357 || i.contains("legacy_", funcEnd, nullptr)
358 || i.contains("temporary_", funcEnd, nullptr))) {
359 i.skipTo(funcEnd);
360 if (!i.skipToBalancedEndBracket('(', ')')) {
361 i.reportError("unbalanced open parent");
362 }
363 i.skipSpace();
364 i.skipExact("const ");
365 i.skipSpace();
366 if (';' == i.peek()) {
367 i.next();
368 }
369 fCheck.fState = CheckCode::State::kNone;
370 return false;
371 }
372 return true;
373}
374
375void IncludeParser::codeBlockAppend(string& result, char ch) const {
376 if (Elided::kYes == fElided && fCheck.fBraceCount) {
377 return;
378 }
379 this->stringAppend(result, ch);
380}
381
382void IncludeParser::codeBlockSpaces(string& result, int indent) const {
383 if (!indent) {
384 return;
385 }
386 if (Elided::kYes == fElided && fCheck.fBraceCount) {
387 return;
388 }
389 SkASSERT(indent > 0);
390 if (fDebugWriteCodeBlock) {
391 SkDebugf("%*c", indent, ' ');
392 }
393 result.append(indent, ' ');
394}
395
396string IncludeParser::writeCodeBlock(const Definition& iDef, MarkType markType) {
397 TextParser i(&iDef);
398 fElided = Elided::kNo;
399 const char* before = iDef.fContentStart;
400 while (' ' == *--before)
401 ;
402 int startIndent = iDef.fContentStart - before - 1;
403 return writeCodeBlock(i, markType, startIndent);
404}
405
406string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
407 string result;
408 char last;
409 int lastIndent = 0;
410 bool lastDoubleMeUp = false;
411 fCheck.reset();
Cary Clarka90ea222018-10-16 10:30:28 -0400412 if (MarkType::kDefine == markType) {
413 result = "#define ";
414 } else {
415 this->codeBlockSpaces(result, startIndent);
416 }
Cary Clark61313f32018-10-08 14:57:48 -0400417 do {
418 if (!this->advanceInclude(i)) {
419 continue;
420 }
421 do {
422 last = i.peek();
423 SkASSERT(' ' < last);
424 if (fCheck.fInDebugCode == i.fChar) {
425 fCheck.fInDebugCode = nullptr;
426 i.next(); // skip close paren
427 break;
428 }
429 if (CheckCode::State::kMethod == fCheck.fState) {
430 this->codeBlockAppend(result, ';');
431 fCheck.fState = CheckCode::State::kNone;
432 }
433 if (fCheck.fWriteReturn) {
434 this->codeBlockAppend(result, '\n');
435 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
436 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
437 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
438 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
439 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
440 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
441 this->codeBlockAppend(result, '\n');
442 }
443 fCheck.fTypedefReturn = false;
444 lastDoubleMeUp = doubleMeUp;
445 }
446 if (doubleMeUp) {
447 fCheck.fTypedefReturn = true;
448 }
449 lastIndent = fCheck.fIndent;
450 }
451 if (fCheck.fIndent) {
452 size_t indent = fCheck.fIndent;
453 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
454 indent -= sizeof(kInline) - 1;
455 }
456 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
457 indent -= sizeof(kSK_API) - 1;
458 }
459 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
460 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
461 }
462
463 this->codeBlockSpaces(result, indent);
464 }
465 this->codeBlockAppend(result, last);
466 fCheck.fWriteReturn = false;
467 fCheck.fIndent = 0;
468 fCheck.fBraceCount += '{' == last;
469 fCheck.fBraceCount -= '}' == last;
470 if (';' == last) {
471 fCheck.fSkipInline = false;
472 fCheck.fSkipAPI = false;
473 fCheck.fSkipWarnUnused = false;
474 }
475 if (fCheck.fBraceCount < 0) {
476 i.reportError("unbalanced close brace");
477 return result;
478 }
479 i.next();
480 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
481 } while (!i.eof());
482 if (CheckCode::State::kMethod == fCheck.fState) {
483 this->codeBlockAppend(result, ';');
484 }
485 bool elidedTemplate = Elided::kYes == fElided && !strncmp(i.fStart, "template ", 9);
486 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
487 if (fCheck.fWriteReturn || elidedTClass) {
488 this->codeBlockAppend(result, '\n');
489 }
490 if ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass) {
491 this->codeBlockAppend(result, '}');
492 }
493 this->codeBlockAppend(result, ';');
494 if (MarkType::kFunction != markType || elidedTemplate) {
495 this->codeBlockAppend(result, '\n');
496 }
497 return result;
498}
499
Ben Wagner63fd7602017-10-09 15:45:33 -0400500void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400501 const vector<string>& foundParams) {
502 for (auto& methodParam : methodParams) {
503 bool found = false;
504 for (auto& foundParam : foundParams) {
505 if (methodParam == foundParam) {
506 found = true;
507 break;
508 }
509 }
510 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400511 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400512 }
513 }
514 for (auto& foundParam : foundParams) {
515 bool found = false;
516 for (auto& methodParam : methodParams) {
517 if (methodParam == foundParam) {
518 found = true;
519 break;
520 }
521 }
522 if (!found) {
523 this->reportError("doxygen param does not match method declaration");
524 }
525 }
526}
527
528bool IncludeParser::checkForWord() {
529 if (!fIncludeWord) {
530 return true;
531 }
532 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400533 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
534 Bracket bracket = this->topBracket();
535 if (Bracket::kParen == bracket) {
536 return true;
537 }
538 }
Cary Clark8032b982017-07-28 11:04:54 -0400539 if (KeyWord::kNone != keyWord) {
540 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
541 this->addKeyword(keyWord);
542 return true;
543 }
544 } else {
545 this->addWord();
546 return true;
547 }
548 Definition* poundDef = fParent;
549 if (!fParent) {
550 return reportError<bool>("expected parent");
551 }
552 if (Definition::Type::kBracket != poundDef->fType) {
553 return reportError<bool>("expected bracket");
554 }
555 if (Bracket::kPound != poundDef->fBracket) {
556 return reportError<bool>("expected preprocessor");
557 }
558 if (KeyWord::kNone != poundDef->fKeyWord) {
559 return reportError<bool>("already found keyword");
560 }
561 poundDef->fKeyWord = keyWord;
562 fIncludeWord = nullptr;
563 switch (keyWord) {
564 // these do not link to other # directives
565 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400566 if (!fInBrace) {
567 SkASSERT(!fInDefine);
568 fInDefine = true;
569 }
Cary Clark8032b982017-07-28 11:04:54 -0400570 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500571 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400572 break;
573 // these start a # directive link
574 case KeyWord::kIf:
575 case KeyWord::kIfdef:
576 case KeyWord::kIfndef:
577 break;
578 // these continue a # directive link
579 case KeyWord::kElif:
Cary Clarkabaffd82018-11-15 08:25:12 -0500580 case KeyWord::kElse:
Cary Clark8032b982017-07-28 11:04:54 -0400581 this->popObject(); // pop elif
582 if (Bracket::kPound != fParent->fBracket) {
583 return this->reportError<bool>("expected preprocessor directive");
584 }
585 this->popBracket(); // pop if
586 poundDef->fParent = fParent;
Cary Clarkabaffd82018-11-15 08:25:12 -0500587 fParent = poundDef; // push elif back
588 break;
Cary Clark8032b982017-07-28 11:04:54 -0400589 // this ends a # directive link
590 case KeyWord::kEndif:
591 // FIXME : should this be calling popBracket() instead?
592 this->popObject(); // pop endif
593 if (Bracket::kPound != fParent->fBracket) {
594 return this->reportError<bool>("expected preprocessor directive");
595 }
596 this->popBracket(); // pop if/else
597 break;
598 default:
599 SkASSERT(0);
600 }
601 return true;
602}
603
604string IncludeParser::className() const {
605 string name(fParent->fName);
606 size_t slash = name.find_last_of("/");
607 if (string::npos == slash) {
608 slash = name.find_last_of("\\");
609 }
610 SkASSERT(string::npos != slash);
611 string result = name.substr(slash);
612 result = result.substr(1, result.size() - 3);
613 return result;
614}
615
Cary Clarka90ea222018-10-16 10:30:28 -0400616void IncludeParser::writeCodeBlock() {
Cary Clark61313f32018-10-08 14:57:48 -0400617 for (auto& classMapper : fIClassMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400618 classMapper.second.fCode = this->writeCodeBlock(classMapper.second, MarkType::kClass);
Cary Clark61313f32018-10-08 14:57:48 -0400619 }
620 for (auto& enumMapper : fIEnumMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400621 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second,
622 enumMapper.second->fMarkType);
623 }
624 for (auto& typedefMapper : fITypedefMap) {
625 typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second,
626 typedefMapper.second->fMarkType);
627 }
628 for (auto& defineMapper : fIDefineMap) {
629 defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second,
630 defineMapper.second->fMarkType);
Cary Clark61313f32018-10-08 14:57:48 -0400631 }
632}
633
Cary Clark884dd7d2017-10-11 10:37:52 -0400634#include <sstream>
635#include <iostream>
636
Cary Clark8032b982017-07-28 11:04:54 -0400637bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400638 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400639 string className = classMapper.first;
640 auto finder = bmhParser.fClassMap.find(className);
641 if (bmhParser.fClassMap.end() == finder) {
642 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400643 continue;
644 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400645 }
646 for (auto& classMapper : fIClassMap) {
Cary Clarkabaffd82018-11-15 08:25:12 -0500647 if (classMapper.second.fUndocumented) {
648 continue;
649 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400650 string className = classMapper.first;
651 std::istringstream iss(className);
652 string classStr;
653 string classBase;
654 RootDefinition* root = nullptr;
655 while (std::getline(iss, classStr, ':')) {
656 if (root) {
657 if (!classStr.length()) {
658 continue;
659 }
660 classBase += "::" + classStr;
661 auto finder = root->fBranches.find(classBase);
662 if (root->fBranches.end() != finder) {
663 root = finder->second;
664 } else {
665 SkASSERT(0);
666 }
667 } else {
668 classBase = classStr;
669 auto finder = bmhParser.fClassMap.find(classBase);
670 if (bmhParser.fClassMap.end() != finder) {
671 root = &finder->second;
672 } else {
673 SkASSERT(0);
674 }
675 }
676 }
Cary Clark8032b982017-07-28 11:04:54 -0400677 auto& classMap = classMapper.second;
678 auto& tokens = classMap.fTokens;
679 for (const auto& token : tokens) {
680 if (token.fPrivate) {
681 continue;
682 }
683 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400684 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400685 switch (token.fMarkType) {
686 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400687 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400688 continue;
689 }
Cary Clark8032b982017-07-28 11:04:54 -0400690 if (!def) {
691 string paramName = className + "::";
692 paramName += string(token.fContentStart,
693 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400694 if (string::npos != paramName.find('\n')) {
695 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
696 paramName.end());
697 }
Cary Clarkce101242017-09-01 15:51:02 -0400698 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400699 if (!def && 0 == token.fName.find("operator")) {
700 string operatorName = className + "::";
701 TextParser oper("", token.fStart, token.fContentEnd, 0);
702 const char* start = oper.strnstr("operator", token.fContentEnd);
703 SkASSERT(start);
704 oper.skipTo(start);
705 oper.skipToEndBracket('(');
706 int parens = 0;
707 do {
708 if ('(' == oper.peek()) {
709 ++parens;
710 } else if (')' == oper.peek()) {
711 --parens;
712 }
713 } while (!oper.eof() && oper.next() && parens > 0);
714 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400715 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400716 }
717 }
718 if (!def) {
719 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
720 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400721 const char* tokenEnd = token.methodEnd();
Cary Clark8032b982017-07-28 11:04:54 -0400722 string constructorName = className + "::";
723 constructorName += string(token.fContentStart + skip,
Cary Clarkab5c9af2018-07-12 16:24:53 -0400724 tokenEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400725 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400726 }
727 if (!def && 0 == token.fName.find("SK_")) {
728 string incName = token.fName + "()";
729 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400730 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400731 if (def) {
732 if (def->fName == incName) {
733 def->fVisited = true;
734 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400735 def = root->find(className + "::toString",
736 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400737 if (def) {
738 def->fVisited = true;
739 } else {
740 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500741 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400742 }
743 }
744 break;
745 } else {
746 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500747 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400748 }
749 }
750 }
751 if (!def) {
752 bool allLower = true;
753 for (size_t index = 0; index < token.fName.length(); ++index) {
754 if (!islower(token.fName[index])) {
755 allLower = false;
756 break;
757 }
758 }
759 if (allLower) {
760 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400761 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400762 }
763 }
764 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400765 if (0 == token.fName.find("SkDEBUGCODE")) {
766 break;
767 }
768 }
769 if (!def) {
770 // simple method names inside nested classes have a bug and are missing trailing parens
771 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400772 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400773 }
774 if (!def) {
Cary Clarkabaffd82018-11-15 08:25:12 -0500775 if (!token.fUndocumented) {
Cary Clark56356312018-02-08 14:45:18 -0500776 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
777 fFailed = true;
778 }
Cary Clark8032b982017-07-28 11:04:54 -0400779 break;
780 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500781 if (token.fUndocumented) {
782 break;
783 }
Cary Clark73fa9722017-08-29 17:36:51 -0400784 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400785 def->fVisited = true;
786 } else {
787 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500788 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400789 }
790 } break;
791 case MarkType::kComment:
792 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400793 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400794 case MarkType::kEnum: {
795 if (!def) {
796 // work backwards from first word to deduce #Enum name
797 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
798 SkAssertResult(firstMember.skipName("enum"));
799 SkAssertResult(firstMember.skipToEndBracket('{'));
800 firstMember.next();
801 firstMember.skipWhiteSpace();
802 SkASSERT('k' == firstMember.peek());
803 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400804 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400805 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400806 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400807 const char* lastUnderscore = nullptr;
808 do {
809 if (!firstMember.skipToEndBracket('_')) {
810 break;
811 }
812 if (firstMember.fChar > wordEnd) {
813 break;
814 }
815 lastUnderscore = firstMember.fChar;
816 } while (firstMember.next());
817 if (lastUnderscore) {
818 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400819 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400820 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400821 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400822 }
823 if (!def) {
Cary Clarkabaffd82018-11-15 08:25:12 -0500824 if (!token.fUndocumented) {
Cary Clark56356312018-02-08 14:45:18 -0500825 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
826 fFailed = true;
827 }
Cary Clark8032b982017-07-28 11:04:54 -0400828 break;
829 }
830 }
831 def->fVisited = true;
Cary Clark61313f32018-10-08 14:57:48 -0400832 bool hasCode = false;
833 bool hasPopulate = true;
Cary Clark8032b982017-07-28 11:04:54 -0400834 for (auto& child : def->fChildren) {
835 if (MarkType::kCode == child->fMarkType) {
Cary Clark61313f32018-10-08 14:57:48 -0400836 hasPopulate = std::any_of(child->fChildren.begin(),
837 child->fChildren.end(), [](auto grandChild){
838 return MarkType::kPopulate == grandChild->fMarkType; });
839 if (!hasPopulate) {
840 def = child;
841 }
842 hasCode = true;
Cary Clark8032b982017-07-28 11:04:54 -0400843 break;
844 }
845 }
Cary Clark61313f32018-10-08 14:57:48 -0400846 if (!hasCode) {
Cary Clarkabaffd82018-11-15 08:25:12 -0500847 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
848 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400849 break;
850 }
Cary Clark61313f32018-10-08 14:57:48 -0400851 if (!hasPopulate) {
852 if (def->crossCheck(token)) {
853 def->fVisited = true;
854 } else {
855 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
856 fFailed = true;
857 }
Cary Clark8032b982017-07-28 11:04:54 -0400858 }
Cary Clarkabaffd82018-11-15 08:25:12 -0500859 for (auto& member : token.fTokens) {
860 if (MarkType::kMember != member.fMarkType) {
861 continue;
862 }
Cary Clarkf05bdda2017-08-24 12:59:48 -0400863 string constName = MarkType::kEnumClass == token.fMarkType ?
864 fullName : className;
Cary Clarkabaffd82018-11-15 08:25:12 -0500865 constName += "::" + member.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400866 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400867 if (!def) {
Cary Clarkabaffd82018-11-15 08:25:12 -0500868 string innerName = classMapper.first + "::" + member.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400869 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400870 }
871 if (!def) {
Cary Clarkabaffd82018-11-15 08:25:12 -0500872 if (!member.fUndocumented) {
873 SkDebugf("const missing from bmh: %s\n", constName.c_str());
874 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400875 }
876 } else {
877 def->fVisited = true;
878 }
879 }
880 } break;
881 case MarkType::kMember:
882 if (def) {
883 def->fVisited = true;
Cary Clarkabaffd82018-11-15 08:25:12 -0500884 } else {
Cary Clark8032b982017-07-28 11:04:54 -0400885 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500886 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400887 }
888 break;
Cary Clark2f466242017-12-11 16:03:17 -0500889 case MarkType::kTypedef:
890 if (def) {
891 def->fVisited = true;
Cary Clarkabaffd82018-11-15 08:25:12 -0500892 } else {
Cary Clark2f466242017-12-11 16:03:17 -0500893 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500894 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500895 }
896 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400897 case MarkType::kConst:
898 if (def) {
899 def->fVisited = true;
Cary Clarkabaffd82018-11-15 08:25:12 -0500900 } else {
901 if (!token.fUndocumented) {
902 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
903 fFailed = true;
904 }
Cary Clarkd98f78c2018-04-26 08:32:37 -0400905 }
906 break;
Cary Clark8032b982017-07-28 11:04:54 -0400907 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400908 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400909 break;
910 }
911 }
912 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500913 int crossChecks = 0;
914 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400915 for (auto& classMapper : fIClassMap) {
916 string className = classMapper.first;
917 auto finder = bmhParser.fClassMap.find(className);
918 if (bmhParser.fClassMap.end() == finder) {
919 continue;
920 }
921 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500922 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500923 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400924 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500925 if (crossChecks) {
926 SkDebugf(".");
927 } else {
928 SkDebugf("cross-check");
929 firstCheck = className;
930 }
931 ++crossChecks;
932 }
933 if (crossChecks) {
934 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500935 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500936 }
937 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400938 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400939 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500940 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400941}
942
943IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400944 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400945 string className;
946 const Definition* test = fParent;
947 while (Definition::Type::kFileType != test->fType) {
948 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
949 className = test->fName + "::";
950 break;
951 }
952 test = test->fParent;
953 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400954 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400955 unordered_map<string, IClassDefinition>& map = fIClassMap;
956 IClassDefinition& markupDef = map[className];
957 if (markupDef.fStart) {
958 typedef IClassDefinition* IClassDefPtr;
959 return INHERITED::reportError<IClassDefPtr>("class already defined");
960 }
961 markupDef.fFileName = fFileName;
962 markupDef.fStart = includeDef.fStart;
963 markupDef.fContentStart = includeDef.fStart;
964 markupDef.fName = className;
965 markupDef.fContentEnd = includeDef.fContentEnd;
966 markupDef.fTerminator = includeDef.fTerminator;
967 markupDef.fParent = fParent;
968 markupDef.fLineCount = fLineCount;
969 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
970 MarkType::kStruct : MarkType::kClass;
971 markupDef.fKeyWord = includeDef.fKeyWord;
972 markupDef.fType = Definition::Type::kMark;
Cary Clarkabaffd82018-11-15 08:25:12 -0500973 auto tokenIter = includeDef.fParent->fTokens.begin();
974 SkASSERT(includeDef.fParentIndex > 0);
975 std::advance(tokenIter, includeDef.fParentIndex - 1);
976 const Definition* priorComment = &*tokenIter;
977 markupDef.fUndocumented = priorComment->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -0400978 fParent = &markupDef;
979 return &markupDef;
980}
981
982void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
983 auto& tokens = classDef.fTokens;
984 for (auto& token : tokens) {
985 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
986 continue;
987 }
988 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400989 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400990 }
991 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400992 case MarkType::kConst:
993 this->dumpConst(token, classDef.fName);
994 break;
Cary Clark8032b982017-07-28 11:04:54 -0400995 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500996 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500997 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400998 break;
999 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001000 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -04001001 break;
1002 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -04001003 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -04001004 continue;
1005 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001006 case MarkType::kTypedef:
1007 this->dumpTypedef(token, classDef.fName);
1008 break;
Cary Clark8032b982017-07-28 11:04:54 -04001009 default:
1010 SkASSERT(0);
1011 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001012 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -04001013 }
1014}
Cary Clark9174bda2017-09-19 17:39:32 -04001015void IncludeParser::dumpComment(const Definition& token) {
1016 fLineCount = token.fLineCount;
1017 fChar = fLine = token.fContentStart;
1018 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001019 bool sawParam = false;
1020 bool multiline = false;
1021 bool sawReturn = false;
1022 bool sawComment = false;
1023 bool methodHasReturn = false;
1024 vector<string> methodParams;
1025 vector<string> foundParams;
1026 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -04001027 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
1028 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001029 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -04001030 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001031 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -04001032 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -04001033 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -05001034 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -04001035 && !methodParser.strnchr('~', methodParser.fEnd);
1036 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
1037 const char* nextEnd = paren;
1038 do {
1039 string paramName;
1040 methodParser.fChar = nextEnd + 1;
1041 methodParser.skipSpace();
1042 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
1043 continue;
1044 }
1045 methodParams.push_back(paramName);
1046 } while (')' != nextEnd[0]);
1047 }
Cary Clark9174bda2017-09-19 17:39:32 -04001048 for (const auto& child : token.fTokens) {
1049 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1050 break;
1051 }
Cary Clark8032b982017-07-28 11:04:54 -04001052 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001053 if (child.fPrivate) {
1054 break;
1055 }
Cary Clark8032b982017-07-28 11:04:54 -04001056 if ('@' == child.fContentStart[0]) {
1057 TextParser parser(&child);
1058 do {
1059 parser.next();
1060 if (parser.startsWith("param ")) {
1061 parser.skipWord("param");
1062 const char* parmStart = parser.fChar;
1063 parser.skipToSpace();
1064 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
1065 parser.skipWhiteSpace();
1066 do {
1067 size_t nextComma = parmName.find(',');
1068 string piece;
1069 if (string::npos == nextComma) {
1070 piece = parmName;
1071 parmName = "";
1072 } else {
1073 piece = parmName.substr(0, nextComma);
1074 parmName = parmName.substr(nextComma + 1);
1075 }
1076 if (sawParam) {
1077 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001078 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001079 }
Cary Clark9174bda2017-09-19 17:39:32 -04001080 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001081 } else {
1082 if (sawComment) {
1083 this->nl();
1084 }
1085 this->lf(2);
1086 }
1087 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -04001088 this->writeTag("Param", piece);
1089 this->writeSpace(2);
1090 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1091 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001092 sawParam = true;
1093 sawComment = false;
1094 } while (parmName.length());
1095 parser.skipTo(parser.fEnd);
1096 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
1097 parser.skipWord("return");
1098 if ('s' == parser.peek()) {
1099 parser.next();
1100 }
1101 if (sawParam) {
1102 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001103 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001104 }
Cary Clark9174bda2017-09-19 17:39:32 -04001105 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001106 }
1107 this->checkForMissingParams(methodParams, foundParams);
1108 sawParam = false;
1109 sawComment = false;
1110 multiline = false;
1111 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001112 this->writeTag("Return");
1113 this->writeSpace(2);
1114 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1115 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001116 sawReturn = true;
1117 parser.skipTo(parser.fEnd);
1118 } else {
1119 this->reportError("unexpected doxygen directive");
1120 }
1121 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -04001122 } else if (child.length() > 1) {
1123 const char* start = child.fContentStart;
1124 ptrdiff_t length = child.fContentEnd - start;
1125 SkASSERT(length >= 0);
1126 while (length && '/' == start[0]) {
1127 start += 1;
1128 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001129 }
Cary Clark9174bda2017-09-19 17:39:32 -04001130 while (length && '/' == start[length - 1]) {
1131 length -= 1;
1132 if (length && '*' == start[length - 1]) {
1133 length -= 1;
1134 }
1135 }
1136 if (length) {
1137 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
1138 if (sawParam || sawReturn) {
1139 this->indentToColumn(8);
1140 }
1141 this->writeBlock(length, start);
1142 this->writeSpace();
1143 sawComment = true;
1144 if (sawParam || sawReturn) {
1145 multiline = true;
1146 }
Cary Clark8032b982017-07-28 11:04:54 -04001147 }
1148 }
1149 }
1150 }
1151 if (sawParam || sawReturn) {
1152 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001153 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001154 }
Cary Clark9174bda2017-09-19 17:39:32 -04001155 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001156 }
1157 if (!sawReturn) {
1158 if (!sawParam) {
1159 if (sawComment) {
1160 this->nl();
1161 }
1162 this->lf(2);
1163 }
1164 this->checkForMissingParams(methodParams, foundParams);
1165 }
1166 if (methodHasReturn != sawReturn) {
1167 if (!methodHasReturn) {
1168 this->reportError("unexpected doxygen return");
1169 } else {
1170 if (sawComment) {
1171 this->nl();
1172 }
1173 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001174 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -04001175 }
1176 }
1177}
1178
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001179void IncludeParser::dumpCommonTail(const Definition& token) {
1180 this->lf(2);
1181 this->writeTag("Example");
1182 this->lf(1);
1183 this->writeString("// incomplete");
1184 this->lf(1);
1185 this->writeEndTag();
1186 this->lf(2);
1187 this->writeTag("SeeAlso");
1188 this->writeSpace();
1189 this->writeString("incomplete");
1190 this->lf(2);
1191 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1192 this->lf(2);
1193}
1194
Cary Clark224c7002018-06-27 11:00:21 -04001195void IncludeParser::dumpConst(const Definition& token, string className) {
1196 this->writeTag("Const");
1197 this->writeSpace();
1198 this->writeString(token.fName);
1199 this->writeTagTable("Line", "incomplete");
1200 this->lf(2);
1201 this->dumpComment(token);
1202}
1203
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001204void IncludeParser::dumpDefine(const Definition& token) {
1205 this->writeTag("Define", token.fName);
1206 this->lf(2);
1207 this->writeTag("Code");
1208 this->lfAlways(1);
1209 this->writeString("###$");
1210 this->lfAlways(1);
1211 this->indentToColumn(4);
1212 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1213 this->lf(1);
1214 this->indentToColumn(0);
1215 this->writeString("$$$#");
1216
1217 this->writeEndTag();
1218 this->lf(2);
1219 this->dumpComment(token);
1220 for (auto& child : token.fTokens) {
1221 if (MarkType::kComment == child.fMarkType) {
1222 continue;
1223 }
1224 this->writeTag("Param", child.fName);
1225 this->writeSpace();
1226 this->writeString("incomplete");
1227 this->writeSpace();
1228 this->writeString("##");
1229 this->lf(1);
1230 }
1231}
1232
1233void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001234 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -04001235 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001236 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -04001237 this->lfAlways(1);
1238 this->indentToColumn(4);
1239 this->writeString("enum");
1240 this->writeSpace();
1241 if ("_anonymous" != token.fName.substr(0, 10)) {
1242 this->writeString(token.fName);
1243 this->writeSpace();
1244 }
1245 this->writeString("{");
1246 this->lfAlways(1);
1247 for (auto& child : token.fChildren) {
1248 this->indentToColumn(8);
1249 this->writeString(child->fName);
1250 if (child->length()) {
1251 this->writeSpace();
1252 this->writeBlock(child->length(), child->fContentStart);
1253 }
1254 if (',' != fLastChar) {
1255 this->writeString(",");
1256 }
1257 this->lfAlways(1);
1258 }
1259 this->indentToColumn(4);
1260 this->writeString("};");
1261 this->lf(1);
1262 this->writeString("##");
1263 this->lf(2);
1264 this->dumpComment(token);
1265 for (auto& child : token.fChildren) {
Cary Clark61313f32018-10-08 14:57:48 -04001266 // TODO: get comments before or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001267 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001268 this->writeSpace();
1269 this->writeString(child->fName);
1270 TextParser val(child);
1271 if (!val.eof()) {
1272 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
1273 val.next();
1274 val.skipSpace();
1275 const char* valEnd = val.anyOf(",\n");
1276 if (!valEnd) {
1277 valEnd = val.fEnd;
1278 }
1279 this->writeSpace();
1280 this->writeBlock(valEnd - val.fStart, val.fStart);
1281 } else {
1282 this->writeSpace();
1283 this->writeDefinition(*child);
1284 }
1285 }
1286 this->lf(1);
1287 for (auto comment : child->fChildren) {
1288 if (MarkType::kComment == comment->fMarkType) {
1289 TextParser parser(comment);
1290 parser.skipExact("*");
1291 parser.skipExact("*");
1292 while (!parser.eof() && parser.skipWhiteSpace()) {
1293 parser.skipExact("*");
1294 parser.skipWhiteSpace();
1295 const char* start = parser.fChar;
1296 parser.skipToEndBracket('\n');
1297 this->lf(1);
1298 this->writeBlock(parser.fChar - start, start);
1299 }
1300 }
1301 }
1302 this->writeEndTag();
1303 }
1304 this->lf(2);
1305}
1306
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001307bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1308 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1309 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1310 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001311 return true;
1312 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001313 size_t lastBSlash = fFileName.rfind('\\');
1314 size_t lastSlash = fFileName.rfind('/');
1315 size_t lastDotH = fFileName.rfind(".h");
1316 SkASSERT(string::npos != lastDotH);
1317 if (string::npos != lastBSlash && (string::npos == lastSlash
1318 || lastBSlash < lastSlash)) {
1319 lastSlash = lastBSlash;
1320 } else if (string::npos == lastSlash) {
1321 lastSlash = -1;
1322 }
1323 lastSlash += 1;
1324 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1325 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001326 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001327 fOut = fopen(fileName.c_str(), "wb");
1328 if (!fOut) {
1329 SkDebugf("could not open output file %s\n", globalsName.c_str());
1330 return false;
1331 }
1332 string prefixName = globalsName.substr(0, 2);
1333 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1334 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1335 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001336 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001337 this->lf(2);
1338 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001339 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001340 this->writeEndTag();
1341 this->lf(2);
1342 if (!fIDefineMap.empty()) {
1343 this->writeTag("Subtopic", "Define");
1344 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -04001345 this->writeEndTag();
1346 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001347 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001348 if (!fIFunctionMap.empty()) {
1349 this->writeTag("Subtopic", "Function");
1350 this->writeTag("Populate");
1351 this->writeEndTag();
1352 this->lf(2);
1353 }
1354 if (!fIEnumMap.empty()) {
1355 this->writeTag("Subtopic", "Enum");
1356 this->writeTag("Populate");
1357 this->writeEndTag();
1358 this->lf(2);
1359 }
1360 if (!fITemplateMap.empty()) {
1361 this->writeTag("Subtopic", "Template");
1362 this->writeTag("Populate");
1363 this->writeEndTag();
1364 this->lf(2);
1365 }
1366 if (!fITypedefMap.empty()) {
1367 this->writeTag("Subtopic", "Typedef");
1368 this->writeTag("Populate");
1369 this->writeEndTag();
1370 this->lf(2);
1371 }
1372 if (!fIUnionMap.empty()) {
1373 this->writeTag("Subtopic", "Union");
1374 this->writeTag("Populate");
1375 this->writeEndTag();
1376 this->lf(2);
1377 }
1378 std::map<int, Definition*> sortedDefs;
1379 for (const auto& entry : fIDefineMap) {
1380 sortedDefs[entry.second->fLineCount] = entry.second;
1381 }
1382 for (const auto& entry : fIFunctionMap) {
1383 sortedDefs[entry.second->fLineCount] = entry.second;
1384 }
1385 for (const auto& entry : fIEnumMap) {
1386 sortedDefs[entry.second->fLineCount] = entry.second;
1387 }
1388 for (const auto& entry : fITemplateMap) {
1389 sortedDefs[entry.second->fLineCount] = entry.second;
1390 }
1391 for (const auto& entry : fITypedefMap) {
1392 sortedDefs[entry.second->fLineCount] = entry.second;
1393 }
1394 for (const auto& entry : fIUnionMap) {
1395 sortedDefs[entry.second->fLineCount] = entry.second;
1396 }
1397 for (const auto& entry : sortedDefs) {
1398 const Definition* def = entry.second;
1399 this->writeBlockSeparator();
1400 switch (def->fMarkType) {
1401 case MarkType::kDefine:
1402 this->dumpDefine(*def);
1403 break;
1404 case MarkType::kMethod:
1405 this->dumpMethod(*def, globalsName);
1406 break;
1407 case MarkType::kEnum:
1408 case MarkType::kEnumClass:
1409 this->dumpEnum(*def, globalsName);
1410 break;
1411 case MarkType::kTemplate:
1412 SkASSERT(0); // incomplete
1413 break;
1414 case MarkType::kTypedef: {
1415 this->writeTag("Typedef");
1416 this->writeSpace();
1417 TextParser parser(def);
1418 if (!parser.skipExact("typedef")) {
1419 return false;
1420 }
1421 if (!parser.skipSpace()) {
1422 return false;
1423 }
1424 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1425 this->lf(2);
1426 this->dumpComment(*def);
1427 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1428 this->lf(2);
1429 } continue;
1430 case MarkType::kUnion:
1431 SkASSERT(0); // incomplete
1432 break;
1433 default:
1434 SkASSERT(0);
1435 }
1436 this->dumpCommonTail(*def);
1437 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001438 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001439 this->writeEndTag("Topic", topicName);
1440 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001441// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001442 SkDebugf("wrote %s\n", fileName.c_str());
1443 return true;
1444}
1445
1446bool IncludeParser::isClone(const Definition& token) {
1447 string name = token.fName;
1448 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1449}
1450
1451bool IncludeParser::isConstructor(const Definition& token, string className) {
1452 string name = token.fName;
1453 return 0 == name.find(className) || '~' == name[0];
1454}
1455
1456bool IncludeParser::isInternalName(const Definition& token) {
1457 string name = token.fName;
1458 // exception for this SkCanvas function .. for now
1459 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1460 return false;
1461 }
1462 return name.substr(0, 7) == "android"
1463 || 0 == token.fName.find("internal_")
1464 || 0 == token.fName.find("Internal_")
1465 || 0 == token.fName.find("legacy_")
1466 || 0 == token.fName.find("temporary_")
1467 || 0 == token.fName.find("private_");
1468}
1469
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001470bool IncludeParser::isMember(const Definition& token) const {
1471 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1472 return true;
1473 }
1474 if (!islower(token.fStart[0])) {
1475 return false;
1476 }
1477 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1478 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1479 const Definition* structToken = token.fParent;
1480 if (!structToken) {
1481 return false;
1482 }
1483 if (KeyWord::kStruct != structToken->fKeyWord) {
1484 structToken = token.fParent->fParent;
1485 if (!structToken) {
1486 return false;
1487 }
1488 if (KeyWord::kStruct != structToken->fKeyWord) {
1489 return false;
1490 }
1491 }
1492 SkASSERT(structToken->fTokens.size() > 0);
1493 const Definition& child = structToken->fTokens.front();
1494 string structName(child.fContentStart, child.length());
1495 if ("RunBuffer" != structName) {
1496 return false;
1497 }
1498 string tokenName(token.fContentStart, token.length());
1499 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1500 for (auto allow : allowed) {
1501 if (allow == tokenName) {
1502 return true;
1503 }
1504 }
1505 }
1506 return false;
1507}
1508
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001509bool IncludeParser::isOperator(const Definition& token) {
1510 return "operator" == token.fName.substr(0, 8);
1511}
1512
1513void IncludeParser::dumpMethod(const Definition& token, string className) {
1514 this->writeTag("Method");
1515 this->writeSpace();
1516
1517 string name = string(token.fStart ? token.fStart : token.fContentStart,
1518 token.length());
1519 if (this->isOperator(token)) {
1520 string spaceConst(" const");
1521 size_t constPos = name.rfind(spaceConst);
1522 if (name.length() - spaceConst.length() == constPos) {
1523 name = name.substr(0, constPos) + "_const";
1524 }
1525 }
Cary Clark224c7002018-06-27 11:00:21 -04001526 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001527 string inType;
1528 if (this->isConstructor(token, className)) {
1529 inType = "Constructor";
1530 } else if (this->isOperator(token)) {
1531 inType = "Operator";
1532 } else {
1533 inType = "incomplete";
1534 }
1535 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001536 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001537 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001538 this->dumpComment(token);
1539}
1540
1541void IncludeParser::dumpMember(const Definition& token) {
1542 this->writeTag("Member");
1543 this->writeSpace();
1544 this->writeDefinition(token, token.fName, 2);
1545 lf(1);
1546 for (auto child : token.fChildren) {
1547 this->writeDefinition(*child);
1548 }
1549 this->writeEndTag();
1550 lf(2);
1551}
1552
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001553bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001554 string globalFileName;
1555 long int globalTell = 0;
1556 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001557 return false;
1558 }
Cary Clark9174bda2017-09-19 17:39:32 -04001559 for (const auto& member : fIClassMap) {
1560 if (string::npos != member.first.find("::")) {
1561 continue;
1562 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001563 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001564 return false;
1565 }
1566 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001567 if (globalTell) {
1568 fclose(fOut);
1569 }
Cary Clark9174bda2017-09-19 17:39:32 -04001570 return true;
1571}
1572
Ben Wagner63fd7602017-10-09 15:45:33 -04001573 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001574bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001575 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001576 if (globalFileName != fileName) {
1577 fOut = fopen(fileName.c_str(), "wb");
1578 if (!fOut) {
1579 SkDebugf("could not open output file %s\n", fileName.c_str());
1580 return false;
1581 }
1582 } else {
1583 fseek(fOut, *globalTell, SEEK_SET);
1584 this->lf(2);
1585 this->writeBlockSeparator();
1586 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001587 }
1588 string prefixName = skClassName.substr(0, 2);
1589 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1590 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001591 if (globalFileName != fileName) {
1592 this->writeTagNoLF("Topic", topicName);
1593 this->writeEndTag("Alias", topicName + "_Reference");
1594 this->lf(2);
1595 }
Cary Clark8032b982017-07-28 11:04:54 -04001596 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001597 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1598 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001599 this->writeTag(containerType, skClassName);
1600 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001601 auto& tokens = classMap.fTokens;
1602 for (auto& token : tokens) {
1603 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1604 continue;
1605 }
Cary Clark9174bda2017-09-19 17:39:32 -04001606 this->writeDefinition(token);
1607 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001608 }
1609 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001610 bool hasClass = false;
1611 bool hasConst = !fIEnumMap.empty();
1612 bool hasConstructor = false;
1613 bool hasMember = false;
1614 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001615 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001616 for (const auto& oneClass : fIClassMap) {
1617 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1618 continue;
1619 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001620 hasClass = true;
1621 break;
Cary Clark8032b982017-07-28 11:04:54 -04001622 }
Cary Clark224c7002018-06-27 11:00:21 -04001623 for (const auto& oneStruct : fIStructMap) {
1624 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1625 continue;
1626 }
1627 hasStruct = true;
1628 break;
1629 }
Cary Clark8032b982017-07-28 11:04:54 -04001630 for (const auto& token : classMap.fTokens) {
1631 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1632 continue;
1633 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001634 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001635 continue;
1636 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001637 if (token.fUndocumented) {
1638 continue;
1639 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001640 if (this->isConstructor(token, skClassName)) {
1641 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001642 continue;
1643 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001644 if (this->isOperator(token)) {
1645 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001646 continue;
1647 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001648 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001649 continue;
1650 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001651 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001652 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001653 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001654 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001655 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001656 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001657
1658 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001659 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001660 this->writeTag("Populate");
1661 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001662 this->lf(2);
1663 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001664 if (hasConst) {
1665 this->writeTag("Subtopic", "Constant");
1666 this->writeTag("Populate");
1667 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001668 this->lf(2);
1669 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001670 if (hasConstructor) {
1671 this->writeTag("Subtopic", "Constructor");
1672 this->writeTag("Populate");
1673 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001674 this->lf(2);
1675 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001676 if (hasOperator) {
1677 this->writeTag("Subtopic", "Operator");
1678 this->writeTag("Populate");
1679 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001680 this->lf(2);
1681 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001682 if (hasMember) {
1683 this->writeTag("Subtopic", "Member_Function");
1684 this->writeTag("Populate");
1685 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001686 this->lf(2);
1687 }
Cary Clark224c7002018-06-27 11:00:21 -04001688 if (hasStruct) {
1689 this->writeTag("Subtopic", "Struct");
1690 this->writeTag("Populate");
1691 this->writeEndTag();
1692 this->lf(2);
1693 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001694 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001695 this->writeBlockSeparator();
1696 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001697 this->lf(2);
1698 this->writeTag("Example");
1699 this->lfcr();
1700 this->writeString("// incomplete");
1701 this->writeEndTag();
1702 this->lf(2);
1703 this->writeTag("SeeAlso", "incomplete");
1704 this->lf(2);
1705 this->writeEndTag("Enum", oneEnum.first);
1706 this->lf(2);
1707 }
Cary Clark8032b982017-07-28 11:04:54 -04001708 for (auto& oneClass : fIClassMap) {
1709 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1710 continue;
1711 }
1712 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001713 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001714 KeyWord keyword = oneClass.second.fKeyWord;
1715 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1716 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001717 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001718 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001719 this->lf(2);
1720 this->writeTag("Code");
1721 this->writeEndTag("ToDo", "fill this in manually");
1722 this->writeEndTag();
1723 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001724 for (auto& token : oneClass.second.fTokens) {
1725 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1726 continue;
1727 }
Cary Clark9174bda2017-09-19 17:39:32 -04001728 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001729 }
1730 this->lf(2);
1731 this->dumpClassTokens(oneClass.second);
1732 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001733 this->writeEndTag(containerType, innerName);
1734 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001735 }
1736 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001737 this->writeEndTag(containerType, skClassName);
1738 this->lf(2);
1739 this->writeEndTag("Topic", topicName);
1740 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001741 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001742 SkDebugf("wrote %s\n", fileName.c_str());
1743 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001744}
1745
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001746void IncludeParser::dumpTypedef(const Definition& token, string className) {
1747 this->writeTag("Typedef");
1748 this->writeSpace();
1749 this->writeString(token.fName);
1750 this->writeTagTable("Line", "incomplete");
1751 this->lf(2);
1752 this->dumpComment(token);
1753}
1754
Cary Clark61313f32018-10-08 14:57:48 -04001755string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1756 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1757 || KeyWord::kTemplate == iDef.fKeyWord);
1758 TextParser i(&iDef);
1759 fElided = Elided::kYes;
1760 MarkType markType = MarkType::kClass;
1761 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1762 for (auto child : iDef.fChildren) {
1763 if (MarkType::kMethod == child->fMarkType) {
1764 markType = MarkType::kFunction;
1765 break;
1766 }
1767 }
1768 }
1769 return this->writeCodeBlock(i, markType, 0);
1770}
1771
Cary Clarka90ea222018-10-16 10:30:28 -04001772 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1773 string result;
1774 const unordered_map<string, Definition*>* mapPtr = nullptr;
1775 MarkType markType = MarkType::kNone;
1776 if ("Constant" == inContents) {
1777 mapPtr = &fIConstMap;
1778 markType = MarkType::kConst;
1779 } else {
1780 SkASSERT(0); // only Constant supported for now
1781 }
Cary Clark4b076532018-10-29 14:17:22 -04001782 vector<Definition*> consts;
Cary Clarka90ea222018-10-16 10:30:28 -04001783 for (auto entry : *mapPtr) {
1784 if (string::npos == entry.first.find(filterContents)) {
1785 continue;
1786 }
Cary Clark4b076532018-10-29 14:17:22 -04001787 consts.push_back(entry.second);
1788 }
1789 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1790 return def1->fLineCount < def2->fLineCount;
1791 } );
1792 for (auto oneConst : consts) {
1793 result += this->writeCodeBlock(*oneConst, markType);
Cary Clarka90ea222018-10-16 10:30:28 -04001794 }
1795 return result;
1796}
1797
Cary Clarkabaffd82018-11-15 08:25:12 -05001798bool IncludeParser::findCommentAfter(const Definition& includeDef, Definition* markupDef) {
1799 const Definition* parent = includeDef.fParent;
1800 int index = includeDef.fParentIndex;
1801 auto wordIter = parent->fTokens.begin();
1802 std::advance(wordIter, index);
1803 SkASSERT(&*wordIter == &includeDef);
1804 size_t commentLine = 0;
1805 do {
1806 wordIter = std::next(wordIter);
1807 if (parent->fTokens.end() == wordIter) {
1808 break;
1809 }
1810 commentLine = wordIter->fLineCount;
1811 } while (Punctuation::kSemicolon != wordIter->fPunctuation);
1812 wordIter = std::next(wordIter);
1813 if (parent->fTokens.end() != wordIter && Bracket::kSlashSlash == wordIter->fBracket
1814 && wordIter->fLineCount == commentLine) {
1815 return this->parseComment(wordIter->fFileName, wordIter->fContentStart,
1816 wordIter->fContentEnd, wordIter->fLineCount, markupDef, &markupDef->fUndocumented);
1817 }
1818 return true;
1819}
1820
Cary Clark8032b982017-07-28 11:04:54 -04001821bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1822 // add comment preceding class, if any
Cary Clarkabaffd82018-11-15 08:25:12 -05001823 Definition* parent = includeDef.fParent;
Cary Clark8032b982017-07-28 11:04:54 -04001824 int index = includeDef.fParentIndex;
1825 auto wordIter = parent->fTokens.begin();
1826 std::advance(wordIter, index);
1827 SkASSERT(&*wordIter == &includeDef);
1828 while (parent->fTokens.begin() != wordIter) {
1829 auto testIter = std::prev(wordIter);
1830 if (Definition::Type::kWord != testIter->fType
1831 && Definition::Type::kKeyWord != testIter->fType
1832 && (Definition::Type::kBracket != testIter->fType
1833 || Bracket::kAngle != testIter->fBracket)
1834 && (Definition::Type::kPunctuation != testIter->fType
1835 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1836 break;
1837 }
1838 wordIter = testIter;
1839 }
1840 auto commentIter = wordIter;
1841 while (parent->fTokens.begin() != commentIter) {
1842 auto testIter = std::prev(commentIter);
1843 bool isComment = Definition::Type::kBracket == testIter->fType
1844 && (Bracket::kSlashSlash == testIter->fBracket
1845 || Bracket::kSlashStar == testIter->fBracket);
1846 if (!isComment) {
1847 break;
1848 }
1849 commentIter = testIter;
1850 }
1851 while (commentIter != wordIter) {
1852 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
Cary Clarkabaffd82018-11-15 08:25:12 -05001853 commentIter->fContentEnd, commentIter->fLineCount, markupDef,
1854 &markupDef->fUndocumented)) {
Cary Clark8032b982017-07-28 11:04:54 -04001855 return false;
1856 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001857 commentIter->fUndocumented = markupDef->fUndocumented;
Cary Clark8032b982017-07-28 11:04:54 -04001858 commentIter = std::next(commentIter);
1859 }
1860 return true;
1861}
1862
Cary Clark0d225392018-06-07 09:59:07 -04001863Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1864 string typeName) {
1865 typedef Definition* DefinitionPtr;
1866 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1867 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1868 if (mapIter == fMaps.end()) {
1869 return nullptr;
1870 }
1871 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1872 return reportError<DefinitionPtr>("invalid mark type");
1873 }
1874 string name = this->uniqueName(*mapIter->fInclude, typeName);
1875 Definition& markupDef = *(*mapIter->fInclude)[name];
1876 if (markupDef.fStart) {
1877 return reportError<DefinitionPtr>("definition already defined");
1878 }
1879 markupDef.fFileName = fFileName;
1880 markupDef.fStart = includeDef.fStart;
1881 markupDef.fContentStart = includeDef.fStart;
1882 markupDef.fName = name;
1883 markupDef.fContentEnd = includeDef.fContentEnd;
1884 markupDef.fTerminator = includeDef.fTerminator;
1885 markupDef.fParent = fParent;
1886 markupDef.fLineCount = includeDef.fLineCount;
1887 markupDef.fMarkType = markType;
1888 markupDef.fKeyWord = includeDef.fKeyWord;
1889 markupDef.fType = Definition::Type::kMark;
1890 return &markupDef;
1891}
1892
Cary Clarka64e4ee2018-10-18 08:30:34 -04001893Definition* IncludeParser::findMethod(const Definition& bmhDef) {
Cary Clark09d80c02018-10-31 12:14:03 -04001894 auto doubleColon = bmhDef.fName.rfind("::");
1895 if (string::npos == doubleColon) {
1896 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1897 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1898 return iGlobalMethod->second;
1899 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001900 string className = bmhDef.fName.substr(0, doubleColon);
1901 const auto& iClass = fIClassMap.find(className);
Cary Clark77b3f3a2018-11-07 14:59:03 -05001902 if (fIClassMap.end() == iClass) {
1903 return nullptr;
1904 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001905 string methodName = bmhDef.fName.substr(doubleColon + 2);
1906 auto& iTokens = iClass->second.fTokens;
1907 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1908 [methodName](Definition& token) {
Cary Clark09d80c02018-10-31 12:14:03 -04001909 return MarkType::kMethod == token.fMarkType
Cary Clarkabaffd82018-11-15 08:25:12 -05001910 && !token.fUndocumented
Cary Clark09d80c02018-10-31 12:14:03 -04001911 && (methodName == token.fName
1912 || methodName == token.fName + "()"); } );
1913 if (iTokens.end() != iMethod) {
1914 return &*iMethod;
1915 }
1916 size_t subClassPos = className.rfind("::");
1917 if (string::npos != subClassPos) {
1918 className = className.substr(subClassPos + 2);
1919 }
1920 // match may be constructor; compare strings to see if this is so
1921 SkASSERT(string::npos != methodName.find('('));
1922 auto stripper = [](string s) -> string {
1923 bool last = false;
1924 string result;
1925 for (char c : s) {
1926 if (' ' >= c) {
1927 if (!last) {
1928 last = true;
1929 result += ' ';
1930 }
1931 continue;
1932 }
1933 result += c;
1934 last = false;
1935 }
1936 return result;
1937 };
1938 string strippedMethodName = stripper(methodName);
1939 if (strippedMethodName == methodName) {
1940 strippedMethodName = "";
1941 }
1942 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1943 [className, methodName, stripper, strippedMethodName](Definition& token) {
1944 if (MarkType::kMethod != token.fMarkType) {
1945 return false;
1946 }
Cary Clarkabaffd82018-11-15 08:25:12 -05001947 if (token.fUndocumented) {
1948 return false;
1949 }
Cary Clark09d80c02018-10-31 12:14:03 -04001950 TextParser parser(&token);
1951 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1952 if (!match) {
1953 return false;
1954 }
1955 parser.skipTo(match);
1956 parser.skipExact(className.c_str());
1957 if ('(' != parser.peek()) {
1958 return false;
1959 }
1960 parser.skipToBalancedEndBracket('(', ')');
1961 string iMethodName(match, parser.fChar - match);
1962 if (methodName == iMethodName) {
1963 return true;
1964 }
1965 if ("" == strippedMethodName) {
1966 return false;
1967 }
1968 string strippedIName = stripper(iMethodName);
1969 return strippedIName == strippedMethodName;
1970 } );
1971 SkAssertResult(iTokens.end() != cMethod);
1972 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04001973}
1974
Cary Clark224c7002018-06-27 11:00:21 -04001975Definition* IncludeParser::parentBracket(Definition* parent) const {
1976 while (parent && Definition::Type::kBracket != parent->fType) {
1977 parent = parent->fParent;
1978 }
1979 return parent;
1980}
1981
1982Bracket IncludeParser::grandParentBracket() const {
1983 Definition* parent = parentBracket(fParent);
1984 parent = parentBracket(parent ? parent->fParent : nullptr);
1985 return parent ? parent->fBracket : Bracket::kNone;
1986}
1987
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001988bool IncludeParser::inAlignAs() const {
1989 if (fParent->fTokens.size() < 2) {
1990 return false;
1991 }
1992 auto reverseIter = fParent->fTokens.end();
1993 bool checkForBracket = true;
1994 while (fParent->fTokens.begin() != reverseIter) {
1995 std::advance(reverseIter, -1);
1996 if (checkForBracket) {
1997 if (Definition::Type::kBracket != reverseIter->fType) {
1998 return false;
1999 }
2000 if (Bracket::kParen != reverseIter->fBracket) {
2001 return false;
2002 }
2003 checkForBracket = false;
2004 continue;
2005 }
2006 if (Definition::Type::kKeyWord != reverseIter->fType) {
2007 return false;
2008 }
2009 return KeyWord::kAlignAs == reverseIter->fKeyWord;
2010 }
2011 return false;
2012}
2013
Cary Clark61313f32018-10-08 14:57:48 -04002014const Definition* IncludeParser::include(string match) const {
2015 for (auto& entry : fIncludeMap) {
2016 if (string::npos == entry.first.find(match)) {
2017 continue;
2018 }
2019 return &entry.second;
2020 }
2021 SkASSERT(0);
2022 return nullptr;
2023}
2024
Cary Clark137b8742018-05-30 09:21:49 -04002025// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04002026bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
2027 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04002028 // parse class header
2029 auto iter = includeDef->fTokens.begin();
2030 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
2031 // todo : documentation is ignoring this for now
2032 iter = std::next(iter);
2033 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002034 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
2035 if (hasAlignAs) {
2036 iter = std::next(iter);
2037 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
2038 return includeDef->reportError<bool>("expected alignas argument");
2039 }
2040 iter = std::next(iter);
2041 }
Cary Clark8032b982017-07-28 11:04:54 -04002042 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
2043 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04002044 iter = std::next(iter);
2045 if (iter == includeDef->fTokens.end()) {
2046 return true; // forward declaration only
2047 }
Cary Clark8032b982017-07-28 11:04:54 -04002048 do {
2049 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002050 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04002051 }
2052 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
2053 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002054 }
Cary Clark8032b982017-07-28 11:04:54 -04002055 } while (static_cast<void>(iter = std::next(iter)), true);
2056 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04002057 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04002058 }
2059 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
2060 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04002061 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04002062 }
2063 markupDef->fStart = iter->fStart;
2064 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002065 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04002066 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002067 if (markupDef->fUndocumented) {
2068 includeDef->fUndocumented = true;
2069 }
Cary Clark8032b982017-07-28 11:04:54 -04002070// if (1 != includeDef->fChildren.size()) {
2071// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
2072// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002073 auto includeDefIter = includeDef->fChildren.begin();
2074 if (hasAlignAs) {
2075 SkASSERT(includeDef->fChildren.end() != includeDefIter);
2076 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2077 std::advance(includeDefIter, 1);
2078 }
2079 if (includeDef->fChildren.end() != includeDefIter
2080 && Bracket::kAngle == (*includeDefIter)->fBracket) {
2081 std::advance(includeDefIter, 1);
2082 }
2083 includeDef = *includeDefIter;
2084 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04002085 iter = includeDef->fTokens.begin();
2086 // skip until public
2087 int publicIndex = 0;
2088 if (IsStruct::kNo == isStruct) {
2089 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2090 size_t publicLen = strlen(publicName);
2091 while (iter != includeDef->fTokens.end()
2092 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2093 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002094 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002095 iter = std::next(iter);
2096 ++publicIndex;
2097 }
2098 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002099 int keyIndex = publicIndex;
2100 KeyWord currentKey = KeyWord::kPublic;
2101 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2102 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002103 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2104 size_t protectedLen = strlen(protectedName);
2105 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2106 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002107 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002108 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002109 std::advance(childIter, 1);
2110 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002111 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002112 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002113 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002114 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002115 const char* testStart = iter->fStart;
2116 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2117 iter = std::next(iter);
2118 ++keyIndex;
2119 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2120 currentKey = KeyWord::kPublic;
2121 break;
2122 }
2123 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2124 currentKey = KeyWord::kProtected;
2125 break;
2126 }
2127 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2128 currentKey = KeyWord::kPrivate;
2129 break;
2130 }
2131 }
2132 fLastObject = nullptr;
2133 if (KeyWord::kPublic == currentKey) {
2134 if (!this->parseObject(child, markupDef)) {
2135 return false;
2136 }
Cary Clark8032b982017-07-28 11:04:54 -04002137 }
Cary Clark73fa9722017-08-29 17:36:51 -04002138 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002139 childIter = std::next(childIter);
2140 }
Cary Clark137b8742018-05-30 09:21:49 -04002141 while (iter != includeDef->fTokens.end()) {
2142 iter->fPrivate = KeyWord::kPublic != currentKey;
2143 iter = std::next(iter);
2144 }
Cary Clark8032b982017-07-28 11:04:54 -04002145 SkASSERT(fParent->fParent);
2146 fParent = fParent->fParent;
2147 return true;
2148}
2149
Cary Clarkabaffd82018-11-15 08:25:12 -05002150bool IncludeParser::isUndocumentable(string filename, const char* start, const char* end,
2151 int lineCount) {
Cary Clark8032b982017-07-28 11:04:54 -04002152 TextParser parser(filename, start, end, lineCount);
Cary Clarkabaffd82018-11-15 08:25:12 -05002153 const vector<string> skipWords = { "experimental", "deprecated", "private" };
2154 const vector<string> butNot = { "to be deprecated", "may be deprecated" };
2155 const vector<string> alsoNot = { "todo" };
2156 string match = parser.anyWord(skipWords, 0);
2157 if ("" != match) {
2158 if ("" == parser.anyWord(alsoNot, 0)
2159 && ("deprecated" != match || "" == parser.anyWord(butNot, 2))) {
2160 return true;
2161 }
2162 }
2163 return false;
2164}
2165
2166bool IncludeParser::parseComment(string filename, const char* start, const char* end,
2167 int lineCount, Definition* markupDef, bool* undocumentedPtr) {
2168 if (this->isUndocumentable(filename, start, end, lineCount)) {
2169 *undocumentedPtr = true;
2170 }
Cary Clark8032b982017-07-28 11:04:54 -04002171 // parse doxygen if present
Cary Clarkabaffd82018-11-15 08:25:12 -05002172 TextParser parser(filename, start, end, lineCount);
Cary Clark8032b982017-07-28 11:04:54 -04002173 if (parser.startsWith("**")) {
2174 parser.next();
2175 parser.next();
2176 parser.skipWhiteSpace();
2177 if ('\\' == parser.peek()) {
2178 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002179 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2180 if (parser.skipExact("file")) {
2181 if (Definition::Type::kFileType != fParent->fType) {
2182 return reportError<bool>("expected parent is file");
2183 }
2184 string filename = markupDef->fileName();
2185 if (!parser.skipWord(filename.c_str())) {
2186 return reportError<bool>("missing object type");
2187 }
2188 } else if (parser.skipExact("fn")) {
2189 SkASSERT(0); // incomplete
2190 } else {
2191 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2192 return reportError<bool>("missing object type");
2193 }
2194 if (!parser.skipWord(markupDef->fName.c_str()) &&
2195 KeyWord::kEnum != markupDef->fKeyWord) {
2196 return reportError<bool>("missing object name");
2197 }
Cary Clark8032b982017-07-28 11:04:54 -04002198 }
Cary Clark8032b982017-07-28 11:04:54 -04002199 }
2200 }
2201 // remove leading '*' if present
2202 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2203 while (!parser.eof() && parser.skipWhiteSpace()) {
2204 while ('*' == parser.peek()) {
2205 parser.next();
2206 if (parser.eof()) {
2207 break;
2208 }
2209 parser.skipWhiteSpace();
2210 }
2211 if (parser.eof()) {
2212 break;
2213 }
2214 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002215 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002216 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002217 parser.skipToEndBracket('\n');
2218 }
2219 return true;
2220}
2221
Cary Clarkabaffd82018-11-15 08:25:12 -05002222/*
2223 find comment either in front of or after the const def and then extract if the
2224 const is undocumented
2225 */
Cary Clarkd98f78c2018-04-26 08:32:37 -04002226bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002227 if (!markupDef) {
2228 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2229 child->fLineCount, fParent, '\0');
2230 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002231 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002232 globalMarkupChild->fName = globalUniqueName;
2233 if (!this->findComments(*child, globalMarkupChild)) {
2234 return false;
2235 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002236 if (!this->findCommentAfter(*child, globalMarkupChild)) {
2237 return false;
2238 }
2239 if (globalMarkupChild->fUndocumented) {
2240 child->fUndocumented = true;
2241 } else {
2242 fIConstMap[globalUniqueName] = globalMarkupChild;
2243 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002244 return true;
2245 }
2246 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2247 child->fLineCount, markupDef, '\0');
2248 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002249 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002250 markupChild->fTerminator = markupChild->fContentEnd;
2251 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002252 classDef.fConsts[child->fName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002253 if (!this->findComments(*child, markupChild)) {
2254 return false;
2255 }
2256 if (!this->findCommentAfter(*child, markupChild)) {
2257 return false;
2258 }
2259 if (markupChild->fUndocumented) {
2260 child->fUndocumented = true;
2261 } else {
2262 fIConstMap[child->fName] = markupChild;
2263 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002264 return true;
2265}
2266
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002267bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2268 TextParser parser(child);
2269 if (!parser.skipExact("#define")) {
2270 return false;
2271 }
2272 if (!parser.skipSpace()) {
2273 return false;
2274 }
2275 const char* nameStart = parser.fChar;
2276 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2277 if (parser.eof()) {
2278 return true; // do nothing if #define doesn't define anything
2279 }
2280 string nameStr(nameStart, parser.fChar - nameStart);
2281 struct Param {
2282 const char* fStart;
2283 const char* fEnd;
2284 };
2285 vector<Param> params;
2286 if ('(' == parser.peek()) {
2287 parser.next();
2288 if (!parser.skipSpace()) {
2289 return false;
2290 }
2291 do {
2292 const char* paramStart = parser.fChar;
2293 if (!parser.skipExact("...")) {
2294 parser.skipToNonAlphaNum();
2295 }
2296 if (parser.eof()) {
2297 return false;
2298 }
2299 params.push_back({paramStart, parser.fChar});
2300 if (!parser.skipSpace()) {
2301 return false;
2302 }
2303 if (')' == parser.peek()) {
2304 parser.next();
2305 break;
2306 }
2307 if (',' != parser.next()) {
2308 return false;
2309 }
2310 if (!parser.skipSpace()) {
2311 return false;
2312 }
2313 } while (true);
2314 }
2315 if (!parser.skipSpace()) {
2316 return false;
2317 }
2318 if (!markupDef) {
2319 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2320 child->fLineCount, fParent, '\0');
2321 Definition* globalMarkupChild = &fGlobals.back();
2322 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2323 globalMarkupChild->fName = globalUniqueName;
2324 globalMarkupChild->fTerminator = child->fContentEnd;
2325 if (!this->findComments(*child, globalMarkupChild)) {
2326 return false;
2327 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002328 if (!globalMarkupChild->fUndocumented) {
2329 fIDefineMap[globalUniqueName] = globalMarkupChild;
2330 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002331 for (Param param : params) {
2332 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2333 child->fLineCount, globalMarkupChild, '\0');
2334 Definition* paramChild = &globalMarkupChild->fTokens.back();
2335 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2336 paramChild->fTerminator = param.fEnd;
2337 }
2338 return true;
2339 }
2340 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2341 child->fLineCount, markupDef, '\0');
2342 Definition* markupChild = &markupDef->fTokens.back();
2343 markupChild->fName = nameStr;
2344 markupChild->fTerminator = markupChild->fContentEnd;
2345 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2346 if (!this->findComments(*child, markupChild)) {
2347 return false;
2348 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002349 if (markupChild->fUndocumented) {
2350 child->fUndocumented = true;
2351 } else {
2352 classDef.fDefines[nameStr] = markupChild;
2353 fIDefineMap[nameStr] = markupChild;
2354 }
Cary Clark8032b982017-07-28 11:04:54 -04002355 return true;
2356}
2357
2358bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002359 if (!child->fTokens.size()) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002360 return true; // if enum is a forward declaration, do nothing
2361 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002362 bool isEnumClass = false;
2363 Definition* parent = child;
2364 auto token = parent->fTokens.begin();
2365 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2366 isEnumClass = true;
2367 parent = &*token;
2368 token = parent->fTokens.begin();
Cary Clark8032b982017-07-28 11:04:54 -04002369 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002370 SkASSERT(Definition::Type::kWord == token->fType);
2371 string nameStr = string(token->fStart, token->fContentEnd - token->fStart);
Cary Clark2dc84ad2018-01-26 12:56:22 -05002372 Definition* markupChild;
2373 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002374 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2375 child->fLineCount, fParent, '\0');
2376 markupChild = &fGlobals.back();
2377 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2378 markupChild->fName = globalUniqueName;
2379 markupChild->fTerminator = child->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002380 if (!markupChild->fUndocumented) {
2381 fIEnumMap[globalUniqueName] = markupChild;
2382 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002383 } else {
2384 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002385 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002386 markupChild = &markupDef->fTokens.back();
2387 }
Cary Clark8032b982017-07-28 11:04:54 -04002388 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2389 markupChild->fKeyWord = KeyWord::kEnum;
Cary Clarkabaffd82018-11-15 08:25:12 -05002390 if (isEnumClass) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002391 markupChild->fMarkType = MarkType::kEnumClass;
2392 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002393 if (markupDef) {
Cary Clarkabaffd82018-11-15 08:25:12 -05002394 markupChild->fName = markupDef->fName + "::" + nameStr;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002395 }
Cary Clark8032b982017-07-28 11:04:54 -04002396 if (!this->findComments(*child, markupChild)) {
2397 return false;
2398 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002399 if (markupChild->fUndocumented) {
2400 child->fUndocumented = true;
2401 }
2402 if (!this->parseEnumConst(token, parent->fTokens.end(), markupChild)) {
2403 return false;
2404 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002405 for (auto outsideMember : child->fChildren) {
2406 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002407 continue;
2408 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002409 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2410 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002411 continue;
2412 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002413 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2414 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002415 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002416 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002417 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002418 // FIXME: ? add comment as well ?
2419 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002420 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002421 if (markupDef) {
2422 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2423 SkASSERT(classDef.fStart);
2424 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002425 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002426 markupChild->fName = uniqueName;
2427 classDef.fEnums[uniqueName] = markupChild;
Cary Clarkabaffd82018-11-15 08:25:12 -05002428 if (!markupChild->fUndocumented) {
2429 fIEnumMap[fullName] = markupChild;
2430 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002431 }
Cary Clark8032b982017-07-28 11:04:54 -04002432 return true;
2433}
2434
Cary Clarkabaffd82018-11-15 08:25:12 -05002435bool IncludeParser::parseOneEnumConst(list<Definition>& constList,
2436 Definition* markupChild, bool skipWord) {
2437 auto memberIter = constList.begin();
2438 const auto memberIterEnd = constList.end();
2439 if (skipWord) {
2440 SkASSERT(Definition::Type::kWord == memberIter->fType);
2441 memberIter = std::next(memberIter);
2442 SkASSERT(memberIterEnd != memberIter);
2443 }
2444 // token array has parse atoms; child array has comments
2445 bool undocumented = false;
2446 while (memberIterEnd != memberIter) {
2447 while (Bracket::kSlashStar == memberIter->fBracket) {
2448 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2449 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2450 return false;
2451 }
2452 memberIter = std::next(memberIter);
2453 if (memberIterEnd == memberIter) {
2454 return false;
2455 }
2456 }
2457 if (Bracket::kPound == memberIter->fBracket) {
2458 KeyWord keyWord = memberIter->fKeyWord;
2459 bool sawIf = KeyWord::kIfdef == keyWord || KeyWord::kIf == keyWord
2460 || KeyWord::kElif == keyWord;
2461 if (sawIf || KeyWord::kElse == keyWord) {
2462 if (!parseOneEnumConst(memberIter->fTokens, markupChild, sawIf)) {
2463 return false;
2464 }
2465 } else {
2466 SkASSERT(KeyWord::kEndif == keyWord || KeyWord::kError == keyWord);
2467 }
2468 memberIter = std::next(memberIter);
2469 if (memberIterEnd == memberIter) {
2470 break;
2471 }
2472 continue;
2473 }
2474 while (Definition::Type::kWord != memberIter->fType) {
2475 memberIter = std::next(memberIter);
2476 if (memberIterEnd == memberIter) {
2477 return false;
2478 }
2479 }
2480 auto memberStart = memberIter;
2481 Definition* memberEnd = nullptr;
2482 const char* last;
2483 do {
2484 last = memberIter->fContentEnd;
2485 memberIter = std::next(memberIter);
2486 if (memberIterEnd == memberIter) {
2487 break;
2488 }
2489 memberEnd = &*memberIter;
2490 } while (string::npos == string(last, memberIter->fContentStart).find(','));
2491 if (!memberEnd) {
2492 return false;
2493 }
2494 if (memberIterEnd != memberIter && Bracket::kSlashSlash == memberIter->fBracket) {
2495 if (!this->parseComment(memberIter->fFileName, memberIter->fContentStart,
2496 memberIter->fContentEnd, memberIter->fLineCount, markupChild, &undocumented)) {
2497 return false;
2498 }
2499 memberIter = std::next(memberIter);
2500 }
2501 markupChild->fTokens.emplace_back(MarkType::kMember, memberStart->fContentStart,
2502 memberEnd->fContentEnd, memberStart->fLineCount, markupChild, '\0');
2503 Definition* markupMember = &markupChild->fTokens.back();
2504 string name = string(memberStart->fContentStart, memberStart->length());
2505 memberStart->fName = name;
2506 markupMember->fName = name;
2507 memberStart->fUndocumented = undocumented;
2508 markupMember->fUndocumented = undocumented;
2509 memberStart->fMarkType = MarkType::kMember;
2510 undocumented = false;
2511 }
2512 return true;
2513}
2514
2515bool IncludeParser::parseEnumConst(list<Definition>::iterator& tokenIter,
2516 const list<Definition>::iterator& tokenEnd, Definition* markupChild) {
2517 SkASSERT(Definition::Type::kWord == tokenIter->fType); // should be enum name
2518 tokenIter = std::next(tokenIter);
2519 SkASSERT(tokenEnd != tokenIter);
2520 if (Definition::Type::kKeyWord == tokenIter->fType) {
2521 SkASSERT((unsigned) tokenIter->fKeyWord < SK_ARRAY_COUNT(kKeyWords));
2522 SkASSERT(KeyProperty::kNumber == kKeyWords[(int) tokenIter->fKeyWord].fProperty);
2523 tokenIter = std::next(tokenIter);
2524 SkASSERT(tokenEnd != tokenIter);
2525 }
2526 SkASSERT(Punctuation::kLeftBrace == tokenIter->fPunctuation);
2527 tokenIter = std::next(tokenIter);
2528 SkASSERT(tokenEnd != tokenIter);
2529 SkASSERT(Bracket::kBrace == tokenIter->fBracket);
2530 return parseOneEnumConst(tokenIter->fTokens, markupChild, false);
2531}
2532
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002533bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002534 fParent = &fIncludeMap[name];
2535 fParent->fName = name;
2536 fParent->fFileName = fFileName;
2537 fParent->fType = Definition::Type::kFileType;
2538 fParent->fContentStart = fChar;
2539 fParent->fContentEnd = fEnd;
2540 // parse include file into tree
2541 while (fChar < fEnd) {
2542 if (!this->parseChar()) {
2543 return false;
2544 }
2545 }
2546 // parse tree and add named objects to maps
2547 fParent = &fIncludeMap[name];
2548 if (!this->parseObjects(fParent, nullptr)) {
2549 return false;
2550 }
2551 return true;
2552}
2553
2554bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2555 const char* typeStart = child->fChildren[0]->fContentStart;
2556 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002557 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002558 Definition* markupChild = &markupDef->fTokens.back();
2559 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002560 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002561 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2562 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2563 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2564 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002565 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clarkabaffd82018-11-15 08:25:12 -05002566 if (!markupChild->fUndocumented) {
2567 classDef.fMembers[uniqueName] = markupChild;
2568 }
Cary Clark8032b982017-07-28 11:04:54 -04002569 if (child->fParentIndex >= 2) {
2570 auto comment = child->fParent->fTokens.begin();
2571 std::advance(comment, child->fParentIndex - 2);
2572 if (Definition::Type::kBracket == comment->fType
2573 && (Bracket::kSlashStar == comment->fBracket
2574 || Bracket::kSlashSlash == comment->fBracket)) {
2575 TextParser parser(&*comment);
2576 do {
2577 parser.skipToAlpha();
2578 if (parser.eof()) {
2579 break;
2580 }
2581 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002582 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002583 if (Bracket::kSlashStar == comment->fBracket) {
2584 const char* commentEnd = parser.strnstr("*/", end);
2585 if (commentEnd) {
2586 end = commentEnd;
2587 }
2588 }
2589 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002590 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002591 Definition* commentChild = &markupDef->fTokens.back();
2592 markupChild->fChildren.emplace_back(commentChild);
2593 parser.skipTo(end);
2594 } while (!parser.eof());
2595 }
2596 }
2597 return true;
2598}
2599
2600bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2601 auto tokenIter = child->fParent->fTokens.begin();
2602 std::advance(tokenIter, child->fParentIndex);
2603 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002604 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002605 bool addConst = false;
2606 auto operatorCheck = tokenIter;
2607 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2608 operatorCheck = std::prev(tokenIter);
2609 }
2610 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002611 auto closeParen = std::next(tokenIter);
2612 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2613 '(' == closeParen->fContentStart[0]);
2614 nameEnd = closeParen->fContentEnd + 1;
2615 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002616 if (Definition::Type::kKeyWord == closeParen->fType &&
2617 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002618 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002619 }
Cary Clarka560c472017-11-27 10:44:06 -05002620 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002621 }
2622 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002623 if (addConst) {
2624 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002625 }
Cary Clark8032b982017-07-28 11:04:54 -04002626 while (tokenIter != child->fParent->fTokens.begin()) {
2627 auto testIter = std::prev(tokenIter);
2628 switch (testIter->fType) {
2629 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002630 if (testIter == child->fParent->fTokens.begin() &&
2631 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2632 KeyWord::kIfndef == child->fParent->fKeyWord ||
2633 KeyWord::kIf == child->fParent->fKeyWord)) {
2634 std::next(tokenIter);
2635 break;
2636 }
Cary Clark8032b982017-07-28 11:04:54 -04002637 goto keepGoing;
2638 case Definition::Type::kKeyWord: {
2639 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2640 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2641 goto keepGoing;
2642 }
2643 } break;
2644 case Definition::Type::kBracket:
2645 if (Bracket::kAngle == testIter->fBracket) {
2646 goto keepGoing;
2647 }
2648 break;
2649 case Definition::Type::kPunctuation:
2650 if (Punctuation::kSemicolon == testIter->fPunctuation
2651 || Punctuation::kLeftBrace == testIter->fPunctuation
2652 || Punctuation::kColon == testIter->fPunctuation) {
2653 break;
2654 }
2655 keepGoing:
2656 tokenIter = testIter;
2657 continue;
2658 default:
2659 break;
2660 }
2661 break;
2662 }
Cary Clark224c7002018-06-27 11:00:21 -04002663 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002664 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002665 tokenIter->fPrivate = string::npos != nameStr.find("::")
2666 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002667 auto testIter = child->fParent->fTokens.begin();
2668 SkASSERT(child->fParentIndex > 0);
2669 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002670 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2671 0 == tokenIter->fParentIndex) {
2672 tokenIter = std::next(tokenIter);
2673 }
Cary Clark8032b982017-07-28 11:04:54 -04002674 const char* start = tokenIter->fContentStart;
2675 const char* end = tokenIter->fContentEnd;
2676 const char kDebugCodeStr[] = "SkDEBUGCODE";
2677 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2678 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2679 std::advance(testIter, 1);
2680 start = testIter->fContentStart + 1;
2681 end = testIter->fContentEnd - 1;
2682 } else {
2683 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002684 do {
2685 std::advance(testIter, 1);
2686 if (testIter == child->fParent->fTokens.end()) {
2687 break;
2688 }
Cary Clark8032b982017-07-28 11:04:54 -04002689 switch (testIter->fType) {
2690 case Definition::Type::kPunctuation:
2691 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2692 || Punctuation::kLeftBrace == testIter->fPunctuation
2693 || Punctuation::kColon == testIter->fPunctuation);
2694 end = testIter->fStart;
2695 break;
2696 case Definition::Type::kKeyWord: {
2697 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2698 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2699 continue;
2700 }
2701 } break;
2702 default:
2703 continue;
2704 }
2705 break;
Cary Clark61313f32018-10-08 14:57:48 -04002706 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002707 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002708 while (end > start && ' ' >= end[-1]) {
2709 --end;
2710 }
Cary Clark9174bda2017-09-19 17:39:32 -04002711 if (!markupDef) {
2712 auto parentIter = child->fParent->fTokens.begin();
2713 SkASSERT(child->fParentIndex > 0);
2714 std::advance(parentIter, child->fParentIndex - 1);
2715 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002716 TextParser nameParser(methodName);
2717 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002718 return true; // expect this is inline class definition outside of class
2719 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002720 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2721 fParent, '\0');
2722 Definition* globalMarkupChild = &fGlobals.back();
2723 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2724 globalMarkupChild->fName = globalUniqueName;
2725 if (!this->findComments(*child, globalMarkupChild)) {
2726 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002727 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002728 if (globalMarkupChild->fUndocumented) {
2729 child->fUndocumented = true;
2730 } else {
2731 fIFunctionMap[globalUniqueName] = globalMarkupChild;
2732 }
Cary Clarka560c472017-11-27 10:44:06 -05002733 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002734 }
Cary Clark8032b982017-07-28 11:04:54 -04002735 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002736 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002737 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002738 {
2739 auto mapIter = fIClassMap.find(markupDef->fName);
2740 SkASSERT(fIClassMap.end() != mapIter);
2741 IClassDefinition& classDef = mapIter->second;
2742 SkASSERT(classDef.fStart);
2743 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2744 markupChild->fName = uniqueName;
2745 if (!this->findComments(*child, markupChild)) {
2746 return false;
2747 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002748 if (markupChild->fUndocumented) {
2749 tokenIter->fUndocumented = true;
2750 } else {
2751 classDef.fMethods[uniqueName] = markupChild;
2752 }
Cary Clark8032b982017-07-28 11:04:54 -04002753 }
Cary Clark8032b982017-07-28 11:04:54 -04002754 return true;
2755}
2756
Cary Clark8032b982017-07-28 11:04:54 -04002757bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002758 fPriorObject = nullptr;
2759 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002760 if (!this->parseObject(child, markupDef)) {
2761 return false;
2762 }
Cary Clark0d225392018-06-07 09:59:07 -04002763 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002764 }
2765 return true;
2766}
2767
2768bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2769 // set up for error reporting
2770 fLine = fChar = child->fStart;
2771 fEnd = child->fContentEnd;
2772 // todo: put original line number in child as well
2773 switch (child->fType) {
2774 case Definition::Type::kKeyWord:
2775 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002776 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002777 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002778 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002779 }
2780 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002781 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002782 case KeyWord::kConst:
2783 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002784 if (!this->parseConst(child, markupDef)) {
2785 return child->reportError<bool>("failed to parse const or constexpr");
2786 }
2787 break;
Cary Clark8032b982017-07-28 11:04:54 -04002788 case KeyWord::kEnum:
2789 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002790 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002791 }
2792 break;
2793 case KeyWord::kStruct:
2794 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002795 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002796 }
2797 break;
2798 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002799 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002800 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002801 }
2802 break;
2803 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002804 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002805 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002806 }
2807 break;
2808 case KeyWord::kUnion:
2809 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002810 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002811 }
2812 break;
Cary Clark61313f32018-10-08 14:57:48 -04002813 case KeyWord::kUsing:
2814 if (!this->parseUsing()) {
2815 return child->reportError<bool>("failed to parse using");
2816 }
2817 break;
Cary Clark8032b982017-07-28 11:04:54 -04002818 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002819 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002820 }
2821 break;
2822 case Definition::Type::kBracket:
2823 switch (child->fBracket) {
2824 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002825 {
2826 auto tokenIter = child->fParent->fTokens.begin();
2827 std::advance(tokenIter, child->fParentIndex);
2828 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002829 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002830 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002831 break;
2832 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002833 if (Bracket::kPound == child->fParent->fBracket &&
2834 KeyWord::kIf == child->fParent->fKeyWord) {
2835 // TODO: this will skip methods named defined() -- for the
2836 // moment there aren't any
2837 if (previousToken.startsWith("defined")) {
2838 break;
2839 }
2840 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002841 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2842 break;
2843 }
Cary Clark73fa9722017-08-29 17:36:51 -04002844 }
Cary Clark0d225392018-06-07 09:59:07 -04002845 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2846 break;
2847 }
Cary Clark8032b982017-07-28 11:04:54 -04002848 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002849 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002850 }
Cary Clark73fa9722017-08-29 17:36:51 -04002851 break;
Cary Clark8032b982017-07-28 11:04:54 -04002852 case Bracket::kSlashSlash:
2853 case Bracket::kSlashStar:
2854 // comments are picked up by parsing objects first
2855 break;
2856 case Bracket::kPound:
2857 // special-case the #xxx xxx_DEFINED entries
2858 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002859 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002860 case KeyWord::kIfndef:
2861 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002862 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002863 if (!this->parseObjects(child, markupDef)) {
2864 return false;
2865 }
2866 break;
2867 }
2868 goto preproError;
2869 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002870 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002871 break;
2872 }
2873 goto preproError;
2874 case KeyWord::kEndif:
2875 if (child->boilerplateEndIf()) {
2876 break;
2877 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002878 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002879 case KeyWord::kInclude:
2880 // ignored for now
2881 break;
2882 case KeyWord::kElse:
Cary Clarkabaffd82018-11-15 08:25:12 -05002883 if (!this->parseObjects(child, markupDef)) {
2884 return false;
2885 }
2886 break;
Cary Clark8032b982017-07-28 11:04:54 -04002887 case KeyWord::kElif:
2888 // todo: handle these
2889 break;
2890 default:
2891 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002892 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002893 }
2894 break;
2895 case Bracket::kAngle:
2896 // pick up templated function pieces when method is found
2897 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002898 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002899 if (!this->parseObjects(child, markupDef)) {
2900 return false;
2901 }
Cary Clark73fa9722017-08-29 17:36:51 -04002902 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002903 case Bracket::kSquare: {
2904 // check to see if parent is operator, the only case we handle so far
2905 auto prev = child->fParent->fTokens.begin();
2906 std::advance(prev, child->fParentIndex - 1);
2907 if (KeyWord::kOperator != prev->fKeyWord) {
2908 return child->reportError<bool>("expected operator overload");
2909 }
2910 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002911 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002912 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002913 }
2914 break;
2915 case Definition::Type::kWord:
2916 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002917 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002918 }
2919 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002920 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002921 }
2922 break;
2923 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002924 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002925 break;
2926 }
2927 return true;
2928}
2929
Cary Clarkbbfda252018-03-09 15:32:01 -05002930bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2931 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002932}
2933
Cary Clark2f466242017-12-11 16:03:17 -05002934bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2935 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002936 typedefParser.skipExact("typedef");
2937 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002938 string nameStr = typedefParser.typedefName();
2939 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002940 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2941 child->fLineCount, fParent, '\0');
2942 Definition* globalMarkupChild = &fGlobals.back();
2943 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2944 globalMarkupChild->fName = globalUniqueName;
2945 if (!this->findComments(*child, globalMarkupChild)) {
2946 return false;
2947 }
Cary Clarkabaffd82018-11-15 08:25:12 -05002948 if (globalMarkupChild->fUndocumented) {
2949 child->fUndocumented = true;
2950 } else {
2951 fITypedefMap[globalUniqueName] = globalMarkupChild;
2952 }
Cary Clark0d225392018-06-07 09:59:07 -04002953 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002954 return true;
2955 }
2956 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002957 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002958 Definition* markupChild = &markupDef->fTokens.back();
2959 markupChild->fName = nameStr;
2960 markupChild->fTerminator = markupChild->fContentEnd;
2961 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2962 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002963 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04002964 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002965 return true;
2966}
2967
2968bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002969 // incomplete
2970 return true;
2971}
Cary Clark8032b982017-07-28 11:04:54 -04002972
Cary Clark61313f32018-10-08 14:57:48 -04002973bool IncludeParser::parseUsing() {
2974 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002975 return true;
2976}
2977
2978bool IncludeParser::parseChar() {
2979 char test = *fChar;
2980 if ('\\' == fPrev) {
2981 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002982// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002983 fLine = fChar + 1;
2984 }
2985 goto done;
2986 }
2987 switch (test) {
2988 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002989// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002990 fLine = fChar + 1;
2991 if (fInChar) {
2992 return reportError<bool>("malformed char");
2993 }
2994 if (fInString) {
2995 return reportError<bool>("malformed string");
2996 }
2997 if (!this->checkForWord()) {
2998 return false;
2999 }
3000 if (Bracket::kPound == this->topBracket()) {
3001 KeyWord keyWord = fParent->fKeyWord;
3002 if (KeyWord::kNone == keyWord) {
3003 return this->reportError<bool>("unhandled preprocessor directive");
3004 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003005 if (fInDefine) {
3006 SkASSERT(KeyWord::kDefine == keyWord);
3007 fInDefine = false;
3008 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05003009 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04003010 this->popBracket();
3011 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003012 if (fInBrace) {
3013 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
3014 fInBrace = nullptr;
3015 }
Cary Clark8032b982017-07-28 11:04:54 -04003016 } else if (Bracket::kSlashSlash == this->topBracket()) {
3017 this->popBracket();
3018 }
3019 break;
3020 case '*':
3021 if (!fInCharCommentString && '/' == fPrev) {
3022 this->pushBracket(Bracket::kSlashStar);
3023 }
3024 if (!this->checkForWord()) {
3025 return false;
3026 }
3027 if (!fInCharCommentString) {
3028 this->addPunctuation(Punctuation::kAsterisk);
3029 }
3030 break;
3031 case '/':
3032 if ('*' == fPrev) {
3033 if (!fInCharCommentString) {
3034 return reportError<bool>("malformed closing comment");
3035 }
3036 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04003037 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05003038 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04003039 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05003040 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04003041 }
3042 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04003043 }
Cary Clark8032b982017-07-28 11:04:54 -04003044 if (!fInCharCommentString && '/' == fPrev) {
3045 this->pushBracket(Bracket::kSlashSlash);
3046 break;
3047 }
3048 if (!this->checkForWord()) {
3049 return false;
3050 }
3051 break;
3052 case '\'':
3053 if (Bracket::kChar == this->topBracket()) {
3054 this->popBracket();
3055 } else if (!fInComment && !fInString) {
3056 if (fIncludeWord) {
3057 return this->reportError<bool>("word then single-quote");
3058 }
3059 this->pushBracket(Bracket::kChar);
3060 }
3061 break;
3062 case '\"':
3063 if (Bracket::kString == this->topBracket()) {
3064 this->popBracket();
3065 } else if (!fInComment && !fInChar) {
3066 if (fIncludeWord) {
3067 return this->reportError<bool>("word then double-quote");
3068 }
3069 this->pushBracket(Bracket::kString);
3070 }
3071 break;
Cary Clark8032b982017-07-28 11:04:54 -04003072 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003073 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04003074 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
3075 this->pushBracket(Bracket::kDebugCode);
3076 break;
3077 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003078 case ':':
3079 case '[':
3080 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04003081 if (fInCharCommentString) {
3082 break;
3083 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003084 if (fInDefine && fInBrace) {
3085 break;
3086 }
Cary Clark8032b982017-07-28 11:04:54 -04003087 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
3088 break;
3089 }
Cary Clark0d225392018-06-07 09:59:07 -04003090 if (fConstExpr) {
3091 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3092 fConstExpr = nullptr;
3093 }
Cary Clark8032b982017-07-28 11:04:54 -04003094 if (!fInBrace) {
3095 if (!this->checkForWord()) {
3096 return false;
3097 }
3098 if (':' == test && !fInFunction) {
3099 break;
3100 }
3101 if ('{' == test) {
3102 this->addPunctuation(Punctuation::kLeftBrace);
3103 } else if (':' == test) {
3104 this->addPunctuation(Punctuation::kColon);
3105 }
3106 }
3107 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3108 && Bracket::kColon == fInBrace->fBracket) {
3109 Definition* braceParent = fParent->fParent;
3110 braceParent->fChildren.pop_back();
3111 braceParent->fTokens.pop_back();
3112 fParent = braceParent;
3113 fInBrace = nullptr;
3114 }
3115 this->pushBracket(
3116 '(' == test ? Bracket::kParen :
3117 '[' == test ? Bracket::kSquare :
3118 '{' == test ? Bracket::kBrace :
3119 Bracket::kColon);
3120 if (!fInBrace
3121 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3122 && fInFunction) {
3123 fInBrace = fParent;
3124 }
3125 } break;
3126 case '<':
3127 if (fInCharCommentString || fInBrace) {
3128 break;
3129 }
3130 if (!this->checkForWord()) {
3131 return false;
3132 }
3133 if (fInEnum) {
3134 break;
3135 }
3136 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003137 // this angle bracket may be an operator or may be a bracket
3138 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003139 break;
3140 case ')':
3141 case ']':
3142 case '}': {
3143 if (fInCharCommentString) {
3144 break;
3145 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003146 if (fInDefine && fInBrace) {
3147 break;
3148 }
Cary Clark8032b982017-07-28 11:04:54 -04003149 if (!fInBrace) {
3150 if (!this->checkForWord()) {
3151 return false;
3152 }
3153 }
3154 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003155 Bracket match = ')' == test ? Bracket::kParen :
3156 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3157 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003158 this->popBracket();
3159 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003160 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003161 } else {
3162 fInFunction = '}' != test;
3163 }
Cary Clark73fa9722017-08-29 17:36:51 -04003164 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3165 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003166 } else if (Bracket::kAngle == this->topBracket()
3167 && match == this->grandParentBracket()) {
3168 this->popBracket();
3169 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003170 } else {
3171 return reportError<bool>("malformed close bracket");
3172 }
3173 if (popBraceParent) {
3174 Definition* braceParent = fInBrace->fParent;
3175 braceParent->fChildren.pop_back();
3176 braceParent->fTokens.pop_back();
3177 fInBrace = nullptr;
3178 }
3179 } break;
3180 case '>':
3181 if (fInCharCommentString || fInBrace) {
3182 break;
3183 }
3184 if (!this->checkForWord()) {
3185 return false;
3186 }
3187 if (fInEnum) {
3188 break;
3189 }
Cary Clarka560c472017-11-27 10:44:06 -05003190 if (Bracket::kPound == this->topBracket()) {
3191 break;
3192 }
Cary Clark8032b982017-07-28 11:04:54 -04003193 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003194 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003195 this->popBracket();
3196 } else {
3197 return reportError<bool>("malformed close angle bracket");
3198 }
3199 break;
3200 case '#': {
3201 if (fInCharCommentString || fInBrace) {
3202 break;
3203 }
3204 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3205 this->pushBracket(Bracket::kPound);
3206 break;
3207 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003208 case ' ':
3209 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3210 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3211 fInBrace = fParent;
3212 // delimiting brackets are space ... unescaped-linefeed
3213 }
Cary Clark8032b982017-07-28 11:04:54 -04003214 case '&':
3215 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003216 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003217 case '-':
3218 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003219 if (fInCharCommentString || fInBrace) {
3220 break;
3221 }
3222 if (!this->checkForWord()) {
3223 return false;
3224 }
3225 break;
Cary Clark0d225392018-06-07 09:59:07 -04003226 case '=':
3227 if (fInCharCommentString || fInBrace) {
3228 break;
3229 }
3230 if (!this->checkForWord()) {
3231 return false;
3232 }
Cary Clarkd7895502018-07-18 15:10:08 -04003233 if (!fParent->fTokens.size()) {
3234 break;
3235 }
Cary Clark0d225392018-06-07 09:59:07 -04003236 {
3237 const Definition& lastToken = fParent->fTokens.back();
3238 if (lastToken.fType != Definition::Type::kWord) {
3239 break;
3240 }
3241 string name(lastToken.fContentStart, lastToken.length());
3242 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3243 break;
3244 }
3245 // find token on start of line
3246 auto lineIter = fParent->fTokens.end();
3247 do {
Cary Clark80247e52018-07-11 16:18:41 -04003248 if (fParent->fTokens.begin() == lineIter) {
3249 break;
3250 }
Cary Clark0d225392018-06-07 09:59:07 -04003251 --lineIter;
3252 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003253 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003254 ++lineIter;
3255 }
3256 Definition* lineStart = &*lineIter;
3257 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3258 bool sawConst = false;
3259 bool sawStatic = false;
3260 bool sawTemplate = false;
3261 bool sawType = false;
3262 while (&lastToken != &*lineIter) {
3263 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3264 if (sawConst || sawStatic || sawTemplate) {
3265 sawConst = false;
3266 break;
3267 }
3268 if (&lastToken == &*++lineIter) {
3269 break;
3270 }
3271 if (KeyWord::kTypename != lineIter->fKeyWord) {
3272 break;
3273 }
3274 if (&lastToken == &*++lineIter) {
3275 break;
3276 }
3277 if (Definition::Type::kWord != lineIter->fType) {
3278 break;
3279 }
3280 sawTemplate = true;
3281 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3282 if (sawConst || sawStatic) {
3283 sawConst = false;
3284 break;
3285 }
3286 sawStatic = true;
3287 } else if (KeyWord::kConst == lineIter->fKeyWord
3288 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3289 if (sawConst) {
3290 sawConst = false;
3291 break;
3292 }
3293 sawConst = true;
3294 } else {
3295 if (sawType) {
3296 sawType = false;
3297 break;
3298 }
3299 if (Definition::Type::kKeyWord == lineIter->fType
3300 && KeyProperty::kNumber
3301 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3302 sawType = true;
3303 } else if (Definition::Type::kWord == lineIter->fType) {
3304 string typeName(lineIter->fContentStart, lineIter->length());
3305 if ("Sk" != name.substr(0, 2)) {
3306 sawType = true;
3307 }
3308 }
3309 }
3310 ++lineIter;
3311 }
3312 if (sawType && sawConst) {
3313 // if found, name first
3314 lineStart->fName = name;
3315 lineStart->fMarkType = MarkType::kConst;
3316 fParent->fChildren.emplace_back(lineStart);
3317 fConstExpr = lineStart;
3318 }
3319 }
3320 break;
Cary Clark8032b982017-07-28 11:04:54 -04003321 case ';':
3322 if (fInCharCommentString || fInBrace) {
3323 break;
3324 }
3325 if (!this->checkForWord()) {
3326 return false;
3327 }
Cary Clark0d225392018-06-07 09:59:07 -04003328 if (fConstExpr) {
3329 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3330 fConstExpr = nullptr;
3331 }
Cary Clark8032b982017-07-28 11:04:54 -04003332 if (Definition::Type::kKeyWord == fParent->fType
3333 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003334 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3335 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003336 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3337 this->popObject();
3338 }
Cary Clark8032b982017-07-28 11:04:54 -04003339 if (KeyWord::kEnum == fParent->fKeyWord) {
3340 fInEnum = false;
3341 }
Cary Clark61313f32018-10-08 14:57:48 -04003342 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003343 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003344 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3345 this->popObject();
3346 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003347 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003348 } else if (Definition::Type::kBracket == fParent->fType
3349 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3350 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3351 list<Definition>::iterator baseIter = fParent->fTokens.end();
3352 list<Definition>::iterator namedIter = fParent->fTokens.end();
3353 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003354 fParent->fTokens.begin() != tokenIter; ) {
3355 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003356 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003357 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003358 if (namedIter != fParent->fTokens.end()) {
3359 return reportError<bool>("found two named member tokens");
3360 }
3361 namedIter = tokenIter;
3362 }
3363 baseIter = tokenIter;
3364 } else {
3365 break;
3366 }
3367 }
3368 // FIXME: if a member definition spans multiple lines, this won't work
3369 if (namedIter != fParent->fTokens.end()) {
3370 if (baseIter == namedIter) {
3371 return this->reportError<bool>("expected type before named token");
3372 }
3373 Definition* member = &*namedIter;
3374 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003375 if (!member->fTerminator) {
3376 member->fTerminator = member->fContentEnd;
3377 }
Cary Clark8032b982017-07-28 11:04:54 -04003378 fParent->fChildren.push_back(member);
3379 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3380 member->fChildren.push_back(&*nameType);
3381 }
Cary Clark8032b982017-07-28 11:04:54 -04003382 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003383 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003384 } else if (fParent->fChildren.size() > 0) {
3385 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003386 Definition* priorEnum = fPriorEnum;
3387 fPriorEnum = nullptr;
3388 if (!priorEnum) {
3389 while (fParent->fChildren.begin() != lastIter) {
3390 std::advance(lastIter, -1);
3391 priorEnum = *lastIter;
3392 if (Definition::Type::kBracket != priorEnum->fType ||
3393 (Bracket::kSlashSlash != priorEnum->fBracket
3394 && Bracket::kSlashStar != priorEnum->fBracket)) {
3395 break;
3396 }
Cary Clark8032b982017-07-28 11:04:54 -04003397 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003398 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003399 }
3400 if (Definition::Type::kKeyWord == priorEnum->fType
3401 && KeyWord::kEnum == priorEnum->fKeyWord) {
3402 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003403 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003404 while (tokenWalker != fParent->fTokens.end()) {
3405 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003406 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003407 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3408 break;
3409 }
3410 }
3411 while (tokenWalker != fParent->fTokens.end()) {
3412 std::advance(tokenWalker, 1);
3413 const Definition* test = &*tokenWalker;
3414 if (Definition::Type::kBracket != test->fType ||
3415 (Bracket::kSlashSlash != test->fBracket
3416 && Bracket::kSlashStar != test->fBracket)) {
3417 break;
3418 }
3419 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003420 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003421 Definition* start = &*tokenWalker;
3422 bool foundExpected = true;
3423 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3424 const Definition* test = &*tokenWalker;
3425 if (expected != test->fKeyWord) {
3426 foundExpected = false;
3427 break;
3428 }
3429 if (tokenWalker == fParent->fTokens.end()) {
3430 break;
3431 }
3432 std::advance(tokenWalker, 1);
3433 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003434 if (!foundExpected) {
3435 foundExpected = true;
3436 tokenWalker = saveTokenWalker;
3437 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3438 const Definition* test = &*tokenWalker;
3439 if (expected != test->fKeyWord) {
3440 foundExpected = false;
3441 break;
3442 }
3443 if (tokenWalker == fParent->fTokens.end()) {
3444 break;
3445 }
3446 if (KeyWord::kNone != expected) {
3447 std::advance(tokenWalker, 1);
3448 }
3449 }
3450 if (foundExpected) {
3451 auto nameToken = priorEnum->fTokens.begin();
3452 string enumName = string(nameToken->fContentStart,
3453 nameToken->fContentEnd - nameToken->fContentStart);
3454 const Definition* test = &*tokenWalker;
3455 string constType = string(test->fContentStart,
3456 test->fContentEnd - test->fContentStart);
3457 if (enumName != constType) {
3458 foundExpected = false;
3459 } else {
3460 std::advance(tokenWalker, 1);
3461 }
3462 }
3463 }
Cary Clark8032b982017-07-28 11:04:54 -04003464 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3465 const char* nameStart = tokenWalker->fStart;
3466 std::advance(tokenWalker, 1);
3467 if (tokenWalker != fParent->fTokens.end()) {
3468 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003469 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003470 start->fName = string(nameStart, tp.fChar - nameStart);
3471 start->fContentEnd = fChar;
3472 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003473 fPriorEnum = priorEnum;
3474 }
Cary Clark8032b982017-07-28 11:04:54 -04003475 }
3476 }
3477 }
3478 this->addPunctuation(Punctuation::kSemicolon);
3479 fInFunction = false;
3480 break;
3481 case '~':
3482 if (fInEnum) {
3483 break;
3484 }
3485 case '0': case '1': case '2': case '3': case '4':
3486 case '5': case '6': case '7': case '8': case '9':
3487 // TODO: don't want to parse numbers, but do need to track for enum defs
3488 // break;
3489 case 'A': case 'B': case 'C': case 'D': case 'E':
3490 case 'F': case 'G': case 'H': case 'I': case 'J':
3491 case 'K': case 'L': case 'M': case 'N': case 'O':
3492 case 'P': case 'Q': case 'R': case 'S': case 'T':
3493 case 'U': case 'V': case 'W': case 'X': case 'Y':
3494 case 'Z': case '_':
3495 case 'a': case 'b': case 'c': case 'd': case 'e':
3496 case 'f': case 'g': case 'h': case 'i': case 'j':
3497 case 'k': case 'l': case 'm': case 'n': case 'o':
3498 case 'p': case 'q': case 'r': case 's': case 't':
3499 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003500 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003501 if (fInCharCommentString || fInBrace) {
3502 break;
3503 }
3504 if (!fIncludeWord) {
3505 fIncludeWord = fChar;
3506 }
3507 break;
3508 }
3509done:
3510 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003511 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003512 return true;
3513}
3514
3515void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003516 IncludeParser::ValidateKeyWords();
3517}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003518
Cary Clark186d08f2018-04-03 08:43:27 -04003519bool IncludeParser::references(const SkString& file) const {
3520 // if includes weren't passed one at a time, assume all references are valid
3521 if (fIncludeMap.empty()) {
3522 return true;
3523 }
3524 SkASSERT(file.endsWith(".bmh") );
3525 string root(file.c_str(), file.size() - 4);
3526 string kReference("_Reference");
3527 if (string::npos != root.find(kReference)) {
3528 root = root.substr(0, root.length() - kReference.length());
3529 }
3530 if (fIClassMap.end() != fIClassMap.find(root)) {
3531 return true;
3532 }
3533 if (fIStructMap.end() != fIStructMap.find(root)) {
3534 return true;
3535 }
Cary Clark224c7002018-06-27 11:00:21 -04003536 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3537 return true;
3538 }
Cary Clarka90ea222018-10-16 10:30:28 -04003539 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3540 return true;
3541 }
Cary Clark224c7002018-06-27 11:00:21 -04003542 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3543 return true;
3544 }
Cary Clark186d08f2018-04-03 08:43:27 -04003545 return false;
3546}
3547
Cary Clark2dc84ad2018-01-26 12:56:22 -05003548void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3549 if (!sk_isdir(includes)) {
3550 IncludeParser::RemoveOneFile(docs, includes);
3551 } else {
3552 SkOSFile::Iter it(includes, ".h");
3553 for (SkString file; it.next(&file); ) {
3554 SkString p = SkOSPath::Join(includes, file.c_str());
3555 const char* hunk = p.c_str();
3556 if (!SkStrEndsWith(hunk, ".h")) {
3557 continue;
3558 }
3559 IncludeParser::RemoveOneFile(docs, hunk);
3560 }
3561 }
3562}
3563
3564void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3565 const char* lastForward = strrchr(includesFile, '/');
3566 const char* lastBackward = strrchr(includesFile, '\\');
3567 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3568 if (!last) {
3569 last = includesFile;
3570 } else {
3571 last += 1;
3572 }
3573 SkString baseName(last);
3574 SkASSERT(baseName.endsWith(".h"));
3575 baseName.remove(baseName.size() - 2, 2);
3576 baseName.append("_Reference.bmh");
3577 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3578 remove(fullName.c_str());
3579}
Cary Clark224c7002018-06-27 11:00:21 -04003580
3581Bracket IncludeParser::topBracket() const {
3582 Definition* parent = this->parentBracket(fParent);
3583 return parent ? parent->fBracket : Bracket::kNone;
3584}