blob: 523a3e808c0d467b670792860e4e1a774eb08058 [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:
580 case KeyWord::kElse: {
581 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;
587 this->addDefinition(poundDef); // push elif back
588 } break;
589 // 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) {
647 string className = classMapper.first;
648 std::istringstream iss(className);
649 string classStr;
650 string classBase;
651 RootDefinition* root = nullptr;
652 while (std::getline(iss, classStr, ':')) {
653 if (root) {
654 if (!classStr.length()) {
655 continue;
656 }
657 classBase += "::" + classStr;
658 auto finder = root->fBranches.find(classBase);
659 if (root->fBranches.end() != finder) {
660 root = finder->second;
661 } else {
662 SkASSERT(0);
663 }
664 } else {
665 classBase = classStr;
666 auto finder = bmhParser.fClassMap.find(classBase);
667 if (bmhParser.fClassMap.end() != finder) {
668 root = &finder->second;
669 } else {
670 SkASSERT(0);
671 }
672 }
673 }
Cary Clark8032b982017-07-28 11:04:54 -0400674 auto& classMap = classMapper.second;
675 auto& tokens = classMap.fTokens;
676 for (const auto& token : tokens) {
677 if (token.fPrivate) {
678 continue;
679 }
680 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400681 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400682 switch (token.fMarkType) {
683 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400684 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400685 continue;
686 }
Cary Clark8032b982017-07-28 11:04:54 -0400687 if (!def) {
688 string paramName = className + "::";
689 paramName += string(token.fContentStart,
690 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400691 if (string::npos != paramName.find('\n')) {
692 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
693 paramName.end());
694 }
Cary Clarkce101242017-09-01 15:51:02 -0400695 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400696 if (!def && 0 == token.fName.find("operator")) {
697 string operatorName = className + "::";
698 TextParser oper("", token.fStart, token.fContentEnd, 0);
699 const char* start = oper.strnstr("operator", token.fContentEnd);
700 SkASSERT(start);
701 oper.skipTo(start);
702 oper.skipToEndBracket('(');
703 int parens = 0;
704 do {
705 if ('(' == oper.peek()) {
706 ++parens;
707 } else if (')' == oper.peek()) {
708 --parens;
709 }
710 } while (!oper.eof() && oper.next() && parens > 0);
711 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400712 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400713 }
714 }
715 if (!def) {
716 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
717 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400718 const char* tokenEnd = token.methodEnd();
Cary Clark8032b982017-07-28 11:04:54 -0400719 string constructorName = className + "::";
720 constructorName += string(token.fContentStart + skip,
Cary Clarkab5c9af2018-07-12 16:24:53 -0400721 tokenEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400722 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400723 }
724 if (!def && 0 == token.fName.find("SK_")) {
725 string incName = token.fName + "()";
726 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400727 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400728 if (def) {
729 if (def->fName == incName) {
730 def->fVisited = true;
731 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400732 def = root->find(className + "::toString",
733 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400734 if (def) {
735 def->fVisited = true;
736 } else {
737 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500738 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400739 }
740 }
741 break;
742 } else {
743 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500744 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400745 }
746 }
747 }
748 if (!def) {
749 bool allLower = true;
750 for (size_t index = 0; index < token.fName.length(); ++index) {
751 if (!islower(token.fName[index])) {
752 allLower = false;
753 break;
754 }
755 }
756 if (allLower) {
757 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400758 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400759 }
760 }
761 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400762 if (0 == token.fName.find("SkDEBUGCODE")) {
763 break;
764 }
765 }
766 if (!def) {
767 // simple method names inside nested classes have a bug and are missing trailing parens
768 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400769 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400770 }
771 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500772 if (!root->fDeprecated) {
773 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
774 fFailed = true;
775 }
Cary Clark8032b982017-07-28 11:04:54 -0400776 break;
777 }
Cary Clark73fa9722017-08-29 17:36:51 -0400778 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400779 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400780 if (token.fDeprecated && !def->fDeprecated) {
781 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
782 }
Cary Clark8032b982017-07-28 11:04:54 -0400783 } else {
784 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500785 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400786 }
787 } break;
788 case MarkType::kComment:
789 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400790 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400791 case MarkType::kEnum: {
792 if (!def) {
793 // work backwards from first word to deduce #Enum name
794 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
795 SkAssertResult(firstMember.skipName("enum"));
796 SkAssertResult(firstMember.skipToEndBracket('{'));
797 firstMember.next();
798 firstMember.skipWhiteSpace();
799 SkASSERT('k' == firstMember.peek());
800 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400801 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400802 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400803 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400804 const char* lastUnderscore = nullptr;
805 do {
806 if (!firstMember.skipToEndBracket('_')) {
807 break;
808 }
809 if (firstMember.fChar > wordEnd) {
810 break;
811 }
812 lastUnderscore = firstMember.fChar;
813 } while (firstMember.next());
814 if (lastUnderscore) {
815 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400816 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400817 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400818 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400819 }
820 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500821 if (!root->fDeprecated) {
822 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
823 fFailed = true;
824 }
Cary Clark8032b982017-07-28 11:04:54 -0400825 break;
826 }
827 }
828 def->fVisited = true;
Cary Clark61313f32018-10-08 14:57:48 -0400829 bool hasCode = false;
830 bool hasPopulate = true;
Cary Clark8032b982017-07-28 11:04:54 -0400831 for (auto& child : def->fChildren) {
832 if (MarkType::kCode == child->fMarkType) {
Cary Clark61313f32018-10-08 14:57:48 -0400833 hasPopulate = std::any_of(child->fChildren.begin(),
834 child->fChildren.end(), [](auto grandChild){
835 return MarkType::kPopulate == grandChild->fMarkType; });
836 if (!hasPopulate) {
837 def = child;
838 }
839 hasCode = true;
Cary Clark8032b982017-07-28 11:04:54 -0400840 break;
841 }
842 }
Cary Clark61313f32018-10-08 14:57:48 -0400843 if (!hasCode) {
Cary Clark56356312018-02-08 14:45:18 -0500844 if (!root->fDeprecated) {
845 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
846 fFailed = true;
847 }
Cary Clark8032b982017-07-28 11:04:54 -0400848 break;
849 }
Cary Clark61313f32018-10-08 14:57:48 -0400850 if (!hasPopulate) {
851 if (def->crossCheck(token)) {
852 def->fVisited = true;
853 } else {
854 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
855 fFailed = true;
856 }
Cary Clark8032b982017-07-28 11:04:54 -0400857 }
858 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400859 string constName = MarkType::kEnumClass == token.fMarkType ?
860 fullName : className;
861 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400862 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400863 if (!def) {
864 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400865 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400866 }
867 if (!def) {
868 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500869 if (!root->fDeprecated) {
870 SkDebugf("const missing from bmh: %s\n", constName.c_str());
871 fFailed = true;
872 }
Cary Clark8032b982017-07-28 11:04:54 -0400873 }
874 } else {
875 def->fVisited = true;
876 }
877 }
878 } break;
879 case MarkType::kMember:
880 if (def) {
881 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500882 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400883 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500884 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400885 }
886 break;
Cary Clark2f466242017-12-11 16:03:17 -0500887 case MarkType::kTypedef:
888 if (def) {
889 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500890 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500891 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500892 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500893 }
894 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400895 case MarkType::kConst:
896 if (def) {
897 def->fVisited = true;
898 } else if (!root->fDeprecated) {
899 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
900 fFailed = true;
901 }
902 break;
Cary Clark8032b982017-07-28 11:04:54 -0400903 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400904 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400905 break;
906 }
907 }
908 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500909 int crossChecks = 0;
910 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400911 for (auto& classMapper : fIClassMap) {
912 string className = classMapper.first;
913 auto finder = bmhParser.fClassMap.find(className);
914 if (bmhParser.fClassMap.end() == finder) {
915 continue;
916 }
917 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500918 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500919 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400920 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500921 if (crossChecks) {
922 SkDebugf(".");
923 } else {
924 SkDebugf("cross-check");
925 firstCheck = className;
926 }
927 ++crossChecks;
928 }
929 if (crossChecks) {
930 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500931 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500932 }
933 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400934 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400935 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500936 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400937}
938
939IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400940 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400941 string className;
942 const Definition* test = fParent;
943 while (Definition::Type::kFileType != test->fType) {
944 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
945 className = test->fName + "::";
946 break;
947 }
948 test = test->fParent;
949 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400950 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400951 unordered_map<string, IClassDefinition>& map = fIClassMap;
952 IClassDefinition& markupDef = map[className];
953 if (markupDef.fStart) {
954 typedef IClassDefinition* IClassDefPtr;
955 return INHERITED::reportError<IClassDefPtr>("class already defined");
956 }
957 markupDef.fFileName = fFileName;
958 markupDef.fStart = includeDef.fStart;
959 markupDef.fContentStart = includeDef.fStart;
960 markupDef.fName = className;
961 markupDef.fContentEnd = includeDef.fContentEnd;
962 markupDef.fTerminator = includeDef.fTerminator;
963 markupDef.fParent = fParent;
964 markupDef.fLineCount = fLineCount;
965 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
966 MarkType::kStruct : MarkType::kClass;
967 markupDef.fKeyWord = includeDef.fKeyWord;
968 markupDef.fType = Definition::Type::kMark;
969 fParent = &markupDef;
970 return &markupDef;
971}
972
973void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
974 auto& tokens = classDef.fTokens;
975 for (auto& token : tokens) {
976 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
977 continue;
978 }
979 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400980 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400981 }
982 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400983 case MarkType::kConst:
984 this->dumpConst(token, classDef.fName);
985 break;
Cary Clark8032b982017-07-28 11:04:54 -0400986 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500987 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500988 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400989 break;
990 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400991 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400992 break;
993 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400994 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400995 continue;
996 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400997 case MarkType::kTypedef:
998 this->dumpTypedef(token, classDef.fName);
999 break;
Cary Clark8032b982017-07-28 11:04:54 -04001000 default:
1001 SkASSERT(0);
1002 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001003 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -04001004 }
1005}
Cary Clark9174bda2017-09-19 17:39:32 -04001006void IncludeParser::dumpComment(const Definition& token) {
1007 fLineCount = token.fLineCount;
1008 fChar = fLine = token.fContentStart;
1009 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001010 bool sawParam = false;
1011 bool multiline = false;
1012 bool sawReturn = false;
1013 bool sawComment = false;
1014 bool methodHasReturn = false;
1015 vector<string> methodParams;
1016 vector<string> foundParams;
1017 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -04001018 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
1019 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001020 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -04001021 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001022 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -04001023 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -04001024 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -05001025 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -04001026 && !methodParser.strnchr('~', methodParser.fEnd);
1027 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
1028 const char* nextEnd = paren;
1029 do {
1030 string paramName;
1031 methodParser.fChar = nextEnd + 1;
1032 methodParser.skipSpace();
1033 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
1034 continue;
1035 }
1036 methodParams.push_back(paramName);
1037 } while (')' != nextEnd[0]);
1038 }
Cary Clark9174bda2017-09-19 17:39:32 -04001039 for (const auto& child : token.fTokens) {
1040 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1041 break;
1042 }
Cary Clark8032b982017-07-28 11:04:54 -04001043 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001044 if (child.fPrivate) {
1045 break;
1046 }
Cary Clark8032b982017-07-28 11:04:54 -04001047 if ('@' == child.fContentStart[0]) {
1048 TextParser parser(&child);
1049 do {
1050 parser.next();
1051 if (parser.startsWith("param ")) {
1052 parser.skipWord("param");
1053 const char* parmStart = parser.fChar;
1054 parser.skipToSpace();
1055 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
1056 parser.skipWhiteSpace();
1057 do {
1058 size_t nextComma = parmName.find(',');
1059 string piece;
1060 if (string::npos == nextComma) {
1061 piece = parmName;
1062 parmName = "";
1063 } else {
1064 piece = parmName.substr(0, nextComma);
1065 parmName = parmName.substr(nextComma + 1);
1066 }
1067 if (sawParam) {
1068 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001069 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001070 }
Cary Clark9174bda2017-09-19 17:39:32 -04001071 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001072 } else {
1073 if (sawComment) {
1074 this->nl();
1075 }
1076 this->lf(2);
1077 }
1078 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -04001079 this->writeTag("Param", piece);
1080 this->writeSpace(2);
1081 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1082 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001083 sawParam = true;
1084 sawComment = false;
1085 } while (parmName.length());
1086 parser.skipTo(parser.fEnd);
1087 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
1088 parser.skipWord("return");
1089 if ('s' == parser.peek()) {
1090 parser.next();
1091 }
1092 if (sawParam) {
1093 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001094 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001095 }
Cary Clark9174bda2017-09-19 17:39:32 -04001096 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001097 }
1098 this->checkForMissingParams(methodParams, foundParams);
1099 sawParam = false;
1100 sawComment = false;
1101 multiline = false;
1102 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001103 this->writeTag("Return");
1104 this->writeSpace(2);
1105 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1106 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001107 sawReturn = true;
1108 parser.skipTo(parser.fEnd);
1109 } else {
1110 this->reportError("unexpected doxygen directive");
1111 }
1112 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -04001113 } else if (child.length() > 1) {
1114 const char* start = child.fContentStart;
1115 ptrdiff_t length = child.fContentEnd - start;
1116 SkASSERT(length >= 0);
1117 while (length && '/' == start[0]) {
1118 start += 1;
1119 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001120 }
Cary Clark9174bda2017-09-19 17:39:32 -04001121 while (length && '/' == start[length - 1]) {
1122 length -= 1;
1123 if (length && '*' == start[length - 1]) {
1124 length -= 1;
1125 }
1126 }
1127 if (length) {
1128 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
1129 if (sawParam || sawReturn) {
1130 this->indentToColumn(8);
1131 }
1132 this->writeBlock(length, start);
1133 this->writeSpace();
1134 sawComment = true;
1135 if (sawParam || sawReturn) {
1136 multiline = true;
1137 }
Cary Clark8032b982017-07-28 11:04:54 -04001138 }
1139 }
1140 }
1141 }
1142 if (sawParam || sawReturn) {
1143 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001144 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001145 }
Cary Clark9174bda2017-09-19 17:39:32 -04001146 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001147 }
1148 if (!sawReturn) {
1149 if (!sawParam) {
1150 if (sawComment) {
1151 this->nl();
1152 }
1153 this->lf(2);
1154 }
1155 this->checkForMissingParams(methodParams, foundParams);
1156 }
1157 if (methodHasReturn != sawReturn) {
1158 if (!methodHasReturn) {
1159 this->reportError("unexpected doxygen return");
1160 } else {
1161 if (sawComment) {
1162 this->nl();
1163 }
1164 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001165 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -04001166 }
1167 }
1168}
1169
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001170void IncludeParser::dumpCommonTail(const Definition& token) {
1171 this->lf(2);
1172 this->writeTag("Example");
1173 this->lf(1);
1174 this->writeString("// incomplete");
1175 this->lf(1);
1176 this->writeEndTag();
1177 this->lf(2);
1178 this->writeTag("SeeAlso");
1179 this->writeSpace();
1180 this->writeString("incomplete");
1181 this->lf(2);
1182 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1183 this->lf(2);
1184}
1185
Cary Clark224c7002018-06-27 11:00:21 -04001186void IncludeParser::dumpConst(const Definition& token, string className) {
1187 this->writeTag("Const");
1188 this->writeSpace();
1189 this->writeString(token.fName);
1190 this->writeTagTable("Line", "incomplete");
1191 this->lf(2);
1192 this->dumpComment(token);
1193}
1194
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001195void IncludeParser::dumpDefine(const Definition& token) {
1196 this->writeTag("Define", token.fName);
1197 this->lf(2);
1198 this->writeTag("Code");
1199 this->lfAlways(1);
1200 this->writeString("###$");
1201 this->lfAlways(1);
1202 this->indentToColumn(4);
1203 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1204 this->lf(1);
1205 this->indentToColumn(0);
1206 this->writeString("$$$#");
1207
1208 this->writeEndTag();
1209 this->lf(2);
1210 this->dumpComment(token);
1211 for (auto& child : token.fTokens) {
1212 if (MarkType::kComment == child.fMarkType) {
1213 continue;
1214 }
1215 this->writeTag("Param", child.fName);
1216 this->writeSpace();
1217 this->writeString("incomplete");
1218 this->writeSpace();
1219 this->writeString("##");
1220 this->lf(1);
1221 }
1222}
1223
1224void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001225 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -04001226 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001227 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -04001228 this->lfAlways(1);
1229 this->indentToColumn(4);
1230 this->writeString("enum");
1231 this->writeSpace();
1232 if ("_anonymous" != token.fName.substr(0, 10)) {
1233 this->writeString(token.fName);
1234 this->writeSpace();
1235 }
1236 this->writeString("{");
1237 this->lfAlways(1);
1238 for (auto& child : token.fChildren) {
1239 this->indentToColumn(8);
1240 this->writeString(child->fName);
1241 if (child->length()) {
1242 this->writeSpace();
1243 this->writeBlock(child->length(), child->fContentStart);
1244 }
1245 if (',' != fLastChar) {
1246 this->writeString(",");
1247 }
1248 this->lfAlways(1);
1249 }
1250 this->indentToColumn(4);
1251 this->writeString("};");
1252 this->lf(1);
1253 this->writeString("##");
1254 this->lf(2);
1255 this->dumpComment(token);
1256 for (auto& child : token.fChildren) {
Cary Clark61313f32018-10-08 14:57:48 -04001257 // TODO: get comments before or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001258 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001259 this->writeSpace();
1260 this->writeString(child->fName);
1261 TextParser val(child);
1262 if (!val.eof()) {
1263 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
1264 val.next();
1265 val.skipSpace();
1266 const char* valEnd = val.anyOf(",\n");
1267 if (!valEnd) {
1268 valEnd = val.fEnd;
1269 }
1270 this->writeSpace();
1271 this->writeBlock(valEnd - val.fStart, val.fStart);
1272 } else {
1273 this->writeSpace();
1274 this->writeDefinition(*child);
1275 }
1276 }
1277 this->lf(1);
1278 for (auto comment : child->fChildren) {
1279 if (MarkType::kComment == comment->fMarkType) {
1280 TextParser parser(comment);
1281 parser.skipExact("*");
1282 parser.skipExact("*");
1283 while (!parser.eof() && parser.skipWhiteSpace()) {
1284 parser.skipExact("*");
1285 parser.skipWhiteSpace();
1286 const char* start = parser.fChar;
1287 parser.skipToEndBracket('\n');
1288 this->lf(1);
1289 this->writeBlock(parser.fChar - start, start);
1290 }
1291 }
1292 }
1293 this->writeEndTag();
1294 }
1295 this->lf(2);
1296}
1297
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001298bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1299 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1300 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1301 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001302 return true;
1303 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001304 size_t lastBSlash = fFileName.rfind('\\');
1305 size_t lastSlash = fFileName.rfind('/');
1306 size_t lastDotH = fFileName.rfind(".h");
1307 SkASSERT(string::npos != lastDotH);
1308 if (string::npos != lastBSlash && (string::npos == lastSlash
1309 || lastBSlash < lastSlash)) {
1310 lastSlash = lastBSlash;
1311 } else if (string::npos == lastSlash) {
1312 lastSlash = -1;
1313 }
1314 lastSlash += 1;
1315 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1316 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001317 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001318 fOut = fopen(fileName.c_str(), "wb");
1319 if (!fOut) {
1320 SkDebugf("could not open output file %s\n", globalsName.c_str());
1321 return false;
1322 }
1323 string prefixName = globalsName.substr(0, 2);
1324 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1325 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1326 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001327 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001328 this->lf(2);
1329 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001330 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001331 this->writeEndTag();
1332 this->lf(2);
1333 if (!fIDefineMap.empty()) {
1334 this->writeTag("Subtopic", "Define");
1335 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -04001336 this->writeEndTag();
1337 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001338 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001339 if (!fIFunctionMap.empty()) {
1340 this->writeTag("Subtopic", "Function");
1341 this->writeTag("Populate");
1342 this->writeEndTag();
1343 this->lf(2);
1344 }
1345 if (!fIEnumMap.empty()) {
1346 this->writeTag("Subtopic", "Enum");
1347 this->writeTag("Populate");
1348 this->writeEndTag();
1349 this->lf(2);
1350 }
1351 if (!fITemplateMap.empty()) {
1352 this->writeTag("Subtopic", "Template");
1353 this->writeTag("Populate");
1354 this->writeEndTag();
1355 this->lf(2);
1356 }
1357 if (!fITypedefMap.empty()) {
1358 this->writeTag("Subtopic", "Typedef");
1359 this->writeTag("Populate");
1360 this->writeEndTag();
1361 this->lf(2);
1362 }
1363 if (!fIUnionMap.empty()) {
1364 this->writeTag("Subtopic", "Union");
1365 this->writeTag("Populate");
1366 this->writeEndTag();
1367 this->lf(2);
1368 }
1369 std::map<int, Definition*> sortedDefs;
1370 for (const auto& entry : fIDefineMap) {
1371 sortedDefs[entry.second->fLineCount] = entry.second;
1372 }
1373 for (const auto& entry : fIFunctionMap) {
1374 sortedDefs[entry.second->fLineCount] = entry.second;
1375 }
1376 for (const auto& entry : fIEnumMap) {
1377 sortedDefs[entry.second->fLineCount] = entry.second;
1378 }
1379 for (const auto& entry : fITemplateMap) {
1380 sortedDefs[entry.second->fLineCount] = entry.second;
1381 }
1382 for (const auto& entry : fITypedefMap) {
1383 sortedDefs[entry.second->fLineCount] = entry.second;
1384 }
1385 for (const auto& entry : fIUnionMap) {
1386 sortedDefs[entry.second->fLineCount] = entry.second;
1387 }
1388 for (const auto& entry : sortedDefs) {
1389 const Definition* def = entry.second;
1390 this->writeBlockSeparator();
1391 switch (def->fMarkType) {
1392 case MarkType::kDefine:
1393 this->dumpDefine(*def);
1394 break;
1395 case MarkType::kMethod:
1396 this->dumpMethod(*def, globalsName);
1397 break;
1398 case MarkType::kEnum:
1399 case MarkType::kEnumClass:
1400 this->dumpEnum(*def, globalsName);
1401 break;
1402 case MarkType::kTemplate:
1403 SkASSERT(0); // incomplete
1404 break;
1405 case MarkType::kTypedef: {
1406 this->writeTag("Typedef");
1407 this->writeSpace();
1408 TextParser parser(def);
1409 if (!parser.skipExact("typedef")) {
1410 return false;
1411 }
1412 if (!parser.skipSpace()) {
1413 return false;
1414 }
1415 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1416 this->lf(2);
1417 this->dumpComment(*def);
1418 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1419 this->lf(2);
1420 } continue;
1421 case MarkType::kUnion:
1422 SkASSERT(0); // incomplete
1423 break;
1424 default:
1425 SkASSERT(0);
1426 }
1427 this->dumpCommonTail(*def);
1428 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001429 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001430 this->writeEndTag("Topic", topicName);
1431 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001432// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001433 SkDebugf("wrote %s\n", fileName.c_str());
1434 return true;
1435}
1436
1437bool IncludeParser::isClone(const Definition& token) {
1438 string name = token.fName;
1439 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1440}
1441
1442bool IncludeParser::isConstructor(const Definition& token, string className) {
1443 string name = token.fName;
1444 return 0 == name.find(className) || '~' == name[0];
1445}
1446
1447bool IncludeParser::isInternalName(const Definition& token) {
1448 string name = token.fName;
1449 // exception for this SkCanvas function .. for now
1450 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1451 return false;
1452 }
1453 return name.substr(0, 7) == "android"
1454 || 0 == token.fName.find("internal_")
1455 || 0 == token.fName.find("Internal_")
1456 || 0 == token.fName.find("legacy_")
1457 || 0 == token.fName.find("temporary_")
1458 || 0 == token.fName.find("private_");
1459}
1460
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001461bool IncludeParser::isMember(const Definition& token) const {
1462 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1463 return true;
1464 }
1465 if (!islower(token.fStart[0])) {
1466 return false;
1467 }
1468 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1469 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1470 const Definition* structToken = token.fParent;
1471 if (!structToken) {
1472 return false;
1473 }
1474 if (KeyWord::kStruct != structToken->fKeyWord) {
1475 structToken = token.fParent->fParent;
1476 if (!structToken) {
1477 return false;
1478 }
1479 if (KeyWord::kStruct != structToken->fKeyWord) {
1480 return false;
1481 }
1482 }
1483 SkASSERT(structToken->fTokens.size() > 0);
1484 const Definition& child = structToken->fTokens.front();
1485 string structName(child.fContentStart, child.length());
1486 if ("RunBuffer" != structName) {
1487 return false;
1488 }
1489 string tokenName(token.fContentStart, token.length());
1490 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1491 for (auto allow : allowed) {
1492 if (allow == tokenName) {
1493 return true;
1494 }
1495 }
1496 }
1497 return false;
1498}
1499
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001500bool IncludeParser::isOperator(const Definition& token) {
1501 return "operator" == token.fName.substr(0, 8);
1502}
1503
1504void IncludeParser::dumpMethod(const Definition& token, string className) {
1505 this->writeTag("Method");
1506 this->writeSpace();
1507
1508 string name = string(token.fStart ? token.fStart : token.fContentStart,
1509 token.length());
1510 if (this->isOperator(token)) {
1511 string spaceConst(" const");
1512 size_t constPos = name.rfind(spaceConst);
1513 if (name.length() - spaceConst.length() == constPos) {
1514 name = name.substr(0, constPos) + "_const";
1515 }
1516 }
Cary Clark224c7002018-06-27 11:00:21 -04001517 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001518 string inType;
1519 if (this->isConstructor(token, className)) {
1520 inType = "Constructor";
1521 } else if (this->isOperator(token)) {
1522 inType = "Operator";
1523 } else {
1524 inType = "incomplete";
1525 }
1526 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001527 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001528 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001529 this->dumpComment(token);
1530}
1531
1532void IncludeParser::dumpMember(const Definition& token) {
1533 this->writeTag("Member");
1534 this->writeSpace();
1535 this->writeDefinition(token, token.fName, 2);
1536 lf(1);
1537 for (auto child : token.fChildren) {
1538 this->writeDefinition(*child);
1539 }
1540 this->writeEndTag();
1541 lf(2);
1542}
1543
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001544bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001545 string globalFileName;
1546 long int globalTell = 0;
1547 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001548 return false;
1549 }
Cary Clark9174bda2017-09-19 17:39:32 -04001550 for (const auto& member : fIClassMap) {
1551 if (string::npos != member.first.find("::")) {
1552 continue;
1553 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001554 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001555 return false;
1556 }
1557 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001558 if (globalTell) {
1559 fclose(fOut);
1560 }
Cary Clark9174bda2017-09-19 17:39:32 -04001561 return true;
1562}
1563
Ben Wagner63fd7602017-10-09 15:45:33 -04001564 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001565bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001566 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001567 if (globalFileName != fileName) {
1568 fOut = fopen(fileName.c_str(), "wb");
1569 if (!fOut) {
1570 SkDebugf("could not open output file %s\n", fileName.c_str());
1571 return false;
1572 }
1573 } else {
1574 fseek(fOut, *globalTell, SEEK_SET);
1575 this->lf(2);
1576 this->writeBlockSeparator();
1577 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001578 }
1579 string prefixName = skClassName.substr(0, 2);
1580 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1581 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001582 if (globalFileName != fileName) {
1583 this->writeTagNoLF("Topic", topicName);
1584 this->writeEndTag("Alias", topicName + "_Reference");
1585 this->lf(2);
1586 }
Cary Clark8032b982017-07-28 11:04:54 -04001587 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001588 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1589 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001590 this->writeTag(containerType, skClassName);
1591 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001592 auto& tokens = classMap.fTokens;
1593 for (auto& token : tokens) {
1594 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1595 continue;
1596 }
Cary Clark9174bda2017-09-19 17:39:32 -04001597 this->writeDefinition(token);
1598 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001599 }
1600 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001601 bool hasClass = false;
1602 bool hasConst = !fIEnumMap.empty();
1603 bool hasConstructor = false;
1604 bool hasMember = false;
1605 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001606 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001607 for (const auto& oneClass : fIClassMap) {
1608 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1609 continue;
1610 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001611 hasClass = true;
1612 break;
Cary Clark8032b982017-07-28 11:04:54 -04001613 }
Cary Clark224c7002018-06-27 11:00:21 -04001614 for (const auto& oneStruct : fIStructMap) {
1615 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1616 continue;
1617 }
1618 hasStruct = true;
1619 break;
1620 }
Cary Clark8032b982017-07-28 11:04:54 -04001621 for (const auto& token : classMap.fTokens) {
1622 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1623 continue;
1624 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001625 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001626 continue;
1627 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001628 if (this->isConstructor(token, skClassName)) {
1629 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001630 continue;
1631 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001632 if (this->isOperator(token)) {
1633 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001634 continue;
1635 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001636 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001637 continue;
1638 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001639 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001640 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001641 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001642 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001643 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001644 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001645
1646 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001647 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001648 this->writeTag("Populate");
1649 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001650 this->lf(2);
1651 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001652 if (hasConst) {
1653 this->writeTag("Subtopic", "Constant");
1654 this->writeTag("Populate");
1655 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001656 this->lf(2);
1657 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001658 if (hasConstructor) {
1659 this->writeTag("Subtopic", "Constructor");
1660 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 (hasOperator) {
1665 this->writeTag("Subtopic", "Operator");
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 (hasMember) {
1671 this->writeTag("Subtopic", "Member_Function");
1672 this->writeTag("Populate");
1673 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001674 this->lf(2);
1675 }
Cary Clark224c7002018-06-27 11:00:21 -04001676 if (hasStruct) {
1677 this->writeTag("Subtopic", "Struct");
1678 this->writeTag("Populate");
1679 this->writeEndTag();
1680 this->lf(2);
1681 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001682 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001683 this->writeBlockSeparator();
1684 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001685 this->lf(2);
1686 this->writeTag("Example");
1687 this->lfcr();
1688 this->writeString("// incomplete");
1689 this->writeEndTag();
1690 this->lf(2);
1691 this->writeTag("SeeAlso", "incomplete");
1692 this->lf(2);
1693 this->writeEndTag("Enum", oneEnum.first);
1694 this->lf(2);
1695 }
Cary Clark8032b982017-07-28 11:04:54 -04001696 for (auto& oneClass : fIClassMap) {
1697 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1698 continue;
1699 }
1700 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001701 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001702 KeyWord keyword = oneClass.second.fKeyWord;
1703 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1704 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001705 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001706 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001707 this->lf(2);
1708 this->writeTag("Code");
1709 this->writeEndTag("ToDo", "fill this in manually");
1710 this->writeEndTag();
1711 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001712 for (auto& token : oneClass.second.fTokens) {
1713 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1714 continue;
1715 }
Cary Clark9174bda2017-09-19 17:39:32 -04001716 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001717 }
1718 this->lf(2);
1719 this->dumpClassTokens(oneClass.second);
1720 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001721 this->writeEndTag(containerType, innerName);
1722 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001723 }
1724 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001725 this->writeEndTag(containerType, skClassName);
1726 this->lf(2);
1727 this->writeEndTag("Topic", topicName);
1728 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001729 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001730 SkDebugf("wrote %s\n", fileName.c_str());
1731 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001732}
1733
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001734void IncludeParser::dumpTypedef(const Definition& token, string className) {
1735 this->writeTag("Typedef");
1736 this->writeSpace();
1737 this->writeString(token.fName);
1738 this->writeTagTable("Line", "incomplete");
1739 this->lf(2);
1740 this->dumpComment(token);
1741}
1742
Cary Clark61313f32018-10-08 14:57:48 -04001743string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1744 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1745 || KeyWord::kTemplate == iDef.fKeyWord);
1746 TextParser i(&iDef);
1747 fElided = Elided::kYes;
1748 MarkType markType = MarkType::kClass;
1749 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1750 for (auto child : iDef.fChildren) {
1751 if (MarkType::kMethod == child->fMarkType) {
1752 markType = MarkType::kFunction;
1753 break;
1754 }
1755 }
1756 }
1757 return this->writeCodeBlock(i, markType, 0);
1758}
1759
Cary Clarka90ea222018-10-16 10:30:28 -04001760 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1761 string result;
1762 const unordered_map<string, Definition*>* mapPtr = nullptr;
1763 MarkType markType = MarkType::kNone;
1764 if ("Constant" == inContents) {
1765 mapPtr = &fIConstMap;
1766 markType = MarkType::kConst;
1767 } else {
1768 SkASSERT(0); // only Constant supported for now
1769 }
Cary Clark4b076532018-10-29 14:17:22 -04001770 vector<Definition*> consts;
Cary Clarka90ea222018-10-16 10:30:28 -04001771 for (auto entry : *mapPtr) {
1772 if (string::npos == entry.first.find(filterContents)) {
1773 continue;
1774 }
Cary Clark4b076532018-10-29 14:17:22 -04001775 consts.push_back(entry.second);
1776 }
1777 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1778 return def1->fLineCount < def2->fLineCount;
1779 } );
1780 for (auto oneConst : consts) {
1781 result += this->writeCodeBlock(*oneConst, markType);
Cary Clarka90ea222018-10-16 10:30:28 -04001782 }
1783 return result;
1784}
1785
Cary Clark8032b982017-07-28 11:04:54 -04001786bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1787 // add comment preceding class, if any
1788 const Definition* parent = includeDef.fParent;
1789 int index = includeDef.fParentIndex;
1790 auto wordIter = parent->fTokens.begin();
1791 std::advance(wordIter, index);
1792 SkASSERT(&*wordIter == &includeDef);
1793 while (parent->fTokens.begin() != wordIter) {
1794 auto testIter = std::prev(wordIter);
1795 if (Definition::Type::kWord != testIter->fType
1796 && Definition::Type::kKeyWord != testIter->fType
1797 && (Definition::Type::kBracket != testIter->fType
1798 || Bracket::kAngle != testIter->fBracket)
1799 && (Definition::Type::kPunctuation != testIter->fType
1800 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1801 break;
1802 }
1803 wordIter = testIter;
1804 }
1805 auto commentIter = wordIter;
1806 while (parent->fTokens.begin() != commentIter) {
1807 auto testIter = std::prev(commentIter);
1808 bool isComment = Definition::Type::kBracket == testIter->fType
1809 && (Bracket::kSlashSlash == testIter->fBracket
1810 || Bracket::kSlashStar == testIter->fBracket);
1811 if (!isComment) {
1812 break;
1813 }
1814 commentIter = testIter;
1815 }
1816 while (commentIter != wordIter) {
1817 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1818 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1819 return false;
1820 }
1821 commentIter = std::next(commentIter);
1822 }
1823 return true;
1824}
1825
Cary Clark0d225392018-06-07 09:59:07 -04001826Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1827 string typeName) {
1828 typedef Definition* DefinitionPtr;
1829 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1830 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1831 if (mapIter == fMaps.end()) {
1832 return nullptr;
1833 }
1834 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1835 return reportError<DefinitionPtr>("invalid mark type");
1836 }
1837 string name = this->uniqueName(*mapIter->fInclude, typeName);
1838 Definition& markupDef = *(*mapIter->fInclude)[name];
1839 if (markupDef.fStart) {
1840 return reportError<DefinitionPtr>("definition already defined");
1841 }
1842 markupDef.fFileName = fFileName;
1843 markupDef.fStart = includeDef.fStart;
1844 markupDef.fContentStart = includeDef.fStart;
1845 markupDef.fName = name;
1846 markupDef.fContentEnd = includeDef.fContentEnd;
1847 markupDef.fTerminator = includeDef.fTerminator;
1848 markupDef.fParent = fParent;
1849 markupDef.fLineCount = includeDef.fLineCount;
1850 markupDef.fMarkType = markType;
1851 markupDef.fKeyWord = includeDef.fKeyWord;
1852 markupDef.fType = Definition::Type::kMark;
1853 return &markupDef;
1854}
1855
Cary Clarka64e4ee2018-10-18 08:30:34 -04001856Definition* IncludeParser::findMethod(const Definition& bmhDef) {
Cary Clark77b3f3a2018-11-07 14:59:03 -05001857 if (std::any_of(bmhDef.fChildren.begin(), bmhDef.fChildren.end(), [](Definition* def) {
1858 return MarkType::kDeprecated == def->fMarkType; } )) {
1859 return nullptr;
1860 }
Cary Clark09d80c02018-10-31 12:14:03 -04001861 auto doubleColon = bmhDef.fName.rfind("::");
1862 if (string::npos == doubleColon) {
1863 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1864 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1865 return iGlobalMethod->second;
1866 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001867 string className = bmhDef.fName.substr(0, doubleColon);
1868 const auto& iClass = fIClassMap.find(className);
Cary Clark77b3f3a2018-11-07 14:59:03 -05001869 if (fIClassMap.end() == iClass) {
1870 return nullptr;
1871 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001872 string methodName = bmhDef.fName.substr(doubleColon + 2);
1873 auto& iTokens = iClass->second.fTokens;
1874 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1875 [methodName](Definition& token) {
Cary Clark09d80c02018-10-31 12:14:03 -04001876 return MarkType::kMethod == token.fMarkType
1877 && (methodName == token.fName
1878 || methodName == token.fName + "()"); } );
1879 if (iTokens.end() != iMethod) {
1880 return &*iMethod;
1881 }
1882 size_t subClassPos = className.rfind("::");
1883 if (string::npos != subClassPos) {
1884 className = className.substr(subClassPos + 2);
1885 }
1886 // match may be constructor; compare strings to see if this is so
1887 SkASSERT(string::npos != methodName.find('('));
1888 auto stripper = [](string s) -> string {
1889 bool last = false;
1890 string result;
1891 for (char c : s) {
1892 if (' ' >= c) {
1893 if (!last) {
1894 last = true;
1895 result += ' ';
1896 }
1897 continue;
1898 }
1899 result += c;
1900 last = false;
1901 }
1902 return result;
1903 };
1904 string strippedMethodName = stripper(methodName);
1905 if (strippedMethodName == methodName) {
1906 strippedMethodName = "";
1907 }
1908 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1909 [className, methodName, stripper, strippedMethodName](Definition& token) {
1910 if (MarkType::kMethod != token.fMarkType) {
1911 return false;
1912 }
1913 TextParser parser(&token);
1914 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1915 if (!match) {
1916 return false;
1917 }
1918 parser.skipTo(match);
1919 parser.skipExact(className.c_str());
1920 if ('(' != parser.peek()) {
1921 return false;
1922 }
1923 parser.skipToBalancedEndBracket('(', ')');
1924 string iMethodName(match, parser.fChar - match);
1925 if (methodName == iMethodName) {
1926 return true;
1927 }
1928 if ("" == strippedMethodName) {
1929 return false;
1930 }
1931 string strippedIName = stripper(iMethodName);
1932 return strippedIName == strippedMethodName;
1933 } );
1934 SkAssertResult(iTokens.end() != cMethod);
1935 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04001936}
1937
Cary Clark224c7002018-06-27 11:00:21 -04001938Definition* IncludeParser::parentBracket(Definition* parent) const {
1939 while (parent && Definition::Type::kBracket != parent->fType) {
1940 parent = parent->fParent;
1941 }
1942 return parent;
1943}
1944
1945Bracket IncludeParser::grandParentBracket() const {
1946 Definition* parent = parentBracket(fParent);
1947 parent = parentBracket(parent ? parent->fParent : nullptr);
1948 return parent ? parent->fBracket : Bracket::kNone;
1949}
1950
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001951bool IncludeParser::inAlignAs() const {
1952 if (fParent->fTokens.size() < 2) {
1953 return false;
1954 }
1955 auto reverseIter = fParent->fTokens.end();
1956 bool checkForBracket = true;
1957 while (fParent->fTokens.begin() != reverseIter) {
1958 std::advance(reverseIter, -1);
1959 if (checkForBracket) {
1960 if (Definition::Type::kBracket != reverseIter->fType) {
1961 return false;
1962 }
1963 if (Bracket::kParen != reverseIter->fBracket) {
1964 return false;
1965 }
1966 checkForBracket = false;
1967 continue;
1968 }
1969 if (Definition::Type::kKeyWord != reverseIter->fType) {
1970 return false;
1971 }
1972 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1973 }
1974 return false;
1975}
1976
Cary Clark61313f32018-10-08 14:57:48 -04001977const Definition* IncludeParser::include(string match) const {
1978 for (auto& entry : fIncludeMap) {
1979 if (string::npos == entry.first.find(match)) {
1980 continue;
1981 }
1982 return &entry.second;
1983 }
1984 SkASSERT(0);
1985 return nullptr;
1986}
1987
Cary Clark137b8742018-05-30 09:21:49 -04001988// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001989bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1990 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001991 // parse class header
1992 auto iter = includeDef->fTokens.begin();
1993 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1994 // todo : documentation is ignoring this for now
1995 iter = std::next(iter);
1996 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001997 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1998 if (hasAlignAs) {
1999 iter = std::next(iter);
2000 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
2001 return includeDef->reportError<bool>("expected alignas argument");
2002 }
2003 iter = std::next(iter);
2004 }
Cary Clark8032b982017-07-28 11:04:54 -04002005 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
2006 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04002007 iter = std::next(iter);
2008 if (iter == includeDef->fTokens.end()) {
2009 return true; // forward declaration only
2010 }
Cary Clark8032b982017-07-28 11:04:54 -04002011 do {
2012 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002013 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04002014 }
2015 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
2016 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002017 }
Cary Clark8032b982017-07-28 11:04:54 -04002018 } while (static_cast<void>(iter = std::next(iter)), true);
2019 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04002020 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04002021 }
2022 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
2023 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04002024 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04002025 }
2026 markupDef->fStart = iter->fStart;
2027 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002028 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04002029 }
2030// if (1 != includeDef->fChildren.size()) {
2031// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
2032// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002033 auto includeDefIter = includeDef->fChildren.begin();
2034 if (hasAlignAs) {
2035 SkASSERT(includeDef->fChildren.end() != includeDefIter);
2036 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2037 std::advance(includeDefIter, 1);
2038 }
2039 if (includeDef->fChildren.end() != includeDefIter
2040 && Bracket::kAngle == (*includeDefIter)->fBracket) {
2041 std::advance(includeDefIter, 1);
2042 }
2043 includeDef = *includeDefIter;
2044 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04002045 iter = includeDef->fTokens.begin();
2046 // skip until public
2047 int publicIndex = 0;
2048 if (IsStruct::kNo == isStruct) {
2049 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2050 size_t publicLen = strlen(publicName);
2051 while (iter != includeDef->fTokens.end()
2052 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2053 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002054 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002055 iter = std::next(iter);
2056 ++publicIndex;
2057 }
2058 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002059 int keyIndex = publicIndex;
2060 KeyWord currentKey = KeyWord::kPublic;
2061 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2062 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002063 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2064 size_t protectedLen = strlen(protectedName);
2065 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2066 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002067 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002068 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002069 std::advance(childIter, 1);
2070 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002071 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002072 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002073 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002074 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002075 const char* testStart = iter->fStart;
2076 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2077 iter = std::next(iter);
2078 ++keyIndex;
2079 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2080 currentKey = KeyWord::kPublic;
2081 break;
2082 }
2083 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2084 currentKey = KeyWord::kProtected;
2085 break;
2086 }
2087 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2088 currentKey = KeyWord::kPrivate;
2089 break;
2090 }
2091 }
2092 fLastObject = nullptr;
2093 if (KeyWord::kPublic == currentKey) {
2094 if (!this->parseObject(child, markupDef)) {
2095 return false;
2096 }
Cary Clark8032b982017-07-28 11:04:54 -04002097 }
Cary Clark73fa9722017-08-29 17:36:51 -04002098 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002099 childIter = std::next(childIter);
2100 }
Cary Clark137b8742018-05-30 09:21:49 -04002101 while (iter != includeDef->fTokens.end()) {
2102 iter->fPrivate = KeyWord::kPublic != currentKey;
2103 iter = std::next(iter);
2104 }
Cary Clark8032b982017-07-28 11:04:54 -04002105 SkASSERT(fParent->fParent);
2106 fParent = fParent->fParent;
2107 return true;
2108}
2109
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002110bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04002111 int lineCount, Definition* markupDef) {
2112 TextParser parser(filename, start, end, lineCount);
2113 // parse doxygen if present
2114 if (parser.startsWith("**")) {
2115 parser.next();
2116 parser.next();
2117 parser.skipWhiteSpace();
2118 if ('\\' == parser.peek()) {
2119 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002120 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2121 if (parser.skipExact("file")) {
2122 if (Definition::Type::kFileType != fParent->fType) {
2123 return reportError<bool>("expected parent is file");
2124 }
2125 string filename = markupDef->fileName();
2126 if (!parser.skipWord(filename.c_str())) {
2127 return reportError<bool>("missing object type");
2128 }
2129 } else if (parser.skipExact("fn")) {
2130 SkASSERT(0); // incomplete
2131 } else {
2132 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2133 return reportError<bool>("missing object type");
2134 }
2135 if (!parser.skipWord(markupDef->fName.c_str()) &&
2136 KeyWord::kEnum != markupDef->fKeyWord) {
2137 return reportError<bool>("missing object name");
2138 }
Cary Clark8032b982017-07-28 11:04:54 -04002139 }
Cary Clark8032b982017-07-28 11:04:54 -04002140 }
2141 }
2142 // remove leading '*' if present
2143 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2144 while (!parser.eof() && parser.skipWhiteSpace()) {
2145 while ('*' == parser.peek()) {
2146 parser.next();
2147 if (parser.eof()) {
2148 break;
2149 }
2150 parser.skipWhiteSpace();
2151 }
2152 if (parser.eof()) {
2153 break;
2154 }
2155 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002156 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002157 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002158 parser.skipToEndBracket('\n');
2159 }
2160 return true;
2161}
2162
Cary Clarkd98f78c2018-04-26 08:32:37 -04002163bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002164 if (!markupDef) {
2165 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2166 child->fLineCount, fParent, '\0');
2167 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002168 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002169 globalMarkupChild->fName = globalUniqueName;
2170 if (!this->findComments(*child, globalMarkupChild)) {
2171 return false;
2172 }
2173 fIConstMap[globalUniqueName] = globalMarkupChild;
2174 return true;
2175 }
2176 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2177 child->fLineCount, markupDef, '\0');
2178 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002179 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002180 markupChild->fTerminator = markupChild->fContentEnd;
2181 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002182 classDef.fConsts[child->fName] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002183 fIConstMap[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002184 return true;
2185}
2186
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002187bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2188 TextParser parser(child);
2189 if (!parser.skipExact("#define")) {
2190 return false;
2191 }
2192 if (!parser.skipSpace()) {
2193 return false;
2194 }
2195 const char* nameStart = parser.fChar;
2196 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2197 if (parser.eof()) {
2198 return true; // do nothing if #define doesn't define anything
2199 }
2200 string nameStr(nameStart, parser.fChar - nameStart);
2201 struct Param {
2202 const char* fStart;
2203 const char* fEnd;
2204 };
2205 vector<Param> params;
2206 if ('(' == parser.peek()) {
2207 parser.next();
2208 if (!parser.skipSpace()) {
2209 return false;
2210 }
2211 do {
2212 const char* paramStart = parser.fChar;
2213 if (!parser.skipExact("...")) {
2214 parser.skipToNonAlphaNum();
2215 }
2216 if (parser.eof()) {
2217 return false;
2218 }
2219 params.push_back({paramStart, parser.fChar});
2220 if (!parser.skipSpace()) {
2221 return false;
2222 }
2223 if (')' == parser.peek()) {
2224 parser.next();
2225 break;
2226 }
2227 if (',' != parser.next()) {
2228 return false;
2229 }
2230 if (!parser.skipSpace()) {
2231 return false;
2232 }
2233 } while (true);
2234 }
2235 if (!parser.skipSpace()) {
2236 return false;
2237 }
2238 if (!markupDef) {
2239 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2240 child->fLineCount, fParent, '\0');
2241 Definition* globalMarkupChild = &fGlobals.back();
2242 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2243 globalMarkupChild->fName = globalUniqueName;
2244 globalMarkupChild->fTerminator = child->fContentEnd;
2245 if (!this->findComments(*child, globalMarkupChild)) {
2246 return false;
2247 }
2248 fIDefineMap[globalUniqueName] = globalMarkupChild;
2249 for (Param param : params) {
2250 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2251 child->fLineCount, globalMarkupChild, '\0');
2252 Definition* paramChild = &globalMarkupChild->fTokens.back();
2253 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2254 paramChild->fTerminator = param.fEnd;
2255 }
2256 return true;
2257 }
2258 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2259 child->fLineCount, markupDef, '\0');
2260 Definition* markupChild = &markupDef->fTokens.back();
2261 markupChild->fName = nameStr;
2262 markupChild->fTerminator = markupChild->fContentEnd;
2263 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2264 if (!this->findComments(*child, markupChild)) {
2265 return false;
2266 }
2267 classDef.fDefines[nameStr] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002268 fIDefineMap[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002269 return true;
2270}
2271
2272bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002273 TextParser parser(child);
2274 parser.skipToEndBracket('{');
2275 if (parser.eof()) {
2276 return true; // if enum is a forward declaration, do nothing
2277 }
2278 parser.next();
2279 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002280 if (child->fTokens.size() > 0) {
2281 auto token = child->fTokens.begin();
2282 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2283 token = token->fTokens.begin();
2284 }
2285 if (Definition::Type::kWord == token->fType) {
2286 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
2287 }
2288 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002289 Definition* markupChild;
2290 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002291 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2292 child->fLineCount, fParent, '\0');
2293 markupChild = &fGlobals.back();
2294 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2295 markupChild->fName = globalUniqueName;
2296 markupChild->fTerminator = child->fContentEnd;
2297 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002298 } else {
2299 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002300 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002301 markupChild = &markupDef->fTokens.back();
2302 }
Cary Clark8032b982017-07-28 11:04:54 -04002303 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2304 markupChild->fKeyWord = KeyWord::kEnum;
2305 TextParser enumName(child);
2306 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05002307 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002308 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002309 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002310 markupChild->fMarkType = MarkType::kEnumClass;
2311 }
Cary Clark8032b982017-07-28 11:04:54 -04002312 const char* nameStart = enumName.fChar;
2313 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05002314 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04002315 markupChild->fName = markupDef->fName + "::" +
2316 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05002317 }
Cary Clark8032b982017-07-28 11:04:54 -04002318 if (!this->findComments(*child, markupChild)) {
2319 return false;
2320 }
Cary Clark8032b982017-07-28 11:04:54 -04002321 const char* dataEnd;
2322 do {
Cary Clark8032b982017-07-28 11:04:54 -04002323 parser.skipWhiteSpace();
2324 if ('}' == parser.peek()) {
2325 break;
2326 }
2327 Definition* comment = nullptr;
2328 // note that comment, if any, can be before or after (on the same line, though) as member
2329 if ('#' == parser.peek()) {
2330 // fixme: handle preprecessor, but just skip it for now
2331 parser.skipToLineStart();
2332 }
2333 while (parser.startsWith("/*") || parser.startsWith("//")) {
2334 parser.next();
2335 const char* start = parser.fChar;
2336 const char* end;
2337 if ('*' == parser.peek()) {
2338 end = parser.strnstr("*/", parser.fEnd);
2339 parser.fChar = end;
2340 parser.next();
2341 parser.next();
2342 } else {
2343 end = parser.trimmedLineEnd();
2344 parser.skipToLineStart();
2345 }
2346 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002347 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002348 comment = &markupChild->fTokens.back();
2349 comment->fTerminator = end;
2350 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
2351 return false;
2352 }
2353 parser.skipWhiteSpace();
2354 }
2355 parser.skipWhiteSpace();
2356 const char* memberStart = parser.fChar;
2357 if ('}' == memberStart[0]) {
2358 break;
2359 }
Cary Clark9174bda2017-09-19 17:39:32 -04002360 // if there's comment on same the line as member def, output first as if it was before
2361
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002362 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002363 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04002364 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04002365 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04002366 }
Cary Clark8032b982017-07-28 11:04:54 -04002367 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04002368 if ('=' == parser.peek()) {
2369 parser.skipToEndBracket(',');
2370 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002371 if (!parser.eof() && '#' == parser.peek()) {
2372 // fixme: handle preprecessor, but just skip it for now
2373 continue;
2374 }
Cary Clark9174bda2017-09-19 17:39:32 -04002375 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04002376 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04002377 }
2378 dataEnd = parser.fChar;
2379 const char* start = parser.anyOf("/\n");
2380 SkASSERT(start);
2381 parser.skipTo(start);
2382 if ('/' == parser.next()) {
2383 char slashStar = parser.next();
2384 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04002385 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04002386 char doxCheck = parser.next();
2387 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
2388 save.restore();
2389 }
2390 }
2391 parser.skipWhiteSpace();
2392 const char* commentStart = parser.fChar;
2393 if ('/' == slashStar) {
2394 parser.skipToEndBracket('\n');
2395 } else {
2396 parser.skipToEndBracket("*/");
2397 }
2398 SkASSERT(!parser.eof());
2399 const char* commentEnd = parser.fChar;
2400 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002401 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04002402 comment = &markupChild->fTokens.back();
2403 comment->fTerminator = commentEnd;
2404 }
Cary Clark8032b982017-07-28 11:04:54 -04002405 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002406 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002407 Definition* member = &markupChild->fTokens.back();
2408 member->fName = memberName;
2409 if (comment) {
2410 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04002411 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002412 }
2413 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04002414 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002415 for (auto outsideMember : child->fChildren) {
2416 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002417 continue;
2418 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002419 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2420 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002421 continue;
2422 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002423 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2424 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002425 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002426 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002427 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002428 // FIXME: ? add comment as well ?
2429 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002430 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002431 if (markupDef) {
2432 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2433 SkASSERT(classDef.fStart);
2434 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002435 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002436 markupChild->fName = uniqueName;
2437 classDef.fEnums[uniqueName] = markupChild;
Cary Clark61313f32018-10-08 14:57:48 -04002438 fIEnumMap[fullName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002439 }
Cary Clark8032b982017-07-28 11:04:54 -04002440 return true;
2441}
2442
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002443bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002444 fParent = &fIncludeMap[name];
2445 fParent->fName = name;
2446 fParent->fFileName = fFileName;
2447 fParent->fType = Definition::Type::kFileType;
2448 fParent->fContentStart = fChar;
2449 fParent->fContentEnd = fEnd;
2450 // parse include file into tree
2451 while (fChar < fEnd) {
2452 if (!this->parseChar()) {
2453 return false;
2454 }
2455 }
2456 // parse tree and add named objects to maps
2457 fParent = &fIncludeMap[name];
2458 if (!this->parseObjects(fParent, nullptr)) {
2459 return false;
2460 }
2461 return true;
2462}
2463
2464bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2465 const char* typeStart = child->fChildren[0]->fContentStart;
2466 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002467 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002468 Definition* markupChild = &markupDef->fTokens.back();
2469 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002470 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002471 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2472 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2473 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2474 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002475 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04002476 classDef.fMembers[uniqueName] = markupChild;
2477 if (child->fParentIndex >= 2) {
2478 auto comment = child->fParent->fTokens.begin();
2479 std::advance(comment, child->fParentIndex - 2);
2480 if (Definition::Type::kBracket == comment->fType
2481 && (Bracket::kSlashStar == comment->fBracket
2482 || Bracket::kSlashSlash == comment->fBracket)) {
2483 TextParser parser(&*comment);
2484 do {
2485 parser.skipToAlpha();
2486 if (parser.eof()) {
2487 break;
2488 }
2489 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002490 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002491 if (Bracket::kSlashStar == comment->fBracket) {
2492 const char* commentEnd = parser.strnstr("*/", end);
2493 if (commentEnd) {
2494 end = commentEnd;
2495 }
2496 }
2497 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002498 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002499 Definition* commentChild = &markupDef->fTokens.back();
2500 markupChild->fChildren.emplace_back(commentChild);
2501 parser.skipTo(end);
2502 } while (!parser.eof());
2503 }
2504 }
2505 return true;
2506}
2507
2508bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2509 auto tokenIter = child->fParent->fTokens.begin();
2510 std::advance(tokenIter, child->fParentIndex);
2511 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002512 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002513 bool addConst = false;
2514 auto operatorCheck = tokenIter;
2515 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2516 operatorCheck = std::prev(tokenIter);
2517 }
2518 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002519 auto closeParen = std::next(tokenIter);
2520 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2521 '(' == closeParen->fContentStart[0]);
2522 nameEnd = closeParen->fContentEnd + 1;
2523 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002524 if (Definition::Type::kKeyWord == closeParen->fType &&
2525 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002526 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002527 }
Cary Clarka560c472017-11-27 10:44:06 -05002528 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002529 }
2530 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002531 if (addConst) {
2532 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002533 }
Cary Clark8032b982017-07-28 11:04:54 -04002534 while (tokenIter != child->fParent->fTokens.begin()) {
2535 auto testIter = std::prev(tokenIter);
2536 switch (testIter->fType) {
2537 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002538 if (testIter == child->fParent->fTokens.begin() &&
2539 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2540 KeyWord::kIfndef == child->fParent->fKeyWord ||
2541 KeyWord::kIf == child->fParent->fKeyWord)) {
2542 std::next(tokenIter);
2543 break;
2544 }
Cary Clark8032b982017-07-28 11:04:54 -04002545 goto keepGoing;
2546 case Definition::Type::kKeyWord: {
2547 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2548 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2549 goto keepGoing;
2550 }
2551 } break;
2552 case Definition::Type::kBracket:
2553 if (Bracket::kAngle == testIter->fBracket) {
2554 goto keepGoing;
2555 }
2556 break;
2557 case Definition::Type::kPunctuation:
2558 if (Punctuation::kSemicolon == testIter->fPunctuation
2559 || Punctuation::kLeftBrace == testIter->fPunctuation
2560 || Punctuation::kColon == testIter->fPunctuation) {
2561 break;
2562 }
2563 keepGoing:
2564 tokenIter = testIter;
2565 continue;
2566 default:
2567 break;
2568 }
2569 break;
2570 }
Cary Clark224c7002018-06-27 11:00:21 -04002571 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002572 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002573 tokenIter->fPrivate = string::npos != nameStr.find("::")
2574 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002575 auto testIter = child->fParent->fTokens.begin();
2576 SkASSERT(child->fParentIndex > 0);
2577 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002578 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2579 0 == tokenIter->fParentIndex) {
2580 tokenIter = std::next(tokenIter);
2581 }
Cary Clark8032b982017-07-28 11:04:54 -04002582 const char* start = tokenIter->fContentStart;
2583 const char* end = tokenIter->fContentEnd;
2584 const char kDebugCodeStr[] = "SkDEBUGCODE";
2585 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2586 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2587 std::advance(testIter, 1);
2588 start = testIter->fContentStart + 1;
2589 end = testIter->fContentEnd - 1;
2590 } else {
2591 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002592 do {
2593 std::advance(testIter, 1);
2594 if (testIter == child->fParent->fTokens.end()) {
2595 break;
2596 }
Cary Clark8032b982017-07-28 11:04:54 -04002597 switch (testIter->fType) {
2598 case Definition::Type::kPunctuation:
2599 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2600 || Punctuation::kLeftBrace == testIter->fPunctuation
2601 || Punctuation::kColon == testIter->fPunctuation);
2602 end = testIter->fStart;
2603 break;
2604 case Definition::Type::kKeyWord: {
2605 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2606 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2607 continue;
2608 }
2609 } break;
2610 default:
2611 continue;
2612 }
2613 break;
Cary Clark61313f32018-10-08 14:57:48 -04002614 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002615 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002616 while (end > start && ' ' >= end[-1]) {
2617 --end;
2618 }
Cary Clark9174bda2017-09-19 17:39:32 -04002619 if (!markupDef) {
2620 auto parentIter = child->fParent->fTokens.begin();
2621 SkASSERT(child->fParentIndex > 0);
2622 std::advance(parentIter, child->fParentIndex - 1);
2623 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002624 TextParser nameParser(methodName);
2625 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002626 return true; // expect this is inline class definition outside of class
2627 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002628 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2629 fParent, '\0');
2630 Definition* globalMarkupChild = &fGlobals.back();
2631 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2632 globalMarkupChild->fName = globalUniqueName;
2633 if (!this->findComments(*child, globalMarkupChild)) {
2634 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002635 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002636 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002637 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002638 }
Cary Clark8032b982017-07-28 11:04:54 -04002639 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002640 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002641 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002642 {
2643 auto mapIter = fIClassMap.find(markupDef->fName);
2644 SkASSERT(fIClassMap.end() != mapIter);
2645 IClassDefinition& classDef = mapIter->second;
2646 SkASSERT(classDef.fStart);
2647 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2648 markupChild->fName = uniqueName;
2649 if (!this->findComments(*child, markupChild)) {
2650 return false;
2651 }
2652 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002653 }
Cary Clark8032b982017-07-28 11:04:54 -04002654 return true;
2655}
2656
Cary Clark8032b982017-07-28 11:04:54 -04002657bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002658 fPriorObject = nullptr;
2659 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002660 if (!this->parseObject(child, markupDef)) {
2661 return false;
2662 }
Cary Clark0d225392018-06-07 09:59:07 -04002663 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002664 }
2665 return true;
2666}
2667
2668bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2669 // set up for error reporting
2670 fLine = fChar = child->fStart;
2671 fEnd = child->fContentEnd;
2672 // todo: put original line number in child as well
2673 switch (child->fType) {
2674 case Definition::Type::kKeyWord:
2675 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002676 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002677 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002678 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002679 }
2680 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002681 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002682 case KeyWord::kConst:
2683 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002684 if (!this->parseConst(child, markupDef)) {
2685 return child->reportError<bool>("failed to parse const or constexpr");
2686 }
2687 break;
Cary Clark8032b982017-07-28 11:04:54 -04002688 case KeyWord::kEnum:
2689 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002690 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002691 }
2692 break;
2693 case KeyWord::kStruct:
2694 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002695 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002696 }
2697 break;
2698 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002699 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002700 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002701 }
2702 break;
2703 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002704 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002705 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002706 }
2707 break;
2708 case KeyWord::kUnion:
2709 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002710 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002711 }
2712 break;
Cary Clark61313f32018-10-08 14:57:48 -04002713 case KeyWord::kUsing:
2714 if (!this->parseUsing()) {
2715 return child->reportError<bool>("failed to parse using");
2716 }
2717 break;
Cary Clark8032b982017-07-28 11:04:54 -04002718 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002719 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002720 }
2721 break;
2722 case Definition::Type::kBracket:
2723 switch (child->fBracket) {
2724 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002725 {
2726 auto tokenIter = child->fParent->fTokens.begin();
2727 std::advance(tokenIter, child->fParentIndex);
2728 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002729 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002730 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002731 break;
2732 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002733 if (Bracket::kPound == child->fParent->fBracket &&
2734 KeyWord::kIf == child->fParent->fKeyWord) {
2735 // TODO: this will skip methods named defined() -- for the
2736 // moment there aren't any
2737 if (previousToken.startsWith("defined")) {
2738 break;
2739 }
2740 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002741 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2742 break;
2743 }
Cary Clark73fa9722017-08-29 17:36:51 -04002744 }
Cary Clark0d225392018-06-07 09:59:07 -04002745 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2746 break;
2747 }
Cary Clark8032b982017-07-28 11:04:54 -04002748 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002749 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002750 }
Cary Clark73fa9722017-08-29 17:36:51 -04002751 break;
Cary Clark8032b982017-07-28 11:04:54 -04002752 case Bracket::kSlashSlash:
2753 case Bracket::kSlashStar:
2754 // comments are picked up by parsing objects first
2755 break;
2756 case Bracket::kPound:
2757 // special-case the #xxx xxx_DEFINED entries
2758 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002759 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002760 case KeyWord::kIfndef:
2761 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002762 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002763 if (!this->parseObjects(child, markupDef)) {
2764 return false;
2765 }
2766 break;
2767 }
2768 goto preproError;
2769 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002770 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002771 break;
2772 }
2773 goto preproError;
2774 case KeyWord::kEndif:
2775 if (child->boilerplateEndIf()) {
2776 break;
2777 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002778 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002779 case KeyWord::kInclude:
2780 // ignored for now
2781 break;
2782 case KeyWord::kElse:
2783 case KeyWord::kElif:
2784 // todo: handle these
2785 break;
2786 default:
2787 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002788 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002789 }
2790 break;
2791 case Bracket::kAngle:
2792 // pick up templated function pieces when method is found
2793 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002794 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002795 if (!this->parseObjects(child, markupDef)) {
2796 return false;
2797 }
Cary Clark73fa9722017-08-29 17:36:51 -04002798 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002799 case Bracket::kSquare: {
2800 // check to see if parent is operator, the only case we handle so far
2801 auto prev = child->fParent->fTokens.begin();
2802 std::advance(prev, child->fParentIndex - 1);
2803 if (KeyWord::kOperator != prev->fKeyWord) {
2804 return child->reportError<bool>("expected operator overload");
2805 }
2806 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002807 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002808 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002809 }
2810 break;
2811 case Definition::Type::kWord:
2812 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002813 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002814 }
2815 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002816 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002817 }
2818 break;
2819 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002820 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002821 break;
2822 }
2823 return true;
2824}
2825
Cary Clarkbbfda252018-03-09 15:32:01 -05002826bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2827 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002828}
2829
Cary Clark2f466242017-12-11 16:03:17 -05002830bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2831 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002832 typedefParser.skipExact("typedef");
2833 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002834 string nameStr = typedefParser.typedefName();
2835 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002836 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2837 child->fLineCount, fParent, '\0');
2838 Definition* globalMarkupChild = &fGlobals.back();
2839 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2840 globalMarkupChild->fName = globalUniqueName;
2841 if (!this->findComments(*child, globalMarkupChild)) {
2842 return false;
2843 }
2844 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002845 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002846 return true;
2847 }
2848 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002849 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002850 Definition* markupChild = &markupDef->fTokens.back();
2851 markupChild->fName = nameStr;
2852 markupChild->fTerminator = markupChild->fContentEnd;
2853 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2854 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002855 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04002856 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002857 return true;
2858}
2859
2860bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002861 // incomplete
2862 return true;
2863}
Cary Clark8032b982017-07-28 11:04:54 -04002864
Cary Clark61313f32018-10-08 14:57:48 -04002865bool IncludeParser::parseUsing() {
2866 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002867 return true;
2868}
2869
2870bool IncludeParser::parseChar() {
2871 char test = *fChar;
2872 if ('\\' == fPrev) {
2873 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002874// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002875 fLine = fChar + 1;
2876 }
2877 goto done;
2878 }
2879 switch (test) {
2880 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002881// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002882 fLine = fChar + 1;
2883 if (fInChar) {
2884 return reportError<bool>("malformed char");
2885 }
2886 if (fInString) {
2887 return reportError<bool>("malformed string");
2888 }
2889 if (!this->checkForWord()) {
2890 return false;
2891 }
2892 if (Bracket::kPound == this->topBracket()) {
2893 KeyWord keyWord = fParent->fKeyWord;
2894 if (KeyWord::kNone == keyWord) {
2895 return this->reportError<bool>("unhandled preprocessor directive");
2896 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002897 if (fInDefine) {
2898 SkASSERT(KeyWord::kDefine == keyWord);
2899 fInDefine = false;
2900 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002901 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002902 this->popBracket();
2903 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002904 if (fInBrace) {
2905 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2906 fInBrace = nullptr;
2907 }
Cary Clark8032b982017-07-28 11:04:54 -04002908 } else if (Bracket::kSlashSlash == this->topBracket()) {
2909 this->popBracket();
2910 }
2911 break;
2912 case '*':
2913 if (!fInCharCommentString && '/' == fPrev) {
2914 this->pushBracket(Bracket::kSlashStar);
2915 }
2916 if (!this->checkForWord()) {
2917 return false;
2918 }
2919 if (!fInCharCommentString) {
2920 this->addPunctuation(Punctuation::kAsterisk);
2921 }
2922 break;
2923 case '/':
2924 if ('*' == fPrev) {
2925 if (!fInCharCommentString) {
2926 return reportError<bool>("malformed closing comment");
2927 }
2928 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002929 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002930 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002931 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002932 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002933 }
2934 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002935 }
Cary Clark8032b982017-07-28 11:04:54 -04002936 if (!fInCharCommentString && '/' == fPrev) {
2937 this->pushBracket(Bracket::kSlashSlash);
2938 break;
2939 }
2940 if (!this->checkForWord()) {
2941 return false;
2942 }
2943 break;
2944 case '\'':
2945 if (Bracket::kChar == this->topBracket()) {
2946 this->popBracket();
2947 } else if (!fInComment && !fInString) {
2948 if (fIncludeWord) {
2949 return this->reportError<bool>("word then single-quote");
2950 }
2951 this->pushBracket(Bracket::kChar);
2952 }
2953 break;
2954 case '\"':
2955 if (Bracket::kString == this->topBracket()) {
2956 this->popBracket();
2957 } else if (!fInComment && !fInChar) {
2958 if (fIncludeWord) {
2959 return this->reportError<bool>("word then double-quote");
2960 }
2961 this->pushBracket(Bracket::kString);
2962 }
2963 break;
Cary Clark8032b982017-07-28 11:04:54 -04002964 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002965 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002966 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2967 this->pushBracket(Bracket::kDebugCode);
2968 break;
2969 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002970 case ':':
2971 case '[':
2972 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002973 if (fInCharCommentString) {
2974 break;
2975 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002976 if (fInDefine && fInBrace) {
2977 break;
2978 }
Cary Clark8032b982017-07-28 11:04:54 -04002979 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2980 break;
2981 }
Cary Clark0d225392018-06-07 09:59:07 -04002982 if (fConstExpr) {
2983 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2984 fConstExpr = nullptr;
2985 }
Cary Clark8032b982017-07-28 11:04:54 -04002986 if (!fInBrace) {
2987 if (!this->checkForWord()) {
2988 return false;
2989 }
2990 if (':' == test && !fInFunction) {
2991 break;
2992 }
2993 if ('{' == test) {
2994 this->addPunctuation(Punctuation::kLeftBrace);
2995 } else if (':' == test) {
2996 this->addPunctuation(Punctuation::kColon);
2997 }
2998 }
2999 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
3000 && Bracket::kColon == fInBrace->fBracket) {
3001 Definition* braceParent = fParent->fParent;
3002 braceParent->fChildren.pop_back();
3003 braceParent->fTokens.pop_back();
3004 fParent = braceParent;
3005 fInBrace = nullptr;
3006 }
3007 this->pushBracket(
3008 '(' == test ? Bracket::kParen :
3009 '[' == test ? Bracket::kSquare :
3010 '{' == test ? Bracket::kBrace :
3011 Bracket::kColon);
3012 if (!fInBrace
3013 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3014 && fInFunction) {
3015 fInBrace = fParent;
3016 }
3017 } break;
3018 case '<':
3019 if (fInCharCommentString || fInBrace) {
3020 break;
3021 }
3022 if (!this->checkForWord()) {
3023 return false;
3024 }
3025 if (fInEnum) {
3026 break;
3027 }
3028 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003029 // this angle bracket may be an operator or may be a bracket
3030 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003031 break;
3032 case ')':
3033 case ']':
3034 case '}': {
3035 if (fInCharCommentString) {
3036 break;
3037 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003038 if (fInDefine && fInBrace) {
3039 break;
3040 }
Cary Clark8032b982017-07-28 11:04:54 -04003041 if (!fInBrace) {
3042 if (!this->checkForWord()) {
3043 return false;
3044 }
3045 }
3046 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003047 Bracket match = ')' == test ? Bracket::kParen :
3048 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3049 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003050 this->popBracket();
3051 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003052 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003053 } else {
3054 fInFunction = '}' != test;
3055 }
Cary Clark73fa9722017-08-29 17:36:51 -04003056 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3057 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003058 } else if (Bracket::kAngle == this->topBracket()
3059 && match == this->grandParentBracket()) {
3060 this->popBracket();
3061 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003062 } else {
3063 return reportError<bool>("malformed close bracket");
3064 }
3065 if (popBraceParent) {
3066 Definition* braceParent = fInBrace->fParent;
3067 braceParent->fChildren.pop_back();
3068 braceParent->fTokens.pop_back();
3069 fInBrace = nullptr;
3070 }
3071 } break;
3072 case '>':
3073 if (fInCharCommentString || fInBrace) {
3074 break;
3075 }
3076 if (!this->checkForWord()) {
3077 return false;
3078 }
3079 if (fInEnum) {
3080 break;
3081 }
Cary Clarka560c472017-11-27 10:44:06 -05003082 if (Bracket::kPound == this->topBracket()) {
3083 break;
3084 }
Cary Clark8032b982017-07-28 11:04:54 -04003085 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003086 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003087 this->popBracket();
3088 } else {
3089 return reportError<bool>("malformed close angle bracket");
3090 }
3091 break;
3092 case '#': {
3093 if (fInCharCommentString || fInBrace) {
3094 break;
3095 }
3096 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3097 this->pushBracket(Bracket::kPound);
3098 break;
3099 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003100 case ' ':
3101 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3102 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3103 fInBrace = fParent;
3104 // delimiting brackets are space ... unescaped-linefeed
3105 }
Cary Clark8032b982017-07-28 11:04:54 -04003106 case '&':
3107 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003108 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003109 case '-':
3110 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003111 if (fInCharCommentString || fInBrace) {
3112 break;
3113 }
3114 if (!this->checkForWord()) {
3115 return false;
3116 }
3117 break;
Cary Clark0d225392018-06-07 09:59:07 -04003118 case '=':
3119 if (fInCharCommentString || fInBrace) {
3120 break;
3121 }
3122 if (!this->checkForWord()) {
3123 return false;
3124 }
Cary Clarkd7895502018-07-18 15:10:08 -04003125 if (!fParent->fTokens.size()) {
3126 break;
3127 }
Cary Clark0d225392018-06-07 09:59:07 -04003128 {
3129 const Definition& lastToken = fParent->fTokens.back();
3130 if (lastToken.fType != Definition::Type::kWord) {
3131 break;
3132 }
3133 string name(lastToken.fContentStart, lastToken.length());
3134 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3135 break;
3136 }
3137 // find token on start of line
3138 auto lineIter = fParent->fTokens.end();
3139 do {
Cary Clark80247e52018-07-11 16:18:41 -04003140 if (fParent->fTokens.begin() == lineIter) {
3141 break;
3142 }
Cary Clark0d225392018-06-07 09:59:07 -04003143 --lineIter;
3144 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003145 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003146 ++lineIter;
3147 }
3148 Definition* lineStart = &*lineIter;
3149 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3150 bool sawConst = false;
3151 bool sawStatic = false;
3152 bool sawTemplate = false;
3153 bool sawType = false;
3154 while (&lastToken != &*lineIter) {
3155 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3156 if (sawConst || sawStatic || sawTemplate) {
3157 sawConst = false;
3158 break;
3159 }
3160 if (&lastToken == &*++lineIter) {
3161 break;
3162 }
3163 if (KeyWord::kTypename != lineIter->fKeyWord) {
3164 break;
3165 }
3166 if (&lastToken == &*++lineIter) {
3167 break;
3168 }
3169 if (Definition::Type::kWord != lineIter->fType) {
3170 break;
3171 }
3172 sawTemplate = true;
3173 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3174 if (sawConst || sawStatic) {
3175 sawConst = false;
3176 break;
3177 }
3178 sawStatic = true;
3179 } else if (KeyWord::kConst == lineIter->fKeyWord
3180 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3181 if (sawConst) {
3182 sawConst = false;
3183 break;
3184 }
3185 sawConst = true;
3186 } else {
3187 if (sawType) {
3188 sawType = false;
3189 break;
3190 }
3191 if (Definition::Type::kKeyWord == lineIter->fType
3192 && KeyProperty::kNumber
3193 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3194 sawType = true;
3195 } else if (Definition::Type::kWord == lineIter->fType) {
3196 string typeName(lineIter->fContentStart, lineIter->length());
3197 if ("Sk" != name.substr(0, 2)) {
3198 sawType = true;
3199 }
3200 }
3201 }
3202 ++lineIter;
3203 }
3204 if (sawType && sawConst) {
3205 // if found, name first
3206 lineStart->fName = name;
3207 lineStart->fMarkType = MarkType::kConst;
3208 fParent->fChildren.emplace_back(lineStart);
3209 fConstExpr = lineStart;
3210 }
3211 }
3212 break;
Cary Clark8032b982017-07-28 11:04:54 -04003213 case ';':
3214 if (fInCharCommentString || fInBrace) {
3215 break;
3216 }
3217 if (!this->checkForWord()) {
3218 return false;
3219 }
Cary Clark0d225392018-06-07 09:59:07 -04003220 if (fConstExpr) {
3221 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3222 fConstExpr = nullptr;
3223 }
Cary Clark8032b982017-07-28 11:04:54 -04003224 if (Definition::Type::kKeyWord == fParent->fType
3225 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003226 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3227 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003228 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3229 this->popObject();
3230 }
Cary Clark8032b982017-07-28 11:04:54 -04003231 if (KeyWord::kEnum == fParent->fKeyWord) {
3232 fInEnum = false;
3233 }
Cary Clark61313f32018-10-08 14:57:48 -04003234 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003235 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003236 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3237 this->popObject();
3238 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003239 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003240 } else if (Definition::Type::kBracket == fParent->fType
3241 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3242 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3243 list<Definition>::iterator baseIter = fParent->fTokens.end();
3244 list<Definition>::iterator namedIter = fParent->fTokens.end();
3245 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003246 fParent->fTokens.begin() != tokenIter; ) {
3247 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003248 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003249 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003250 if (namedIter != fParent->fTokens.end()) {
3251 return reportError<bool>("found two named member tokens");
3252 }
3253 namedIter = tokenIter;
3254 }
3255 baseIter = tokenIter;
3256 } else {
3257 break;
3258 }
3259 }
3260 // FIXME: if a member definition spans multiple lines, this won't work
3261 if (namedIter != fParent->fTokens.end()) {
3262 if (baseIter == namedIter) {
3263 return this->reportError<bool>("expected type before named token");
3264 }
3265 Definition* member = &*namedIter;
3266 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003267 if (!member->fTerminator) {
3268 member->fTerminator = member->fContentEnd;
3269 }
Cary Clark8032b982017-07-28 11:04:54 -04003270 fParent->fChildren.push_back(member);
3271 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3272 member->fChildren.push_back(&*nameType);
3273 }
Cary Clark8032b982017-07-28 11:04:54 -04003274 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003275 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003276 } else if (fParent->fChildren.size() > 0) {
3277 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003278 Definition* priorEnum = fPriorEnum;
3279 fPriorEnum = nullptr;
3280 if (!priorEnum) {
3281 while (fParent->fChildren.begin() != lastIter) {
3282 std::advance(lastIter, -1);
3283 priorEnum = *lastIter;
3284 if (Definition::Type::kBracket != priorEnum->fType ||
3285 (Bracket::kSlashSlash != priorEnum->fBracket
3286 && Bracket::kSlashStar != priorEnum->fBracket)) {
3287 break;
3288 }
Cary Clark8032b982017-07-28 11:04:54 -04003289 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003290 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003291 }
3292 if (Definition::Type::kKeyWord == priorEnum->fType
3293 && KeyWord::kEnum == priorEnum->fKeyWord) {
3294 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003295 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003296 while (tokenWalker != fParent->fTokens.end()) {
3297 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003298 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003299 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3300 break;
3301 }
3302 }
3303 while (tokenWalker != fParent->fTokens.end()) {
3304 std::advance(tokenWalker, 1);
3305 const Definition* test = &*tokenWalker;
3306 if (Definition::Type::kBracket != test->fType ||
3307 (Bracket::kSlashSlash != test->fBracket
3308 && Bracket::kSlashStar != test->fBracket)) {
3309 break;
3310 }
3311 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003312 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003313 Definition* start = &*tokenWalker;
3314 bool foundExpected = true;
3315 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3316 const Definition* test = &*tokenWalker;
3317 if (expected != test->fKeyWord) {
3318 foundExpected = false;
3319 break;
3320 }
3321 if (tokenWalker == fParent->fTokens.end()) {
3322 break;
3323 }
3324 std::advance(tokenWalker, 1);
3325 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003326 if (!foundExpected) {
3327 foundExpected = true;
3328 tokenWalker = saveTokenWalker;
3329 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3330 const Definition* test = &*tokenWalker;
3331 if (expected != test->fKeyWord) {
3332 foundExpected = false;
3333 break;
3334 }
3335 if (tokenWalker == fParent->fTokens.end()) {
3336 break;
3337 }
3338 if (KeyWord::kNone != expected) {
3339 std::advance(tokenWalker, 1);
3340 }
3341 }
3342 if (foundExpected) {
3343 auto nameToken = priorEnum->fTokens.begin();
3344 string enumName = string(nameToken->fContentStart,
3345 nameToken->fContentEnd - nameToken->fContentStart);
3346 const Definition* test = &*tokenWalker;
3347 string constType = string(test->fContentStart,
3348 test->fContentEnd - test->fContentStart);
3349 if (enumName != constType) {
3350 foundExpected = false;
3351 } else {
3352 std::advance(tokenWalker, 1);
3353 }
3354 }
3355 }
Cary Clark8032b982017-07-28 11:04:54 -04003356 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3357 const char* nameStart = tokenWalker->fStart;
3358 std::advance(tokenWalker, 1);
3359 if (tokenWalker != fParent->fTokens.end()) {
3360 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003361 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003362 start->fName = string(nameStart, tp.fChar - nameStart);
3363 start->fContentEnd = fChar;
3364 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003365 fPriorEnum = priorEnum;
3366 }
Cary Clark8032b982017-07-28 11:04:54 -04003367 }
3368 }
3369 }
3370 this->addPunctuation(Punctuation::kSemicolon);
3371 fInFunction = false;
3372 break;
3373 case '~':
3374 if (fInEnum) {
3375 break;
3376 }
3377 case '0': case '1': case '2': case '3': case '4':
3378 case '5': case '6': case '7': case '8': case '9':
3379 // TODO: don't want to parse numbers, but do need to track for enum defs
3380 // break;
3381 case 'A': case 'B': case 'C': case 'D': case 'E':
3382 case 'F': case 'G': case 'H': case 'I': case 'J':
3383 case 'K': case 'L': case 'M': case 'N': case 'O':
3384 case 'P': case 'Q': case 'R': case 'S': case 'T':
3385 case 'U': case 'V': case 'W': case 'X': case 'Y':
3386 case 'Z': case '_':
3387 case 'a': case 'b': case 'c': case 'd': case 'e':
3388 case 'f': case 'g': case 'h': case 'i': case 'j':
3389 case 'k': case 'l': case 'm': case 'n': case 'o':
3390 case 'p': case 'q': case 'r': case 's': case 't':
3391 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003392 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003393 if (fInCharCommentString || fInBrace) {
3394 break;
3395 }
3396 if (!fIncludeWord) {
3397 fIncludeWord = fChar;
3398 }
3399 break;
3400 }
3401done:
3402 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003403 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003404 return true;
3405}
3406
3407void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003408 IncludeParser::ValidateKeyWords();
3409}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003410
Cary Clark186d08f2018-04-03 08:43:27 -04003411bool IncludeParser::references(const SkString& file) const {
3412 // if includes weren't passed one at a time, assume all references are valid
3413 if (fIncludeMap.empty()) {
3414 return true;
3415 }
3416 SkASSERT(file.endsWith(".bmh") );
3417 string root(file.c_str(), file.size() - 4);
3418 string kReference("_Reference");
3419 if (string::npos != root.find(kReference)) {
3420 root = root.substr(0, root.length() - kReference.length());
3421 }
3422 if (fIClassMap.end() != fIClassMap.find(root)) {
3423 return true;
3424 }
3425 if (fIStructMap.end() != fIStructMap.find(root)) {
3426 return true;
3427 }
Cary Clark224c7002018-06-27 11:00:21 -04003428 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3429 return true;
3430 }
Cary Clarka90ea222018-10-16 10:30:28 -04003431 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3432 return true;
3433 }
Cary Clark224c7002018-06-27 11:00:21 -04003434 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3435 return true;
3436 }
Cary Clark186d08f2018-04-03 08:43:27 -04003437 return false;
3438}
3439
Cary Clark2dc84ad2018-01-26 12:56:22 -05003440void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3441 if (!sk_isdir(includes)) {
3442 IncludeParser::RemoveOneFile(docs, includes);
3443 } else {
3444 SkOSFile::Iter it(includes, ".h");
3445 for (SkString file; it.next(&file); ) {
3446 SkString p = SkOSPath::Join(includes, file.c_str());
3447 const char* hunk = p.c_str();
3448 if (!SkStrEndsWith(hunk, ".h")) {
3449 continue;
3450 }
3451 IncludeParser::RemoveOneFile(docs, hunk);
3452 }
3453 }
3454}
3455
3456void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3457 const char* lastForward = strrchr(includesFile, '/');
3458 const char* lastBackward = strrchr(includesFile, '\\');
3459 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3460 if (!last) {
3461 last = includesFile;
3462 } else {
3463 last += 1;
3464 }
3465 SkString baseName(last);
3466 SkASSERT(baseName.endsWith(".h"));
3467 baseName.remove(baseName.size() - 2, 2);
3468 baseName.append("_Reference.bmh");
3469 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3470 remove(fullName.c_str());
3471}
Cary Clark224c7002018-06-27 11:00:21 -04003472
3473Bracket IncludeParser::topBracket() const {
3474 Definition* parent = this->parentBracket(fParent);
3475 return parent ? parent->fBracket : Bracket::kNone;
3476}