blob: 2fdd7ae5dac9f270ee931af13a9eb55b0ed96a84 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
Cary Clark2dc84ad2018-01-26 12:56:22 -05009#include "SkOSFile.h"
10#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040011
Cary Clark8032b982017-07-28 11:04:54 -040012const IncludeKey kKeyWords[] = {
13 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040014 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040015 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clarkd2ca79c2018-08-10 13:09:13 -040016 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040017 { "bool", KeyWord::kBool, KeyProperty::kNumber },
18 { "char", KeyWord::kChar, KeyProperty::kNumber },
19 { "class", KeyWord::kClass, KeyProperty::kObject },
20 { "const", KeyWord::kConst, KeyProperty::kModifier },
21 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
22 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
23 { "double", KeyWord::kDouble, KeyProperty::kNumber },
24 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
25 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
26 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
27 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050028 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040029 { "float", KeyWord::kFloat, KeyProperty::kNumber },
30 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
31 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
32 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
33 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
34 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
35 { "inline", KeyWord::kInline, KeyProperty::kModifier },
36 { "int", KeyWord::kInt, KeyProperty::kNumber },
37 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
38 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
39 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
40 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
41 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
42 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
43 { "static", KeyWord::kStatic, KeyProperty::kModifier },
44 { "struct", KeyWord::kStruct, KeyProperty::kObject },
45 { "template", KeyWord::kTemplate, KeyProperty::kObject },
46 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clark0d225392018-06-07 09:59:07 -040047 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040048 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040049 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040050 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
51 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040052 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040053 { "union", KeyWord::kUnion, KeyProperty::kObject },
54 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
Cary Clark61313f32018-10-08 14:57:48 -040055 { "using", KeyWord::kUsing, KeyProperty::kObject },
Cary Clark8032b982017-07-28 11:04:54 -040056 { "void", KeyWord::kVoid, KeyProperty::kNumber },
57};
58
59const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
60
61KeyWord IncludeParser::FindKey(const char* start, const char* end) {
62 int ch = 0;
63 for (size_t index = 0; index < kKeyWordCount; ) {
64 if (start[ch] > kKeyWords[index].fName[ch]) {
65 ++index;
Cary Clark61313f32018-10-08 14:57:48 -040066 if (ch > 0 && (index == kKeyWordCount ||
67 kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
Cary Clark8032b982017-07-28 11:04:54 -040068 return KeyWord::kNone;
69 }
70 continue;
71 }
72 if (start[ch] < kKeyWords[index].fName[ch]) {
73 return KeyWord::kNone;
74 }
75 ++ch;
76 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040077 if (end - start < (int) strlen(kKeyWords[index].fName)) {
78 return KeyWord::kNone;
79 }
Cary Clark8032b982017-07-28 11:04:54 -040080 return kKeyWords[index].fKeyWord;
81 }
82 }
83 return KeyWord::kNone;
84}
85
86void IncludeParser::ValidateKeyWords() {
87 for (size_t index = 1; index < kKeyWordCount; ++index) {
88 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
89 == (int) kKeyWords[index].fKeyWord);
90 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
91 }
92}
93
94void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050095 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040096 fIncludeWord = nullptr;
97 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
98 Definition* def = &fParent->fTokens.back();
99 this->addDefinition(def);
100 if (KeyWord::kEnum == fParent->fKeyWord) {
101 fInEnum = true;
102 }
103 }
104}
105
Cary Clark61313f32018-10-08 14:57:48 -0400106static bool looks_like_method(const TextParser& tp) {
107 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
108 t.skipSpace();
109 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
110 && !t.skipExact("enum")) {
111 return true;
112 }
113 t.skipSpace();
114 if (t.skipExact("SK_API")) {
115 t.skipSpace();
116 }
117 if (!isupper(t.peek())) {
118 return true;
119 }
120 return nullptr != t.strnchr('(', t.fEnd);
121}
122
123static bool looks_like_forward_declaration(const TextParser& tp) {
124 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
125 t.skipSpace();
126 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
127 && !t.skipExact("enum")) {
128 return false;
129 }
130 t.skipSpace();
131 if (t.skipExact("SK_API")) {
132 t.skipSpace();
133 }
134 if (!isupper(t.peek())) {
135 return false;
136 }
137 t.skipToNonAlphaNum();
138 if (t.eof() || ';' != t.next()) {
139 return false;
140 }
141 if (t.eof() || '\n' != t.next()) {
142 return false;
143 }
144 return t.eof();
145}
146
147static bool looks_like_constructor(const TextParser& tp) {
148 TextParser t(tp.fFileName, tp.fLine, tp.lineEnd(), tp.fLineCount);
149 t.skipSpace();
150 if (!isupper(t.peek())) {
151 if (':' == t.next() && ' ' >= t.peek()) {
152 return true;
153 }
154 return false;
155 }
156 t.skipToNonAlphaNum();
157 if ('(' != t.peek()) {
158 return false;
159 }
160 if (!t.skipToEndBracket(')')) {
161 return false;
162 }
163 SkAssertResult(')' == t.next());
164 t.skipSpace();
165 return tp.fChar == t.fChar;
166}
167
168static bool looks_like_class_decl(const TextParser& tp) {
169 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
170 t.skipSpace();
171 if (!t.skipExact("class")) {
172 return false;
173 }
174 t.skipSpace();
175 if (t.skipExact("SK_API")) {
176 t.skipSpace();
177 }
178 if (!isupper(t.peek())) {
179 return false;
180 }
181 t.skipToNonAlphaNum();
182 return !t.skipToEndBracket('(');
183}
184
185static bool looks_like_const(const TextParser& tp) {
186 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
187 if (!t.startsWith("static constexpr ")) {
188 return false;
189 }
190 if (t.skipToEndBracket(" k")) {
191 SkAssertResult(t.skipExact(" k"));
192 } else if (t.skipToEndBracket(" SK_")) {
193 SkAssertResult(t.skipExact(" SK_"));
194 } else {
195 return false;
196 }
197 if (!isupper(t.peek())) {
198 return false;
199 }
200 return t.skipToEndBracket(" = ");
201}
202
203static bool looks_like_member(const TextParser& tp) {
204 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
205 const char* end = t.anyOf("(;");
206 if (!end || '(' == *end) {
207 return false;
208 }
209 bool foundMember = false;
210 do {
211 const char* next = t.anyOf(" ;");
212 if (';' == *next) {
213 break;
214 }
215 t.skipTo(next);
216 t.skipSpace();
217 foundMember = 'f' == t.fChar[0] && isupper(t.fChar[1]);
218 } while (true);
219 return foundMember;
220}
221
222static void skip_constructor_initializers(TextParser& t) {
223 SkAssertResult(':' == t.next());
224 do {
225 t.skipWhiteSpace();
226 t.skipToNonAlphaNum();
227 t.skipWhiteSpace();
228 if ('{' == t.peek()) {
229 t.skipToBalancedEndBracket('{', '}');
230 }
231 do {
232 const char* limiter = t.anyOf("(,{");
233 t.skipTo(limiter);
234 if ('(' != t.peek()) {
235 break;
236 }
237 t.skipToBalancedEndBracket('(', ')');
238 } while (true);
239 if ('{' == t.peek()) {
240 return;
241 }
242 SkAssertResult(',' == t.next());
243 } while (true);
244}
245
246static const char kInline[] = "inline ";
247static const char kSK_API[] = "SK_API ";
248static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
249
250bool IncludeParser::advanceInclude(TextParser& i) {
251 i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn);
252 if (fCheck.fPrivateBrace) {
253 if (i.startsWith("};")) {
254 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
255 fCheck.fPrivateBrace = 0;
256 fCheck.fDoubleReturn = true;
257 } else {
258 i.skipExact("};");
259 fCheck.fBraceCount -= 1;
260 }
261 return false;
262 }
263 if (i.startsWith("public:")) {
264 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
265 fCheck.fPrivateBrace = 0;
266 if (fCheck.fPrivateProtected) {
267 i.skipExact("public:");
268 }
269 } else {
270 i.skipExact("public:");
271 }
272 } else {
273 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
274 }
275 return false;
276 } else if (i.startsWith("};")) {
277 fCheck.fDoubleReturn = 2;
278 }
279 if (i.skipExact(kInline)) {
280 fCheck.fSkipInline = true;
281 return false;
282 }
283 if (i.skipExact(kSK_API)) {
284 fCheck.fSkipAPI = true;
285 return false;
286 }
287 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
288 fCheck.fSkipWarnUnused = true;
289 return false;
290 }
291 if (i.skipExact("SK_ATTR_DEPRECATED")) {
292 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
293 return false;
294 }
295 if (i.skipExact("SkDEBUGCODE")) {
296 i.skipWhiteSpace();
297 if ('(' != i.peek()) {
298 i.reportError("expected open paren");
299 }
300 TextParserSave save(&i);
301 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
302 fCheck.fInDebugCode = i.fChar - 1;
303 save.restore();
304 SkAssertResult('(' == i.next());
305 }
306 if ('{' == i.peek()) {
307 if (looks_like_method(i)) {
308 fCheck.fState = CheckCode::State::kMethod;
309 if (!i.skipToBalancedEndBracket('{', '}')) {
310 i.reportError("unbalanced open brace");
311 }
312 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
313 return false;
314 } else if (looks_like_class_decl(i)) {
315 fCheck.fState = CheckCode::State::kClassDeclaration;
316 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
317 fCheck.fPrivateProtected = false;
318 }
319 }
320 if (':' == i.peek() && looks_like_constructor(i)) {
321 fCheck.fState = CheckCode::State::kConstructor;
322 skip_constructor_initializers(i);
323 return false;
324 }
325 if ('#' == i.peek()) {
326 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
327 return false;
328 }
329 if (i.startsWith("//")) {
330 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
331 return false;
332 }
333 if (i.startsWith("/*")) {
334 i.skipToEndBracket("*/");
335 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
336 return false;
337 }
338 if (looks_like_forward_declaration(i)) {
339 fCheck.fState = CheckCode::State::kForwardDeclaration;
340 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
341 return false;
342 }
343 if (i.skipExact("private:") || i.skipExact("protected:")) {
344 if (!fCheck.fBraceCount) {
345 i.reportError("expect private in brace");
346 }
347 fCheck.fPrivateBrace = fCheck.fBraceCount;
348 fCheck.fPrivateProtected = true;
349 return false;
350 }
351 const char* funcEnd = i.anyOf("(\n");
352 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
353 && (i.contains("internal_", funcEnd, nullptr)
354 || i.contains("private_", funcEnd, nullptr)
355 || i.contains("legacy_", funcEnd, nullptr)
356 || i.contains("temporary_", funcEnd, nullptr))) {
357 i.skipTo(funcEnd);
358 if (!i.skipToBalancedEndBracket('(', ')')) {
359 i.reportError("unbalanced open parent");
360 }
361 i.skipSpace();
362 i.skipExact("const ");
363 i.skipSpace();
364 if (';' == i.peek()) {
365 i.next();
366 }
367 fCheck.fState = CheckCode::State::kNone;
368 return false;
369 }
370 return true;
371}
372
373void IncludeParser::codeBlockAppend(string& result, char ch) const {
374 if (Elided::kYes == fElided && fCheck.fBraceCount) {
375 return;
376 }
377 this->stringAppend(result, ch);
378}
379
380void IncludeParser::codeBlockSpaces(string& result, int indent) const {
381 if (!indent) {
382 return;
383 }
384 if (Elided::kYes == fElided && fCheck.fBraceCount) {
385 return;
386 }
387 SkASSERT(indent > 0);
388 if (fDebugWriteCodeBlock) {
389 SkDebugf("%*c", indent, ' ');
390 }
391 result.append(indent, ' ');
392}
393
394string IncludeParser::writeCodeBlock(const Definition& iDef, MarkType markType) {
395 TextParser i(&iDef);
396 fElided = Elided::kNo;
397 const char* before = iDef.fContentStart;
398 while (' ' == *--before)
399 ;
400 int startIndent = iDef.fContentStart - before - 1;
401 return writeCodeBlock(i, markType, startIndent);
402}
403
404string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
405 string result;
406 char last;
407 int lastIndent = 0;
408 bool lastDoubleMeUp = false;
409 fCheck.reset();
Cary Clarka90ea222018-10-16 10:30:28 -0400410 if (MarkType::kDefine == markType) {
411 result = "#define ";
412 } else {
413 this->codeBlockSpaces(result, startIndent);
414 }
Cary Clark61313f32018-10-08 14:57:48 -0400415 do {
416 if (!this->advanceInclude(i)) {
417 continue;
418 }
419 do {
420 last = i.peek();
421 SkASSERT(' ' < last);
422 if (fCheck.fInDebugCode == i.fChar) {
423 fCheck.fInDebugCode = nullptr;
424 i.next(); // skip close paren
425 break;
426 }
427 if (CheckCode::State::kMethod == fCheck.fState) {
428 this->codeBlockAppend(result, ';');
429 fCheck.fState = CheckCode::State::kNone;
430 }
431 if (fCheck.fWriteReturn) {
432 this->codeBlockAppend(result, '\n');
433 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
434 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
435 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
436 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
437 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
438 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
439 this->codeBlockAppend(result, '\n');
440 }
441 fCheck.fTypedefReturn = false;
442 lastDoubleMeUp = doubleMeUp;
443 }
444 if (doubleMeUp) {
445 fCheck.fTypedefReturn = true;
446 }
447 lastIndent = fCheck.fIndent;
448 }
449 if (fCheck.fIndent) {
450 size_t indent = fCheck.fIndent;
451 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
452 indent -= sizeof(kInline) - 1;
453 }
454 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
455 indent -= sizeof(kSK_API) - 1;
456 }
457 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
458 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
459 }
460
461 this->codeBlockSpaces(result, indent);
462 }
463 this->codeBlockAppend(result, last);
464 fCheck.fWriteReturn = false;
465 fCheck.fIndent = 0;
466 fCheck.fBraceCount += '{' == last;
467 fCheck.fBraceCount -= '}' == last;
468 if (';' == last) {
469 fCheck.fSkipInline = false;
470 fCheck.fSkipAPI = false;
471 fCheck.fSkipWarnUnused = false;
472 }
473 if (fCheck.fBraceCount < 0) {
474 i.reportError("unbalanced close brace");
475 return result;
476 }
477 i.next();
478 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
479 } while (!i.eof());
480 if (CheckCode::State::kMethod == fCheck.fState) {
481 this->codeBlockAppend(result, ';');
482 }
483 bool elidedTemplate = Elided::kYes == fElided && !strncmp(i.fStart, "template ", 9);
484 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
485 if (fCheck.fWriteReturn || elidedTClass) {
486 this->codeBlockAppend(result, '\n');
487 }
488 if ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass) {
489 this->codeBlockAppend(result, '}');
490 }
491 this->codeBlockAppend(result, ';');
492 if (MarkType::kFunction != markType || elidedTemplate) {
493 this->codeBlockAppend(result, '\n');
494 }
495 return result;
496}
497
Ben Wagner63fd7602017-10-09 15:45:33 -0400498void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400499 const vector<string>& foundParams) {
500 for (auto& methodParam : methodParams) {
501 bool found = false;
502 for (auto& foundParam : foundParams) {
503 if (methodParam == foundParam) {
504 found = true;
505 break;
506 }
507 }
508 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400509 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400510 }
511 }
512 for (auto& foundParam : foundParams) {
513 bool found = false;
514 for (auto& methodParam : methodParams) {
515 if (methodParam == foundParam) {
516 found = true;
517 break;
518 }
519 }
520 if (!found) {
521 this->reportError("doxygen param does not match method declaration");
522 }
523 }
524}
525
526bool IncludeParser::checkForWord() {
527 if (!fIncludeWord) {
528 return true;
529 }
530 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400531 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
532 Bracket bracket = this->topBracket();
533 if (Bracket::kParen == bracket) {
534 return true;
535 }
536 }
Cary Clark8032b982017-07-28 11:04:54 -0400537 if (KeyWord::kNone != keyWord) {
538 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
539 this->addKeyword(keyWord);
540 return true;
541 }
542 } else {
543 this->addWord();
544 return true;
545 }
546 Definition* poundDef = fParent;
547 if (!fParent) {
548 return reportError<bool>("expected parent");
549 }
550 if (Definition::Type::kBracket != poundDef->fType) {
551 return reportError<bool>("expected bracket");
552 }
553 if (Bracket::kPound != poundDef->fBracket) {
554 return reportError<bool>("expected preprocessor");
555 }
556 if (KeyWord::kNone != poundDef->fKeyWord) {
557 return reportError<bool>("already found keyword");
558 }
559 poundDef->fKeyWord = keyWord;
560 fIncludeWord = nullptr;
561 switch (keyWord) {
562 // these do not link to other # directives
563 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400564 if (!fInBrace) {
565 SkASSERT(!fInDefine);
566 fInDefine = true;
567 }
Cary Clark8032b982017-07-28 11:04:54 -0400568 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500569 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400570 break;
571 // these start a # directive link
572 case KeyWord::kIf:
573 case KeyWord::kIfdef:
574 case KeyWord::kIfndef:
575 break;
576 // these continue a # directive link
577 case KeyWord::kElif:
578 case KeyWord::kElse: {
579 this->popObject(); // pop elif
580 if (Bracket::kPound != fParent->fBracket) {
581 return this->reportError<bool>("expected preprocessor directive");
582 }
583 this->popBracket(); // pop if
584 poundDef->fParent = fParent;
585 this->addDefinition(poundDef); // push elif back
586 } break;
587 // this ends a # directive link
588 case KeyWord::kEndif:
589 // FIXME : should this be calling popBracket() instead?
590 this->popObject(); // pop endif
591 if (Bracket::kPound != fParent->fBracket) {
592 return this->reportError<bool>("expected preprocessor directive");
593 }
594 this->popBracket(); // pop if/else
595 break;
596 default:
597 SkASSERT(0);
598 }
599 return true;
600}
601
602string IncludeParser::className() const {
603 string name(fParent->fName);
604 size_t slash = name.find_last_of("/");
605 if (string::npos == slash) {
606 slash = name.find_last_of("\\");
607 }
608 SkASSERT(string::npos != slash);
609 string result = name.substr(slash);
610 result = result.substr(1, result.size() - 3);
611 return result;
612}
613
Cary Clarka90ea222018-10-16 10:30:28 -0400614void IncludeParser::writeCodeBlock() {
Cary Clark61313f32018-10-08 14:57:48 -0400615 for (auto& classMapper : fIClassMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400616 classMapper.second.fCode = this->writeCodeBlock(classMapper.second, MarkType::kClass);
Cary Clark61313f32018-10-08 14:57:48 -0400617 }
618 for (auto& enumMapper : fIEnumMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400619 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second,
620 enumMapper.second->fMarkType);
621 }
622 for (auto& typedefMapper : fITypedefMap) {
623 typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second,
624 typedefMapper.second->fMarkType);
625 }
626 for (auto& defineMapper : fIDefineMap) {
627 defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second,
628 defineMapper.second->fMarkType);
Cary Clark61313f32018-10-08 14:57:48 -0400629 }
630}
631
Cary Clark884dd7d2017-10-11 10:37:52 -0400632#include <sstream>
633#include <iostream>
634
Cary Clark8032b982017-07-28 11:04:54 -0400635bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400636 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400637 string className = classMapper.first;
638 auto finder = bmhParser.fClassMap.find(className);
639 if (bmhParser.fClassMap.end() == finder) {
640 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400641 continue;
642 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400643 }
644 for (auto& classMapper : fIClassMap) {
645 string className = classMapper.first;
646 std::istringstream iss(className);
647 string classStr;
648 string classBase;
649 RootDefinition* root = nullptr;
650 while (std::getline(iss, classStr, ':')) {
651 if (root) {
652 if (!classStr.length()) {
653 continue;
654 }
655 classBase += "::" + classStr;
656 auto finder = root->fBranches.find(classBase);
657 if (root->fBranches.end() != finder) {
658 root = finder->second;
659 } else {
660 SkASSERT(0);
661 }
662 } else {
663 classBase = classStr;
664 auto finder = bmhParser.fClassMap.find(classBase);
665 if (bmhParser.fClassMap.end() != finder) {
666 root = &finder->second;
667 } else {
668 SkASSERT(0);
669 }
670 }
671 }
Cary Clark8032b982017-07-28 11:04:54 -0400672 auto& classMap = classMapper.second;
673 auto& tokens = classMap.fTokens;
674 for (const auto& token : tokens) {
675 if (token.fPrivate) {
676 continue;
677 }
678 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400679 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400680 switch (token.fMarkType) {
681 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400682 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400683 continue;
684 }
Cary Clark8032b982017-07-28 11:04:54 -0400685 if (!def) {
686 string paramName = className + "::";
687 paramName += string(token.fContentStart,
688 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400689 if (string::npos != paramName.find('\n')) {
690 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
691 paramName.end());
692 }
Cary Clarkce101242017-09-01 15:51:02 -0400693 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400694 if (!def && 0 == token.fName.find("operator")) {
695 string operatorName = className + "::";
696 TextParser oper("", token.fStart, token.fContentEnd, 0);
697 const char* start = oper.strnstr("operator", token.fContentEnd);
698 SkASSERT(start);
699 oper.skipTo(start);
700 oper.skipToEndBracket('(');
701 int parens = 0;
702 do {
703 if ('(' == oper.peek()) {
704 ++parens;
705 } else if (')' == oper.peek()) {
706 --parens;
707 }
708 } while (!oper.eof() && oper.next() && parens > 0);
709 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400710 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400711 }
712 }
713 if (!def) {
714 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
715 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400716 const char* tokenEnd = token.methodEnd();
Cary Clark8032b982017-07-28 11:04:54 -0400717 string constructorName = className + "::";
718 constructorName += string(token.fContentStart + skip,
Cary Clarkab5c9af2018-07-12 16:24:53 -0400719 tokenEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400720 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400721 }
722 if (!def && 0 == token.fName.find("SK_")) {
723 string incName = token.fName + "()";
724 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400725 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400726 if (def) {
727 if (def->fName == incName) {
728 def->fVisited = true;
729 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400730 def = root->find(className + "::toString",
731 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400732 if (def) {
733 def->fVisited = true;
734 } else {
735 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500736 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400737 }
738 }
739 break;
740 } else {
741 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500742 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400743 }
744 }
745 }
746 if (!def) {
747 bool allLower = true;
748 for (size_t index = 0; index < token.fName.length(); ++index) {
749 if (!islower(token.fName[index])) {
750 allLower = false;
751 break;
752 }
753 }
754 if (allLower) {
755 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400756 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400757 }
758 }
759 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400760 if (0 == token.fName.find("SkDEBUGCODE")) {
761 break;
762 }
763 }
764 if (!def) {
765 // simple method names inside nested classes have a bug and are missing trailing parens
766 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400767 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400768 }
769 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500770 if (!root->fDeprecated) {
771 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
772 fFailed = true;
773 }
Cary Clark8032b982017-07-28 11:04:54 -0400774 break;
775 }
Cary Clark73fa9722017-08-29 17:36:51 -0400776 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400777 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400778 if (token.fDeprecated && !def->fDeprecated) {
779 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
780 }
Cary Clark8032b982017-07-28 11:04:54 -0400781 } else {
782 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500783 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400784 }
785 } break;
786 case MarkType::kComment:
787 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400788 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400789 case MarkType::kEnum: {
790 if (!def) {
791 // work backwards from first word to deduce #Enum name
792 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
793 SkAssertResult(firstMember.skipName("enum"));
794 SkAssertResult(firstMember.skipToEndBracket('{'));
795 firstMember.next();
796 firstMember.skipWhiteSpace();
797 SkASSERT('k' == firstMember.peek());
798 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400799 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400800 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400801 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400802 const char* lastUnderscore = nullptr;
803 do {
804 if (!firstMember.skipToEndBracket('_')) {
805 break;
806 }
807 if (firstMember.fChar > wordEnd) {
808 break;
809 }
810 lastUnderscore = firstMember.fChar;
811 } while (firstMember.next());
812 if (lastUnderscore) {
813 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400814 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400815 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400816 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400817 }
818 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500819 if (!root->fDeprecated) {
820 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
821 fFailed = true;
822 }
Cary Clark8032b982017-07-28 11:04:54 -0400823 break;
824 }
825 }
826 def->fVisited = true;
Cary Clark61313f32018-10-08 14:57:48 -0400827 bool hasCode = false;
828 bool hasPopulate = true;
Cary Clark8032b982017-07-28 11:04:54 -0400829 for (auto& child : def->fChildren) {
830 if (MarkType::kCode == child->fMarkType) {
Cary Clark61313f32018-10-08 14:57:48 -0400831 hasPopulate = std::any_of(child->fChildren.begin(),
832 child->fChildren.end(), [](auto grandChild){
833 return MarkType::kPopulate == grandChild->fMarkType; });
834 if (!hasPopulate) {
835 def = child;
836 }
837 hasCode = true;
Cary Clark8032b982017-07-28 11:04:54 -0400838 break;
839 }
840 }
Cary Clark61313f32018-10-08 14:57:48 -0400841 if (!hasCode) {
Cary Clark56356312018-02-08 14:45:18 -0500842 if (!root->fDeprecated) {
843 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
844 fFailed = true;
845 }
Cary Clark8032b982017-07-28 11:04:54 -0400846 break;
847 }
Cary Clark61313f32018-10-08 14:57:48 -0400848 if (!hasPopulate) {
849 if (def->crossCheck(token)) {
850 def->fVisited = true;
851 } else {
852 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
853 fFailed = true;
854 }
Cary Clark8032b982017-07-28 11:04:54 -0400855 }
856 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400857 string constName = MarkType::kEnumClass == token.fMarkType ?
858 fullName : className;
859 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400860 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400861 if (!def) {
862 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400863 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400864 }
865 if (!def) {
866 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500867 if (!root->fDeprecated) {
868 SkDebugf("const missing from bmh: %s\n", constName.c_str());
869 fFailed = true;
870 }
Cary Clark8032b982017-07-28 11:04:54 -0400871 }
872 } else {
873 def->fVisited = true;
874 }
875 }
876 } break;
877 case MarkType::kMember:
878 if (def) {
879 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500880 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400881 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500882 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400883 }
884 break;
Cary Clark2f466242017-12-11 16:03:17 -0500885 case MarkType::kTypedef:
886 if (def) {
887 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500888 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500889 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500890 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500891 }
892 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400893 case MarkType::kConst:
894 if (def) {
895 def->fVisited = true;
896 } else if (!root->fDeprecated) {
897 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
898 fFailed = true;
899 }
900 break;
Cary Clark8032b982017-07-28 11:04:54 -0400901 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400902 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400903 break;
904 }
905 }
906 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500907 int crossChecks = 0;
908 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400909 for (auto& classMapper : fIClassMap) {
910 string className = classMapper.first;
911 auto finder = bmhParser.fClassMap.find(className);
912 if (bmhParser.fClassMap.end() == finder) {
913 continue;
914 }
915 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500916 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500917 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400918 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500919 if (crossChecks) {
920 SkDebugf(".");
921 } else {
922 SkDebugf("cross-check");
923 firstCheck = className;
924 }
925 ++crossChecks;
926 }
927 if (crossChecks) {
928 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500929 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500930 }
931 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400932 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400933 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500934 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400935}
936
937IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400938 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400939 string className;
940 const Definition* test = fParent;
941 while (Definition::Type::kFileType != test->fType) {
942 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
943 className = test->fName + "::";
944 break;
945 }
946 test = test->fParent;
947 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400948 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400949 unordered_map<string, IClassDefinition>& map = fIClassMap;
950 IClassDefinition& markupDef = map[className];
951 if (markupDef.fStart) {
952 typedef IClassDefinition* IClassDefPtr;
953 return INHERITED::reportError<IClassDefPtr>("class already defined");
954 }
955 markupDef.fFileName = fFileName;
956 markupDef.fStart = includeDef.fStart;
957 markupDef.fContentStart = includeDef.fStart;
958 markupDef.fName = className;
959 markupDef.fContentEnd = includeDef.fContentEnd;
960 markupDef.fTerminator = includeDef.fTerminator;
961 markupDef.fParent = fParent;
962 markupDef.fLineCount = fLineCount;
963 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
964 MarkType::kStruct : MarkType::kClass;
965 markupDef.fKeyWord = includeDef.fKeyWord;
966 markupDef.fType = Definition::Type::kMark;
967 fParent = &markupDef;
968 return &markupDef;
969}
970
971void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
972 auto& tokens = classDef.fTokens;
973 for (auto& token : tokens) {
974 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
975 continue;
976 }
977 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400978 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400979 }
980 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400981 case MarkType::kConst:
982 this->dumpConst(token, classDef.fName);
983 break;
Cary Clark8032b982017-07-28 11:04:54 -0400984 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500985 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500986 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400987 break;
988 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400989 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400990 break;
991 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400992 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400993 continue;
994 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400995 case MarkType::kTypedef:
996 this->dumpTypedef(token, classDef.fName);
997 break;
Cary Clark8032b982017-07-28 11:04:54 -0400998 default:
999 SkASSERT(0);
1000 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001001 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -04001002 }
1003}
Cary Clark9174bda2017-09-19 17:39:32 -04001004void IncludeParser::dumpComment(const Definition& token) {
1005 fLineCount = token.fLineCount;
1006 fChar = fLine = token.fContentStart;
1007 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001008 bool sawParam = false;
1009 bool multiline = false;
1010 bool sawReturn = false;
1011 bool sawComment = false;
1012 bool methodHasReturn = false;
1013 vector<string> methodParams;
1014 vector<string> foundParams;
1015 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -04001016 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
1017 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001018 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -04001019 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001020 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -04001021 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -04001022 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -05001023 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -04001024 && !methodParser.strnchr('~', methodParser.fEnd);
1025 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
1026 const char* nextEnd = paren;
1027 do {
1028 string paramName;
1029 methodParser.fChar = nextEnd + 1;
1030 methodParser.skipSpace();
1031 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
1032 continue;
1033 }
1034 methodParams.push_back(paramName);
1035 } while (')' != nextEnd[0]);
1036 }
Cary Clark9174bda2017-09-19 17:39:32 -04001037 for (const auto& child : token.fTokens) {
1038 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1039 break;
1040 }
Cary Clark8032b982017-07-28 11:04:54 -04001041 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001042 if (child.fPrivate) {
1043 break;
1044 }
Cary Clark8032b982017-07-28 11:04:54 -04001045 if ('@' == child.fContentStart[0]) {
1046 TextParser parser(&child);
1047 do {
1048 parser.next();
1049 if (parser.startsWith("param ")) {
1050 parser.skipWord("param");
1051 const char* parmStart = parser.fChar;
1052 parser.skipToSpace();
1053 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
1054 parser.skipWhiteSpace();
1055 do {
1056 size_t nextComma = parmName.find(',');
1057 string piece;
1058 if (string::npos == nextComma) {
1059 piece = parmName;
1060 parmName = "";
1061 } else {
1062 piece = parmName.substr(0, nextComma);
1063 parmName = parmName.substr(nextComma + 1);
1064 }
1065 if (sawParam) {
1066 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001067 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001068 }
Cary Clark9174bda2017-09-19 17:39:32 -04001069 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001070 } else {
1071 if (sawComment) {
1072 this->nl();
1073 }
1074 this->lf(2);
1075 }
1076 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -04001077 this->writeTag("Param", piece);
1078 this->writeSpace(2);
1079 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1080 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001081 sawParam = true;
1082 sawComment = false;
1083 } while (parmName.length());
1084 parser.skipTo(parser.fEnd);
1085 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
1086 parser.skipWord("return");
1087 if ('s' == parser.peek()) {
1088 parser.next();
1089 }
1090 if (sawParam) {
1091 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001092 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001093 }
Cary Clark9174bda2017-09-19 17:39:32 -04001094 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001095 }
1096 this->checkForMissingParams(methodParams, foundParams);
1097 sawParam = false;
1098 sawComment = false;
1099 multiline = false;
1100 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001101 this->writeTag("Return");
1102 this->writeSpace(2);
1103 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1104 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001105 sawReturn = true;
1106 parser.skipTo(parser.fEnd);
1107 } else {
1108 this->reportError("unexpected doxygen directive");
1109 }
1110 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -04001111 } else if (child.length() > 1) {
1112 const char* start = child.fContentStart;
1113 ptrdiff_t length = child.fContentEnd - start;
1114 SkASSERT(length >= 0);
1115 while (length && '/' == start[0]) {
1116 start += 1;
1117 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001118 }
Cary Clark9174bda2017-09-19 17:39:32 -04001119 while (length && '/' == start[length - 1]) {
1120 length -= 1;
1121 if (length && '*' == start[length - 1]) {
1122 length -= 1;
1123 }
1124 }
1125 if (length) {
1126 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
1127 if (sawParam || sawReturn) {
1128 this->indentToColumn(8);
1129 }
1130 this->writeBlock(length, start);
1131 this->writeSpace();
1132 sawComment = true;
1133 if (sawParam || sawReturn) {
1134 multiline = true;
1135 }
Cary Clark8032b982017-07-28 11:04:54 -04001136 }
1137 }
1138 }
1139 }
1140 if (sawParam || sawReturn) {
1141 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001142 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001143 }
Cary Clark9174bda2017-09-19 17:39:32 -04001144 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001145 }
1146 if (!sawReturn) {
1147 if (!sawParam) {
1148 if (sawComment) {
1149 this->nl();
1150 }
1151 this->lf(2);
1152 }
1153 this->checkForMissingParams(methodParams, foundParams);
1154 }
1155 if (methodHasReturn != sawReturn) {
1156 if (!methodHasReturn) {
1157 this->reportError("unexpected doxygen return");
1158 } else {
1159 if (sawComment) {
1160 this->nl();
1161 }
1162 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001163 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -04001164 }
1165 }
1166}
1167
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001168void IncludeParser::dumpCommonTail(const Definition& token) {
1169 this->lf(2);
1170 this->writeTag("Example");
1171 this->lf(1);
1172 this->writeString("// incomplete");
1173 this->lf(1);
1174 this->writeEndTag();
1175 this->lf(2);
1176 this->writeTag("SeeAlso");
1177 this->writeSpace();
1178 this->writeString("incomplete");
1179 this->lf(2);
1180 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1181 this->lf(2);
1182}
1183
Cary Clark224c7002018-06-27 11:00:21 -04001184void IncludeParser::dumpConst(const Definition& token, string className) {
1185 this->writeTag("Const");
1186 this->writeSpace();
1187 this->writeString(token.fName);
1188 this->writeTagTable("Line", "incomplete");
1189 this->lf(2);
1190 this->dumpComment(token);
1191}
1192
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001193void IncludeParser::dumpDefine(const Definition& token) {
1194 this->writeTag("Define", token.fName);
1195 this->lf(2);
1196 this->writeTag("Code");
1197 this->lfAlways(1);
1198 this->writeString("###$");
1199 this->lfAlways(1);
1200 this->indentToColumn(4);
1201 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1202 this->lf(1);
1203 this->indentToColumn(0);
1204 this->writeString("$$$#");
1205
1206 this->writeEndTag();
1207 this->lf(2);
1208 this->dumpComment(token);
1209 for (auto& child : token.fTokens) {
1210 if (MarkType::kComment == child.fMarkType) {
1211 continue;
1212 }
1213 this->writeTag("Param", child.fName);
1214 this->writeSpace();
1215 this->writeString("incomplete");
1216 this->writeSpace();
1217 this->writeString("##");
1218 this->lf(1);
1219 }
1220}
1221
1222void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001223 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -04001224 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001225 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -04001226 this->lfAlways(1);
1227 this->indentToColumn(4);
1228 this->writeString("enum");
1229 this->writeSpace();
1230 if ("_anonymous" != token.fName.substr(0, 10)) {
1231 this->writeString(token.fName);
1232 this->writeSpace();
1233 }
1234 this->writeString("{");
1235 this->lfAlways(1);
1236 for (auto& child : token.fChildren) {
1237 this->indentToColumn(8);
1238 this->writeString(child->fName);
1239 if (child->length()) {
1240 this->writeSpace();
1241 this->writeBlock(child->length(), child->fContentStart);
1242 }
1243 if (',' != fLastChar) {
1244 this->writeString(",");
1245 }
1246 this->lfAlways(1);
1247 }
1248 this->indentToColumn(4);
1249 this->writeString("};");
1250 this->lf(1);
1251 this->writeString("##");
1252 this->lf(2);
1253 this->dumpComment(token);
1254 for (auto& child : token.fChildren) {
Cary Clark61313f32018-10-08 14:57:48 -04001255 // TODO: get comments before or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001256 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001257 this->writeSpace();
1258 this->writeString(child->fName);
1259 TextParser val(child);
1260 if (!val.eof()) {
1261 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
1262 val.next();
1263 val.skipSpace();
1264 const char* valEnd = val.anyOf(",\n");
1265 if (!valEnd) {
1266 valEnd = val.fEnd;
1267 }
1268 this->writeSpace();
1269 this->writeBlock(valEnd - val.fStart, val.fStart);
1270 } else {
1271 this->writeSpace();
1272 this->writeDefinition(*child);
1273 }
1274 }
1275 this->lf(1);
1276 for (auto comment : child->fChildren) {
1277 if (MarkType::kComment == comment->fMarkType) {
1278 TextParser parser(comment);
1279 parser.skipExact("*");
1280 parser.skipExact("*");
1281 while (!parser.eof() && parser.skipWhiteSpace()) {
1282 parser.skipExact("*");
1283 parser.skipWhiteSpace();
1284 const char* start = parser.fChar;
1285 parser.skipToEndBracket('\n');
1286 this->lf(1);
1287 this->writeBlock(parser.fChar - start, start);
1288 }
1289 }
1290 }
1291 this->writeEndTag();
1292 }
1293 this->lf(2);
1294}
1295
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001296bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1297 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1298 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1299 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001300 return true;
1301 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001302 size_t lastBSlash = fFileName.rfind('\\');
1303 size_t lastSlash = fFileName.rfind('/');
1304 size_t lastDotH = fFileName.rfind(".h");
1305 SkASSERT(string::npos != lastDotH);
1306 if (string::npos != lastBSlash && (string::npos == lastSlash
1307 || lastBSlash < lastSlash)) {
1308 lastSlash = lastBSlash;
1309 } else if (string::npos == lastSlash) {
1310 lastSlash = -1;
1311 }
1312 lastSlash += 1;
1313 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1314 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001315 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001316 fOut = fopen(fileName.c_str(), "wb");
1317 if (!fOut) {
1318 SkDebugf("could not open output file %s\n", globalsName.c_str());
1319 return false;
1320 }
1321 string prefixName = globalsName.substr(0, 2);
1322 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1323 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1324 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001325 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001326 this->lf(2);
1327 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001328 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001329 this->writeEndTag();
1330 this->lf(2);
1331 if (!fIDefineMap.empty()) {
1332 this->writeTag("Subtopic", "Define");
1333 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -04001334 this->writeEndTag();
1335 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001336 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001337 if (!fIFunctionMap.empty()) {
1338 this->writeTag("Subtopic", "Function");
1339 this->writeTag("Populate");
1340 this->writeEndTag();
1341 this->lf(2);
1342 }
1343 if (!fIEnumMap.empty()) {
1344 this->writeTag("Subtopic", "Enum");
1345 this->writeTag("Populate");
1346 this->writeEndTag();
1347 this->lf(2);
1348 }
1349 if (!fITemplateMap.empty()) {
1350 this->writeTag("Subtopic", "Template");
1351 this->writeTag("Populate");
1352 this->writeEndTag();
1353 this->lf(2);
1354 }
1355 if (!fITypedefMap.empty()) {
1356 this->writeTag("Subtopic", "Typedef");
1357 this->writeTag("Populate");
1358 this->writeEndTag();
1359 this->lf(2);
1360 }
1361 if (!fIUnionMap.empty()) {
1362 this->writeTag("Subtopic", "Union");
1363 this->writeTag("Populate");
1364 this->writeEndTag();
1365 this->lf(2);
1366 }
1367 std::map<int, Definition*> sortedDefs;
1368 for (const auto& entry : fIDefineMap) {
1369 sortedDefs[entry.second->fLineCount] = entry.second;
1370 }
1371 for (const auto& entry : fIFunctionMap) {
1372 sortedDefs[entry.second->fLineCount] = entry.second;
1373 }
1374 for (const auto& entry : fIEnumMap) {
1375 sortedDefs[entry.second->fLineCount] = entry.second;
1376 }
1377 for (const auto& entry : fITemplateMap) {
1378 sortedDefs[entry.second->fLineCount] = entry.second;
1379 }
1380 for (const auto& entry : fITypedefMap) {
1381 sortedDefs[entry.second->fLineCount] = entry.second;
1382 }
1383 for (const auto& entry : fIUnionMap) {
1384 sortedDefs[entry.second->fLineCount] = entry.second;
1385 }
1386 for (const auto& entry : sortedDefs) {
1387 const Definition* def = entry.second;
1388 this->writeBlockSeparator();
1389 switch (def->fMarkType) {
1390 case MarkType::kDefine:
1391 this->dumpDefine(*def);
1392 break;
1393 case MarkType::kMethod:
1394 this->dumpMethod(*def, globalsName);
1395 break;
1396 case MarkType::kEnum:
1397 case MarkType::kEnumClass:
1398 this->dumpEnum(*def, globalsName);
1399 break;
1400 case MarkType::kTemplate:
1401 SkASSERT(0); // incomplete
1402 break;
1403 case MarkType::kTypedef: {
1404 this->writeTag("Typedef");
1405 this->writeSpace();
1406 TextParser parser(def);
1407 if (!parser.skipExact("typedef")) {
1408 return false;
1409 }
1410 if (!parser.skipSpace()) {
1411 return false;
1412 }
1413 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1414 this->lf(2);
1415 this->dumpComment(*def);
1416 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1417 this->lf(2);
1418 } continue;
1419 case MarkType::kUnion:
1420 SkASSERT(0); // incomplete
1421 break;
1422 default:
1423 SkASSERT(0);
1424 }
1425 this->dumpCommonTail(*def);
1426 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001427 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001428 this->writeEndTag("Topic", topicName);
1429 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001430// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001431 SkDebugf("wrote %s\n", fileName.c_str());
1432 return true;
1433}
1434
1435bool IncludeParser::isClone(const Definition& token) {
1436 string name = token.fName;
1437 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1438}
1439
1440bool IncludeParser::isConstructor(const Definition& token, string className) {
1441 string name = token.fName;
1442 return 0 == name.find(className) || '~' == name[0];
1443}
1444
1445bool IncludeParser::isInternalName(const Definition& token) {
1446 string name = token.fName;
1447 // exception for this SkCanvas function .. for now
1448 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1449 return false;
1450 }
1451 return name.substr(0, 7) == "android"
1452 || 0 == token.fName.find("internal_")
1453 || 0 == token.fName.find("Internal_")
1454 || 0 == token.fName.find("legacy_")
1455 || 0 == token.fName.find("temporary_")
1456 || 0 == token.fName.find("private_");
1457}
1458
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001459bool IncludeParser::isMember(const Definition& token) const {
1460 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1461 return true;
1462 }
1463 if (!islower(token.fStart[0])) {
1464 return false;
1465 }
1466 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1467 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1468 const Definition* structToken = token.fParent;
1469 if (!structToken) {
1470 return false;
1471 }
1472 if (KeyWord::kStruct != structToken->fKeyWord) {
1473 structToken = token.fParent->fParent;
1474 if (!structToken) {
1475 return false;
1476 }
1477 if (KeyWord::kStruct != structToken->fKeyWord) {
1478 return false;
1479 }
1480 }
1481 SkASSERT(structToken->fTokens.size() > 0);
1482 const Definition& child = structToken->fTokens.front();
1483 string structName(child.fContentStart, child.length());
1484 if ("RunBuffer" != structName) {
1485 return false;
1486 }
1487 string tokenName(token.fContentStart, token.length());
1488 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1489 for (auto allow : allowed) {
1490 if (allow == tokenName) {
1491 return true;
1492 }
1493 }
1494 }
1495 return false;
1496}
1497
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001498bool IncludeParser::isOperator(const Definition& token) {
1499 return "operator" == token.fName.substr(0, 8);
1500}
1501
1502void IncludeParser::dumpMethod(const Definition& token, string className) {
1503 this->writeTag("Method");
1504 this->writeSpace();
1505
1506 string name = string(token.fStart ? token.fStart : token.fContentStart,
1507 token.length());
1508 if (this->isOperator(token)) {
1509 string spaceConst(" const");
1510 size_t constPos = name.rfind(spaceConst);
1511 if (name.length() - spaceConst.length() == constPos) {
1512 name = name.substr(0, constPos) + "_const";
1513 }
1514 }
Cary Clark224c7002018-06-27 11:00:21 -04001515 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001516 string inType;
1517 if (this->isConstructor(token, className)) {
1518 inType = "Constructor";
1519 } else if (this->isOperator(token)) {
1520 inType = "Operator";
1521 } else {
1522 inType = "incomplete";
1523 }
1524 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001525 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001526 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001527 this->dumpComment(token);
1528}
1529
1530void IncludeParser::dumpMember(const Definition& token) {
1531 this->writeTag("Member");
1532 this->writeSpace();
1533 this->writeDefinition(token, token.fName, 2);
1534 lf(1);
1535 for (auto child : token.fChildren) {
1536 this->writeDefinition(*child);
1537 }
1538 this->writeEndTag();
1539 lf(2);
1540}
1541
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001542bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001543 string globalFileName;
1544 long int globalTell = 0;
1545 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001546 return false;
1547 }
Cary Clark9174bda2017-09-19 17:39:32 -04001548 for (const auto& member : fIClassMap) {
1549 if (string::npos != member.first.find("::")) {
1550 continue;
1551 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001552 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001553 return false;
1554 }
1555 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001556 if (globalTell) {
1557 fclose(fOut);
1558 }
Cary Clark9174bda2017-09-19 17:39:32 -04001559 return true;
1560}
1561
Ben Wagner63fd7602017-10-09 15:45:33 -04001562 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001563bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001564 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001565 if (globalFileName != fileName) {
1566 fOut = fopen(fileName.c_str(), "wb");
1567 if (!fOut) {
1568 SkDebugf("could not open output file %s\n", fileName.c_str());
1569 return false;
1570 }
1571 } else {
1572 fseek(fOut, *globalTell, SEEK_SET);
1573 this->lf(2);
1574 this->writeBlockSeparator();
1575 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001576 }
1577 string prefixName = skClassName.substr(0, 2);
1578 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1579 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001580 if (globalFileName != fileName) {
1581 this->writeTagNoLF("Topic", topicName);
1582 this->writeEndTag("Alias", topicName + "_Reference");
1583 this->lf(2);
1584 }
Cary Clark8032b982017-07-28 11:04:54 -04001585 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001586 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1587 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001588 this->writeTag(containerType, skClassName);
1589 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001590 auto& tokens = classMap.fTokens;
1591 for (auto& token : tokens) {
1592 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1593 continue;
1594 }
Cary Clark9174bda2017-09-19 17:39:32 -04001595 this->writeDefinition(token);
1596 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001597 }
1598 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001599 bool hasClass = false;
1600 bool hasConst = !fIEnumMap.empty();
1601 bool hasConstructor = false;
1602 bool hasMember = false;
1603 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001604 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001605 for (const auto& oneClass : fIClassMap) {
1606 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1607 continue;
1608 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001609 hasClass = true;
1610 break;
Cary Clark8032b982017-07-28 11:04:54 -04001611 }
Cary Clark224c7002018-06-27 11:00:21 -04001612 for (const auto& oneStruct : fIStructMap) {
1613 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1614 continue;
1615 }
1616 hasStruct = true;
1617 break;
1618 }
Cary Clark8032b982017-07-28 11:04:54 -04001619 for (const auto& token : classMap.fTokens) {
1620 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1621 continue;
1622 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001623 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001624 continue;
1625 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001626 if (this->isConstructor(token, skClassName)) {
1627 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001628 continue;
1629 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001630 if (this->isOperator(token)) {
1631 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001632 continue;
1633 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001634 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001635 continue;
1636 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001637 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001638 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001639 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001640 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001641 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001642 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001643
1644 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001645 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001646 this->writeTag("Populate");
1647 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001648 this->lf(2);
1649 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001650 if (hasConst) {
1651 this->writeTag("Subtopic", "Constant");
1652 this->writeTag("Populate");
1653 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001654 this->lf(2);
1655 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001656 if (hasConstructor) {
1657 this->writeTag("Subtopic", "Constructor");
1658 this->writeTag("Populate");
1659 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001660 this->lf(2);
1661 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001662 if (hasOperator) {
1663 this->writeTag("Subtopic", "Operator");
1664 this->writeTag("Populate");
1665 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001666 this->lf(2);
1667 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001668 if (hasMember) {
1669 this->writeTag("Subtopic", "Member_Function");
1670 this->writeTag("Populate");
1671 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001672 this->lf(2);
1673 }
Cary Clark224c7002018-06-27 11:00:21 -04001674 if (hasStruct) {
1675 this->writeTag("Subtopic", "Struct");
1676 this->writeTag("Populate");
1677 this->writeEndTag();
1678 this->lf(2);
1679 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001680 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001681 this->writeBlockSeparator();
1682 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001683 this->lf(2);
1684 this->writeTag("Example");
1685 this->lfcr();
1686 this->writeString("// incomplete");
1687 this->writeEndTag();
1688 this->lf(2);
1689 this->writeTag("SeeAlso", "incomplete");
1690 this->lf(2);
1691 this->writeEndTag("Enum", oneEnum.first);
1692 this->lf(2);
1693 }
Cary Clark8032b982017-07-28 11:04:54 -04001694 for (auto& oneClass : fIClassMap) {
1695 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1696 continue;
1697 }
1698 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001699 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001700 KeyWord keyword = oneClass.second.fKeyWord;
1701 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1702 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001703 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001704 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001705 this->lf(2);
1706 this->writeTag("Code");
1707 this->writeEndTag("ToDo", "fill this in manually");
1708 this->writeEndTag();
1709 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001710 for (auto& token : oneClass.second.fTokens) {
1711 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1712 continue;
1713 }
Cary Clark9174bda2017-09-19 17:39:32 -04001714 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001715 }
1716 this->lf(2);
1717 this->dumpClassTokens(oneClass.second);
1718 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001719 this->writeEndTag(containerType, innerName);
1720 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001721 }
1722 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001723 this->writeEndTag(containerType, skClassName);
1724 this->lf(2);
1725 this->writeEndTag("Topic", topicName);
1726 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001727 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001728 SkDebugf("wrote %s\n", fileName.c_str());
1729 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001730}
1731
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001732void IncludeParser::dumpTypedef(const Definition& token, string className) {
1733 this->writeTag("Typedef");
1734 this->writeSpace();
1735 this->writeString(token.fName);
1736 this->writeTagTable("Line", "incomplete");
1737 this->lf(2);
1738 this->dumpComment(token);
1739}
1740
Cary Clark61313f32018-10-08 14:57:48 -04001741string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1742 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1743 || KeyWord::kTemplate == iDef.fKeyWord);
1744 TextParser i(&iDef);
1745 fElided = Elided::kYes;
1746 MarkType markType = MarkType::kClass;
1747 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1748 for (auto child : iDef.fChildren) {
1749 if (MarkType::kMethod == child->fMarkType) {
1750 markType = MarkType::kFunction;
1751 break;
1752 }
1753 }
1754 }
1755 return this->writeCodeBlock(i, markType, 0);
1756}
1757
Cary Clarka90ea222018-10-16 10:30:28 -04001758 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1759 string result;
1760 const unordered_map<string, Definition*>* mapPtr = nullptr;
1761 MarkType markType = MarkType::kNone;
1762 if ("Constant" == inContents) {
1763 mapPtr = &fIConstMap;
1764 markType = MarkType::kConst;
1765 } else {
1766 SkASSERT(0); // only Constant supported for now
1767 }
Cary Clark4b076532018-10-29 14:17:22 -04001768 vector<Definition*> consts;
Cary Clarka90ea222018-10-16 10:30:28 -04001769 for (auto entry : *mapPtr) {
1770 if (string::npos == entry.first.find(filterContents)) {
1771 continue;
1772 }
Cary Clark4b076532018-10-29 14:17:22 -04001773 consts.push_back(entry.second);
1774 }
1775 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1776 return def1->fLineCount < def2->fLineCount;
1777 } );
1778 for (auto oneConst : consts) {
1779 result += this->writeCodeBlock(*oneConst, markType);
Cary Clarka90ea222018-10-16 10:30:28 -04001780 }
1781 return result;
1782}
1783
Cary Clark8032b982017-07-28 11:04:54 -04001784bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1785 // add comment preceding class, if any
1786 const Definition* parent = includeDef.fParent;
1787 int index = includeDef.fParentIndex;
1788 auto wordIter = parent->fTokens.begin();
1789 std::advance(wordIter, index);
1790 SkASSERT(&*wordIter == &includeDef);
1791 while (parent->fTokens.begin() != wordIter) {
1792 auto testIter = std::prev(wordIter);
1793 if (Definition::Type::kWord != testIter->fType
1794 && Definition::Type::kKeyWord != testIter->fType
1795 && (Definition::Type::kBracket != testIter->fType
1796 || Bracket::kAngle != testIter->fBracket)
1797 && (Definition::Type::kPunctuation != testIter->fType
1798 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1799 break;
1800 }
1801 wordIter = testIter;
1802 }
1803 auto commentIter = wordIter;
1804 while (parent->fTokens.begin() != commentIter) {
1805 auto testIter = std::prev(commentIter);
1806 bool isComment = Definition::Type::kBracket == testIter->fType
1807 && (Bracket::kSlashSlash == testIter->fBracket
1808 || Bracket::kSlashStar == testIter->fBracket);
1809 if (!isComment) {
1810 break;
1811 }
1812 commentIter = testIter;
1813 }
1814 while (commentIter != wordIter) {
1815 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1816 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1817 return false;
1818 }
1819 commentIter = std::next(commentIter);
1820 }
1821 return true;
1822}
1823
Cary Clark0d225392018-06-07 09:59:07 -04001824Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1825 string typeName) {
1826 typedef Definition* DefinitionPtr;
1827 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1828 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1829 if (mapIter == fMaps.end()) {
1830 return nullptr;
1831 }
1832 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1833 return reportError<DefinitionPtr>("invalid mark type");
1834 }
1835 string name = this->uniqueName(*mapIter->fInclude, typeName);
1836 Definition& markupDef = *(*mapIter->fInclude)[name];
1837 if (markupDef.fStart) {
1838 return reportError<DefinitionPtr>("definition already defined");
1839 }
1840 markupDef.fFileName = fFileName;
1841 markupDef.fStart = includeDef.fStart;
1842 markupDef.fContentStart = includeDef.fStart;
1843 markupDef.fName = name;
1844 markupDef.fContentEnd = includeDef.fContentEnd;
1845 markupDef.fTerminator = includeDef.fTerminator;
1846 markupDef.fParent = fParent;
1847 markupDef.fLineCount = includeDef.fLineCount;
1848 markupDef.fMarkType = markType;
1849 markupDef.fKeyWord = includeDef.fKeyWord;
1850 markupDef.fType = Definition::Type::kMark;
1851 return &markupDef;
1852}
1853
Cary Clarka64e4ee2018-10-18 08:30:34 -04001854Definition* IncludeParser::findMethod(const Definition& bmhDef) {
Cary Clark09d80c02018-10-31 12:14:03 -04001855 auto doubleColon = bmhDef.fName.rfind("::");
1856 if (string::npos == doubleColon) {
1857 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1858 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1859 return iGlobalMethod->second;
1860 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001861 string className = bmhDef.fName.substr(0, doubleColon);
1862 const auto& iClass = fIClassMap.find(className);
1863 SkASSERT(fIClassMap.end() != iClass);
1864 string methodName = bmhDef.fName.substr(doubleColon + 2);
1865 auto& iTokens = iClass->second.fTokens;
1866 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1867 [methodName](Definition& token) {
Cary Clark09d80c02018-10-31 12:14:03 -04001868 return MarkType::kMethod == token.fMarkType
1869 && (methodName == token.fName
1870 || methodName == token.fName + "()"); } );
1871 if (iTokens.end() != iMethod) {
1872 return &*iMethod;
1873 }
1874 size_t subClassPos = className.rfind("::");
1875 if (string::npos != subClassPos) {
1876 className = className.substr(subClassPos + 2);
1877 }
1878 // match may be constructor; compare strings to see if this is so
1879 SkASSERT(string::npos != methodName.find('('));
1880 auto stripper = [](string s) -> string {
1881 bool last = false;
1882 string result;
1883 for (char c : s) {
1884 if (' ' >= c) {
1885 if (!last) {
1886 last = true;
1887 result += ' ';
1888 }
1889 continue;
1890 }
1891 result += c;
1892 last = false;
1893 }
1894 return result;
1895 };
1896 string strippedMethodName = stripper(methodName);
1897 if (strippedMethodName == methodName) {
1898 strippedMethodName = "";
1899 }
1900 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1901 [className, methodName, stripper, strippedMethodName](Definition& token) {
1902 if (MarkType::kMethod != token.fMarkType) {
1903 return false;
1904 }
1905 TextParser parser(&token);
1906 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1907 if (!match) {
1908 return false;
1909 }
1910 parser.skipTo(match);
1911 parser.skipExact(className.c_str());
1912 if ('(' != parser.peek()) {
1913 return false;
1914 }
1915 parser.skipToBalancedEndBracket('(', ')');
1916 string iMethodName(match, parser.fChar - match);
1917 if (methodName == iMethodName) {
1918 return true;
1919 }
1920 if ("" == strippedMethodName) {
1921 return false;
1922 }
1923 string strippedIName = stripper(iMethodName);
1924 return strippedIName == strippedMethodName;
1925 } );
1926 SkAssertResult(iTokens.end() != cMethod);
1927 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04001928}
1929
Cary Clark224c7002018-06-27 11:00:21 -04001930Definition* IncludeParser::parentBracket(Definition* parent) const {
1931 while (parent && Definition::Type::kBracket != parent->fType) {
1932 parent = parent->fParent;
1933 }
1934 return parent;
1935}
1936
1937Bracket IncludeParser::grandParentBracket() const {
1938 Definition* parent = parentBracket(fParent);
1939 parent = parentBracket(parent ? parent->fParent : nullptr);
1940 return parent ? parent->fBracket : Bracket::kNone;
1941}
1942
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001943bool IncludeParser::inAlignAs() const {
1944 if (fParent->fTokens.size() < 2) {
1945 return false;
1946 }
1947 auto reverseIter = fParent->fTokens.end();
1948 bool checkForBracket = true;
1949 while (fParent->fTokens.begin() != reverseIter) {
1950 std::advance(reverseIter, -1);
1951 if (checkForBracket) {
1952 if (Definition::Type::kBracket != reverseIter->fType) {
1953 return false;
1954 }
1955 if (Bracket::kParen != reverseIter->fBracket) {
1956 return false;
1957 }
1958 checkForBracket = false;
1959 continue;
1960 }
1961 if (Definition::Type::kKeyWord != reverseIter->fType) {
1962 return false;
1963 }
1964 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1965 }
1966 return false;
1967}
1968
Cary Clark61313f32018-10-08 14:57:48 -04001969const Definition* IncludeParser::include(string match) const {
1970 for (auto& entry : fIncludeMap) {
1971 if (string::npos == entry.first.find(match)) {
1972 continue;
1973 }
1974 return &entry.second;
1975 }
1976 SkASSERT(0);
1977 return nullptr;
1978}
1979
Cary Clark137b8742018-05-30 09:21:49 -04001980// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001981bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1982 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001983 // parse class header
1984 auto iter = includeDef->fTokens.begin();
1985 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1986 // todo : documentation is ignoring this for now
1987 iter = std::next(iter);
1988 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001989 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1990 if (hasAlignAs) {
1991 iter = std::next(iter);
1992 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1993 return includeDef->reportError<bool>("expected alignas argument");
1994 }
1995 iter = std::next(iter);
1996 }
Cary Clark8032b982017-07-28 11:04:54 -04001997 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1998 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001999 iter = std::next(iter);
2000 if (iter == includeDef->fTokens.end()) {
2001 return true; // forward declaration only
2002 }
Cary Clark8032b982017-07-28 11:04:54 -04002003 do {
2004 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002005 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04002006 }
2007 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
2008 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002009 }
Cary Clark8032b982017-07-28 11:04:54 -04002010 } while (static_cast<void>(iter = std::next(iter)), true);
2011 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04002012 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04002013 }
2014 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
2015 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04002016 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04002017 }
2018 markupDef->fStart = iter->fStart;
2019 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002020 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04002021 }
2022// if (1 != includeDef->fChildren.size()) {
2023// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
2024// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002025 auto includeDefIter = includeDef->fChildren.begin();
2026 if (hasAlignAs) {
2027 SkASSERT(includeDef->fChildren.end() != includeDefIter);
2028 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2029 std::advance(includeDefIter, 1);
2030 }
2031 if (includeDef->fChildren.end() != includeDefIter
2032 && Bracket::kAngle == (*includeDefIter)->fBracket) {
2033 std::advance(includeDefIter, 1);
2034 }
2035 includeDef = *includeDefIter;
2036 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04002037 iter = includeDef->fTokens.begin();
2038 // skip until public
2039 int publicIndex = 0;
2040 if (IsStruct::kNo == isStruct) {
2041 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2042 size_t publicLen = strlen(publicName);
2043 while (iter != includeDef->fTokens.end()
2044 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2045 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002046 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002047 iter = std::next(iter);
2048 ++publicIndex;
2049 }
2050 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002051 int keyIndex = publicIndex;
2052 KeyWord currentKey = KeyWord::kPublic;
2053 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2054 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002055 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2056 size_t protectedLen = strlen(protectedName);
2057 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2058 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002059 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002060 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002061 std::advance(childIter, 1);
2062 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002063 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002064 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002065 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002066 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002067 const char* testStart = iter->fStart;
2068 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2069 iter = std::next(iter);
2070 ++keyIndex;
2071 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2072 currentKey = KeyWord::kPublic;
2073 break;
2074 }
2075 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2076 currentKey = KeyWord::kProtected;
2077 break;
2078 }
2079 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2080 currentKey = KeyWord::kPrivate;
2081 break;
2082 }
2083 }
2084 fLastObject = nullptr;
2085 if (KeyWord::kPublic == currentKey) {
2086 if (!this->parseObject(child, markupDef)) {
2087 return false;
2088 }
Cary Clark8032b982017-07-28 11:04:54 -04002089 }
Cary Clark73fa9722017-08-29 17:36:51 -04002090 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002091 childIter = std::next(childIter);
2092 }
Cary Clark137b8742018-05-30 09:21:49 -04002093 while (iter != includeDef->fTokens.end()) {
2094 iter->fPrivate = KeyWord::kPublic != currentKey;
2095 iter = std::next(iter);
2096 }
Cary Clark8032b982017-07-28 11:04:54 -04002097 SkASSERT(fParent->fParent);
2098 fParent = fParent->fParent;
2099 return true;
2100}
2101
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002102bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04002103 int lineCount, Definition* markupDef) {
2104 TextParser parser(filename, start, end, lineCount);
2105 // parse doxygen if present
2106 if (parser.startsWith("**")) {
2107 parser.next();
2108 parser.next();
2109 parser.skipWhiteSpace();
2110 if ('\\' == parser.peek()) {
2111 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002112 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2113 if (parser.skipExact("file")) {
2114 if (Definition::Type::kFileType != fParent->fType) {
2115 return reportError<bool>("expected parent is file");
2116 }
2117 string filename = markupDef->fileName();
2118 if (!parser.skipWord(filename.c_str())) {
2119 return reportError<bool>("missing object type");
2120 }
2121 } else if (parser.skipExact("fn")) {
2122 SkASSERT(0); // incomplete
2123 } else {
2124 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2125 return reportError<bool>("missing object type");
2126 }
2127 if (!parser.skipWord(markupDef->fName.c_str()) &&
2128 KeyWord::kEnum != markupDef->fKeyWord) {
2129 return reportError<bool>("missing object name");
2130 }
Cary Clark8032b982017-07-28 11:04:54 -04002131 }
Cary Clark8032b982017-07-28 11:04:54 -04002132 }
2133 }
2134 // remove leading '*' if present
2135 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2136 while (!parser.eof() && parser.skipWhiteSpace()) {
2137 while ('*' == parser.peek()) {
2138 parser.next();
2139 if (parser.eof()) {
2140 break;
2141 }
2142 parser.skipWhiteSpace();
2143 }
2144 if (parser.eof()) {
2145 break;
2146 }
2147 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002148 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002149 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002150 parser.skipToEndBracket('\n');
2151 }
2152 return true;
2153}
2154
Cary Clarkd98f78c2018-04-26 08:32:37 -04002155bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002156 if (!markupDef) {
2157 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2158 child->fLineCount, fParent, '\0');
2159 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002160 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002161 globalMarkupChild->fName = globalUniqueName;
2162 if (!this->findComments(*child, globalMarkupChild)) {
2163 return false;
2164 }
2165 fIConstMap[globalUniqueName] = globalMarkupChild;
2166 return true;
2167 }
2168 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2169 child->fLineCount, markupDef, '\0');
2170 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002171 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002172 markupChild->fTerminator = markupChild->fContentEnd;
2173 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002174 classDef.fConsts[child->fName] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002175 fIConstMap[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002176 return true;
2177}
2178
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002179bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2180 TextParser parser(child);
2181 if (!parser.skipExact("#define")) {
2182 return false;
2183 }
2184 if (!parser.skipSpace()) {
2185 return false;
2186 }
2187 const char* nameStart = parser.fChar;
2188 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2189 if (parser.eof()) {
2190 return true; // do nothing if #define doesn't define anything
2191 }
2192 string nameStr(nameStart, parser.fChar - nameStart);
2193 struct Param {
2194 const char* fStart;
2195 const char* fEnd;
2196 };
2197 vector<Param> params;
2198 if ('(' == parser.peek()) {
2199 parser.next();
2200 if (!parser.skipSpace()) {
2201 return false;
2202 }
2203 do {
2204 const char* paramStart = parser.fChar;
2205 if (!parser.skipExact("...")) {
2206 parser.skipToNonAlphaNum();
2207 }
2208 if (parser.eof()) {
2209 return false;
2210 }
2211 params.push_back({paramStart, parser.fChar});
2212 if (!parser.skipSpace()) {
2213 return false;
2214 }
2215 if (')' == parser.peek()) {
2216 parser.next();
2217 break;
2218 }
2219 if (',' != parser.next()) {
2220 return false;
2221 }
2222 if (!parser.skipSpace()) {
2223 return false;
2224 }
2225 } while (true);
2226 }
2227 if (!parser.skipSpace()) {
2228 return false;
2229 }
2230 if (!markupDef) {
2231 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2232 child->fLineCount, fParent, '\0');
2233 Definition* globalMarkupChild = &fGlobals.back();
2234 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2235 globalMarkupChild->fName = globalUniqueName;
2236 globalMarkupChild->fTerminator = child->fContentEnd;
2237 if (!this->findComments(*child, globalMarkupChild)) {
2238 return false;
2239 }
2240 fIDefineMap[globalUniqueName] = globalMarkupChild;
2241 for (Param param : params) {
2242 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2243 child->fLineCount, globalMarkupChild, '\0');
2244 Definition* paramChild = &globalMarkupChild->fTokens.back();
2245 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2246 paramChild->fTerminator = param.fEnd;
2247 }
2248 return true;
2249 }
2250 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2251 child->fLineCount, markupDef, '\0');
2252 Definition* markupChild = &markupDef->fTokens.back();
2253 markupChild->fName = nameStr;
2254 markupChild->fTerminator = markupChild->fContentEnd;
2255 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2256 if (!this->findComments(*child, markupChild)) {
2257 return false;
2258 }
2259 classDef.fDefines[nameStr] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002260 fIDefineMap[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002261 return true;
2262}
2263
2264bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002265 TextParser parser(child);
2266 parser.skipToEndBracket('{');
2267 if (parser.eof()) {
2268 return true; // if enum is a forward declaration, do nothing
2269 }
2270 parser.next();
2271 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002272 if (child->fTokens.size() > 0) {
2273 auto token = child->fTokens.begin();
2274 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2275 token = token->fTokens.begin();
2276 }
2277 if (Definition::Type::kWord == token->fType) {
2278 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
2279 }
2280 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002281 Definition* markupChild;
2282 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002283 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2284 child->fLineCount, fParent, '\0');
2285 markupChild = &fGlobals.back();
2286 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2287 markupChild->fName = globalUniqueName;
2288 markupChild->fTerminator = child->fContentEnd;
2289 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002290 } else {
2291 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002292 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002293 markupChild = &markupDef->fTokens.back();
2294 }
Cary Clark8032b982017-07-28 11:04:54 -04002295 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2296 markupChild->fKeyWord = KeyWord::kEnum;
2297 TextParser enumName(child);
2298 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05002299 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002300 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002301 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002302 markupChild->fMarkType = MarkType::kEnumClass;
2303 }
Cary Clark8032b982017-07-28 11:04:54 -04002304 const char* nameStart = enumName.fChar;
2305 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05002306 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04002307 markupChild->fName = markupDef->fName + "::" +
2308 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05002309 }
Cary Clark8032b982017-07-28 11:04:54 -04002310 if (!this->findComments(*child, markupChild)) {
2311 return false;
2312 }
Cary Clark8032b982017-07-28 11:04:54 -04002313 const char* dataEnd;
2314 do {
Cary Clark8032b982017-07-28 11:04:54 -04002315 parser.skipWhiteSpace();
2316 if ('}' == parser.peek()) {
2317 break;
2318 }
2319 Definition* comment = nullptr;
2320 // note that comment, if any, can be before or after (on the same line, though) as member
2321 if ('#' == parser.peek()) {
2322 // fixme: handle preprecessor, but just skip it for now
2323 parser.skipToLineStart();
2324 }
2325 while (parser.startsWith("/*") || parser.startsWith("//")) {
2326 parser.next();
2327 const char* start = parser.fChar;
2328 const char* end;
2329 if ('*' == parser.peek()) {
2330 end = parser.strnstr("*/", parser.fEnd);
2331 parser.fChar = end;
2332 parser.next();
2333 parser.next();
2334 } else {
2335 end = parser.trimmedLineEnd();
2336 parser.skipToLineStart();
2337 }
2338 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002339 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002340 comment = &markupChild->fTokens.back();
2341 comment->fTerminator = end;
2342 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
2343 return false;
2344 }
2345 parser.skipWhiteSpace();
2346 }
2347 parser.skipWhiteSpace();
2348 const char* memberStart = parser.fChar;
2349 if ('}' == memberStart[0]) {
2350 break;
2351 }
Cary Clark9174bda2017-09-19 17:39:32 -04002352 // if there's comment on same the line as member def, output first as if it was before
2353
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002354 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002355 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04002356 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04002357 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04002358 }
Cary Clark8032b982017-07-28 11:04:54 -04002359 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04002360 if ('=' == parser.peek()) {
2361 parser.skipToEndBracket(',');
2362 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002363 if (!parser.eof() && '#' == parser.peek()) {
2364 // fixme: handle preprecessor, but just skip it for now
2365 continue;
2366 }
Cary Clark9174bda2017-09-19 17:39:32 -04002367 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04002368 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04002369 }
2370 dataEnd = parser.fChar;
2371 const char* start = parser.anyOf("/\n");
2372 SkASSERT(start);
2373 parser.skipTo(start);
2374 if ('/' == parser.next()) {
2375 char slashStar = parser.next();
2376 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04002377 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04002378 char doxCheck = parser.next();
2379 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
2380 save.restore();
2381 }
2382 }
2383 parser.skipWhiteSpace();
2384 const char* commentStart = parser.fChar;
2385 if ('/' == slashStar) {
2386 parser.skipToEndBracket('\n');
2387 } else {
2388 parser.skipToEndBracket("*/");
2389 }
2390 SkASSERT(!parser.eof());
2391 const char* commentEnd = parser.fChar;
2392 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002393 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04002394 comment = &markupChild->fTokens.back();
2395 comment->fTerminator = commentEnd;
2396 }
Cary Clark8032b982017-07-28 11:04:54 -04002397 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002398 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002399 Definition* member = &markupChild->fTokens.back();
2400 member->fName = memberName;
2401 if (comment) {
2402 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04002403 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002404 }
2405 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04002406 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002407 for (auto outsideMember : child->fChildren) {
2408 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002409 continue;
2410 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002411 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2412 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002413 continue;
2414 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002415 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2416 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002417 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002418 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002419 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002420 // FIXME: ? add comment as well ?
2421 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002422 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002423 if (markupDef) {
2424 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2425 SkASSERT(classDef.fStart);
2426 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002427 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002428 markupChild->fName = uniqueName;
2429 classDef.fEnums[uniqueName] = markupChild;
Cary Clark61313f32018-10-08 14:57:48 -04002430 fIEnumMap[fullName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002431 }
Cary Clark8032b982017-07-28 11:04:54 -04002432 return true;
2433}
2434
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002435bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002436 fParent = &fIncludeMap[name];
2437 fParent->fName = name;
2438 fParent->fFileName = fFileName;
2439 fParent->fType = Definition::Type::kFileType;
2440 fParent->fContentStart = fChar;
2441 fParent->fContentEnd = fEnd;
2442 // parse include file into tree
2443 while (fChar < fEnd) {
2444 if (!this->parseChar()) {
2445 return false;
2446 }
2447 }
2448 // parse tree and add named objects to maps
2449 fParent = &fIncludeMap[name];
2450 if (!this->parseObjects(fParent, nullptr)) {
2451 return false;
2452 }
2453 return true;
2454}
2455
2456bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2457 const char* typeStart = child->fChildren[0]->fContentStart;
2458 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002459 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002460 Definition* markupChild = &markupDef->fTokens.back();
2461 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002462 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002463 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2464 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2465 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2466 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002467 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04002468 classDef.fMembers[uniqueName] = markupChild;
2469 if (child->fParentIndex >= 2) {
2470 auto comment = child->fParent->fTokens.begin();
2471 std::advance(comment, child->fParentIndex - 2);
2472 if (Definition::Type::kBracket == comment->fType
2473 && (Bracket::kSlashStar == comment->fBracket
2474 || Bracket::kSlashSlash == comment->fBracket)) {
2475 TextParser parser(&*comment);
2476 do {
2477 parser.skipToAlpha();
2478 if (parser.eof()) {
2479 break;
2480 }
2481 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002482 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002483 if (Bracket::kSlashStar == comment->fBracket) {
2484 const char* commentEnd = parser.strnstr("*/", end);
2485 if (commentEnd) {
2486 end = commentEnd;
2487 }
2488 }
2489 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002490 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002491 Definition* commentChild = &markupDef->fTokens.back();
2492 markupChild->fChildren.emplace_back(commentChild);
2493 parser.skipTo(end);
2494 } while (!parser.eof());
2495 }
2496 }
2497 return true;
2498}
2499
2500bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2501 auto tokenIter = child->fParent->fTokens.begin();
2502 std::advance(tokenIter, child->fParentIndex);
2503 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002504 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002505 bool addConst = false;
2506 auto operatorCheck = tokenIter;
2507 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2508 operatorCheck = std::prev(tokenIter);
2509 }
2510 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002511 auto closeParen = std::next(tokenIter);
2512 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2513 '(' == closeParen->fContentStart[0]);
2514 nameEnd = closeParen->fContentEnd + 1;
2515 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002516 if (Definition::Type::kKeyWord == closeParen->fType &&
2517 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002518 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002519 }
Cary Clarka560c472017-11-27 10:44:06 -05002520 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002521 }
2522 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002523 if (addConst) {
2524 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002525 }
Cary Clark8032b982017-07-28 11:04:54 -04002526 while (tokenIter != child->fParent->fTokens.begin()) {
2527 auto testIter = std::prev(tokenIter);
2528 switch (testIter->fType) {
2529 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002530 if (testIter == child->fParent->fTokens.begin() &&
2531 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2532 KeyWord::kIfndef == child->fParent->fKeyWord ||
2533 KeyWord::kIf == child->fParent->fKeyWord)) {
2534 std::next(tokenIter);
2535 break;
2536 }
Cary Clark8032b982017-07-28 11:04:54 -04002537 goto keepGoing;
2538 case Definition::Type::kKeyWord: {
2539 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2540 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2541 goto keepGoing;
2542 }
2543 } break;
2544 case Definition::Type::kBracket:
2545 if (Bracket::kAngle == testIter->fBracket) {
2546 goto keepGoing;
2547 }
2548 break;
2549 case Definition::Type::kPunctuation:
2550 if (Punctuation::kSemicolon == testIter->fPunctuation
2551 || Punctuation::kLeftBrace == testIter->fPunctuation
2552 || Punctuation::kColon == testIter->fPunctuation) {
2553 break;
2554 }
2555 keepGoing:
2556 tokenIter = testIter;
2557 continue;
2558 default:
2559 break;
2560 }
2561 break;
2562 }
Cary Clark224c7002018-06-27 11:00:21 -04002563 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002564 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002565 tokenIter->fPrivate = string::npos != nameStr.find("::")
2566 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002567 auto testIter = child->fParent->fTokens.begin();
2568 SkASSERT(child->fParentIndex > 0);
2569 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002570 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2571 0 == tokenIter->fParentIndex) {
2572 tokenIter = std::next(tokenIter);
2573 }
Cary Clark8032b982017-07-28 11:04:54 -04002574 const char* start = tokenIter->fContentStart;
2575 const char* end = tokenIter->fContentEnd;
2576 const char kDebugCodeStr[] = "SkDEBUGCODE";
2577 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2578 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2579 std::advance(testIter, 1);
2580 start = testIter->fContentStart + 1;
2581 end = testIter->fContentEnd - 1;
2582 } else {
2583 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002584 do {
2585 std::advance(testIter, 1);
2586 if (testIter == child->fParent->fTokens.end()) {
2587 break;
2588 }
Cary Clark8032b982017-07-28 11:04:54 -04002589 switch (testIter->fType) {
2590 case Definition::Type::kPunctuation:
2591 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2592 || Punctuation::kLeftBrace == testIter->fPunctuation
2593 || Punctuation::kColon == testIter->fPunctuation);
2594 end = testIter->fStart;
2595 break;
2596 case Definition::Type::kKeyWord: {
2597 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2598 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2599 continue;
2600 }
2601 } break;
2602 default:
2603 continue;
2604 }
2605 break;
Cary Clark61313f32018-10-08 14:57:48 -04002606 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002607 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002608 while (end > start && ' ' >= end[-1]) {
2609 --end;
2610 }
Cary Clark9174bda2017-09-19 17:39:32 -04002611 if (!markupDef) {
2612 auto parentIter = child->fParent->fTokens.begin();
2613 SkASSERT(child->fParentIndex > 0);
2614 std::advance(parentIter, child->fParentIndex - 1);
2615 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002616 TextParser nameParser(methodName);
2617 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002618 return true; // expect this is inline class definition outside of class
2619 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002620 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2621 fParent, '\0');
2622 Definition* globalMarkupChild = &fGlobals.back();
2623 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2624 globalMarkupChild->fName = globalUniqueName;
2625 if (!this->findComments(*child, globalMarkupChild)) {
2626 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002627 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002628 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002629 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002630 }
Cary Clark8032b982017-07-28 11:04:54 -04002631 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002632 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002633 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002634 {
2635 auto mapIter = fIClassMap.find(markupDef->fName);
2636 SkASSERT(fIClassMap.end() != mapIter);
2637 IClassDefinition& classDef = mapIter->second;
2638 SkASSERT(classDef.fStart);
2639 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2640 markupChild->fName = uniqueName;
2641 if (!this->findComments(*child, markupChild)) {
2642 return false;
2643 }
2644 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002645 }
Cary Clark8032b982017-07-28 11:04:54 -04002646 return true;
2647}
2648
Cary Clark8032b982017-07-28 11:04:54 -04002649bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002650 fPriorObject = nullptr;
2651 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002652 if (!this->parseObject(child, markupDef)) {
2653 return false;
2654 }
Cary Clark0d225392018-06-07 09:59:07 -04002655 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002656 }
2657 return true;
2658}
2659
2660bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2661 // set up for error reporting
2662 fLine = fChar = child->fStart;
2663 fEnd = child->fContentEnd;
2664 // todo: put original line number in child as well
2665 switch (child->fType) {
2666 case Definition::Type::kKeyWord:
2667 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002668 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002669 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002670 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002671 }
2672 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002673 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002674 case KeyWord::kConst:
2675 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002676 if (!this->parseConst(child, markupDef)) {
2677 return child->reportError<bool>("failed to parse const or constexpr");
2678 }
2679 break;
Cary Clark8032b982017-07-28 11:04:54 -04002680 case KeyWord::kEnum:
2681 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002682 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002683 }
2684 break;
2685 case KeyWord::kStruct:
2686 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002687 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002688 }
2689 break;
2690 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002691 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002692 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002693 }
2694 break;
2695 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002696 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002697 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002698 }
2699 break;
2700 case KeyWord::kUnion:
2701 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002702 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002703 }
2704 break;
Cary Clark61313f32018-10-08 14:57:48 -04002705 case KeyWord::kUsing:
2706 if (!this->parseUsing()) {
2707 return child->reportError<bool>("failed to parse using");
2708 }
2709 break;
Cary Clark8032b982017-07-28 11:04:54 -04002710 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002711 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002712 }
2713 break;
2714 case Definition::Type::kBracket:
2715 switch (child->fBracket) {
2716 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002717 {
2718 auto tokenIter = child->fParent->fTokens.begin();
2719 std::advance(tokenIter, child->fParentIndex);
2720 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002721 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002722 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002723 break;
2724 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002725 if (Bracket::kPound == child->fParent->fBracket &&
2726 KeyWord::kIf == child->fParent->fKeyWord) {
2727 // TODO: this will skip methods named defined() -- for the
2728 // moment there aren't any
2729 if (previousToken.startsWith("defined")) {
2730 break;
2731 }
2732 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002733 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2734 break;
2735 }
Cary Clark73fa9722017-08-29 17:36:51 -04002736 }
Cary Clark0d225392018-06-07 09:59:07 -04002737 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2738 break;
2739 }
Cary Clark8032b982017-07-28 11:04:54 -04002740 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002741 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002742 }
Cary Clark73fa9722017-08-29 17:36:51 -04002743 break;
Cary Clark8032b982017-07-28 11:04:54 -04002744 case Bracket::kSlashSlash:
2745 case Bracket::kSlashStar:
2746 // comments are picked up by parsing objects first
2747 break;
2748 case Bracket::kPound:
2749 // special-case the #xxx xxx_DEFINED entries
2750 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002751 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002752 case KeyWord::kIfndef:
2753 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002754 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002755 if (!this->parseObjects(child, markupDef)) {
2756 return false;
2757 }
2758 break;
2759 }
2760 goto preproError;
2761 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002762 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002763 break;
2764 }
2765 goto preproError;
2766 case KeyWord::kEndif:
2767 if (child->boilerplateEndIf()) {
2768 break;
2769 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002770 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002771 case KeyWord::kInclude:
2772 // ignored for now
2773 break;
2774 case KeyWord::kElse:
2775 case KeyWord::kElif:
2776 // todo: handle these
2777 break;
2778 default:
2779 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002780 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002781 }
2782 break;
2783 case Bracket::kAngle:
2784 // pick up templated function pieces when method is found
2785 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002786 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002787 if (!this->parseObjects(child, markupDef)) {
2788 return false;
2789 }
Cary Clark73fa9722017-08-29 17:36:51 -04002790 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002791 case Bracket::kSquare: {
2792 // check to see if parent is operator, the only case we handle so far
2793 auto prev = child->fParent->fTokens.begin();
2794 std::advance(prev, child->fParentIndex - 1);
2795 if (KeyWord::kOperator != prev->fKeyWord) {
2796 return child->reportError<bool>("expected operator overload");
2797 }
2798 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002799 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002800 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002801 }
2802 break;
2803 case Definition::Type::kWord:
2804 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002805 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002806 }
2807 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002808 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002809 }
2810 break;
2811 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002812 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002813 break;
2814 }
2815 return true;
2816}
2817
Cary Clarkbbfda252018-03-09 15:32:01 -05002818bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2819 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002820}
2821
Cary Clark2f466242017-12-11 16:03:17 -05002822bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2823 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002824 typedefParser.skipExact("typedef");
2825 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002826 string nameStr = typedefParser.typedefName();
2827 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002828 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2829 child->fLineCount, fParent, '\0');
2830 Definition* globalMarkupChild = &fGlobals.back();
2831 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2832 globalMarkupChild->fName = globalUniqueName;
2833 if (!this->findComments(*child, globalMarkupChild)) {
2834 return false;
2835 }
2836 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002837 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002838 return true;
2839 }
2840 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002841 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002842 Definition* markupChild = &markupDef->fTokens.back();
2843 markupChild->fName = nameStr;
2844 markupChild->fTerminator = markupChild->fContentEnd;
2845 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2846 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002847 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04002848 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002849 return true;
2850}
2851
2852bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002853 // incomplete
2854 return true;
2855}
Cary Clark8032b982017-07-28 11:04:54 -04002856
Cary Clark61313f32018-10-08 14:57:48 -04002857bool IncludeParser::parseUsing() {
2858 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002859 return true;
2860}
2861
2862bool IncludeParser::parseChar() {
2863 char test = *fChar;
2864 if ('\\' == fPrev) {
2865 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002866// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002867 fLine = fChar + 1;
2868 }
2869 goto done;
2870 }
2871 switch (test) {
2872 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002873// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002874 fLine = fChar + 1;
2875 if (fInChar) {
2876 return reportError<bool>("malformed char");
2877 }
2878 if (fInString) {
2879 return reportError<bool>("malformed string");
2880 }
2881 if (!this->checkForWord()) {
2882 return false;
2883 }
2884 if (Bracket::kPound == this->topBracket()) {
2885 KeyWord keyWord = fParent->fKeyWord;
2886 if (KeyWord::kNone == keyWord) {
2887 return this->reportError<bool>("unhandled preprocessor directive");
2888 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002889 if (fInDefine) {
2890 SkASSERT(KeyWord::kDefine == keyWord);
2891 fInDefine = false;
2892 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002893 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002894 this->popBracket();
2895 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002896 if (fInBrace) {
2897 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2898 fInBrace = nullptr;
2899 }
Cary Clark8032b982017-07-28 11:04:54 -04002900 } else if (Bracket::kSlashSlash == this->topBracket()) {
2901 this->popBracket();
2902 }
2903 break;
2904 case '*':
2905 if (!fInCharCommentString && '/' == fPrev) {
2906 this->pushBracket(Bracket::kSlashStar);
2907 }
2908 if (!this->checkForWord()) {
2909 return false;
2910 }
2911 if (!fInCharCommentString) {
2912 this->addPunctuation(Punctuation::kAsterisk);
2913 }
2914 break;
2915 case '/':
2916 if ('*' == fPrev) {
2917 if (!fInCharCommentString) {
2918 return reportError<bool>("malformed closing comment");
2919 }
2920 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002921 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002922 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002923 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002924 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002925 }
2926 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002927 }
Cary Clark8032b982017-07-28 11:04:54 -04002928 if (!fInCharCommentString && '/' == fPrev) {
2929 this->pushBracket(Bracket::kSlashSlash);
2930 break;
2931 }
2932 if (!this->checkForWord()) {
2933 return false;
2934 }
2935 break;
2936 case '\'':
2937 if (Bracket::kChar == this->topBracket()) {
2938 this->popBracket();
2939 } else if (!fInComment && !fInString) {
2940 if (fIncludeWord) {
2941 return this->reportError<bool>("word then single-quote");
2942 }
2943 this->pushBracket(Bracket::kChar);
2944 }
2945 break;
2946 case '\"':
2947 if (Bracket::kString == this->topBracket()) {
2948 this->popBracket();
2949 } else if (!fInComment && !fInChar) {
2950 if (fIncludeWord) {
2951 return this->reportError<bool>("word then double-quote");
2952 }
2953 this->pushBracket(Bracket::kString);
2954 }
2955 break;
Cary Clark8032b982017-07-28 11:04:54 -04002956 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002957 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002958 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2959 this->pushBracket(Bracket::kDebugCode);
2960 break;
2961 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002962 case ':':
2963 case '[':
2964 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002965 if (fInCharCommentString) {
2966 break;
2967 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002968 if (fInDefine && fInBrace) {
2969 break;
2970 }
Cary Clark8032b982017-07-28 11:04:54 -04002971 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2972 break;
2973 }
Cary Clark0d225392018-06-07 09:59:07 -04002974 if (fConstExpr) {
2975 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2976 fConstExpr = nullptr;
2977 }
Cary Clark8032b982017-07-28 11:04:54 -04002978 if (!fInBrace) {
2979 if (!this->checkForWord()) {
2980 return false;
2981 }
2982 if (':' == test && !fInFunction) {
2983 break;
2984 }
2985 if ('{' == test) {
2986 this->addPunctuation(Punctuation::kLeftBrace);
2987 } else if (':' == test) {
2988 this->addPunctuation(Punctuation::kColon);
2989 }
2990 }
2991 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2992 && Bracket::kColon == fInBrace->fBracket) {
2993 Definition* braceParent = fParent->fParent;
2994 braceParent->fChildren.pop_back();
2995 braceParent->fTokens.pop_back();
2996 fParent = braceParent;
2997 fInBrace = nullptr;
2998 }
2999 this->pushBracket(
3000 '(' == test ? Bracket::kParen :
3001 '[' == test ? Bracket::kSquare :
3002 '{' == test ? Bracket::kBrace :
3003 Bracket::kColon);
3004 if (!fInBrace
3005 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3006 && fInFunction) {
3007 fInBrace = fParent;
3008 }
3009 } break;
3010 case '<':
3011 if (fInCharCommentString || fInBrace) {
3012 break;
3013 }
3014 if (!this->checkForWord()) {
3015 return false;
3016 }
3017 if (fInEnum) {
3018 break;
3019 }
3020 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003021 // this angle bracket may be an operator or may be a bracket
3022 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003023 break;
3024 case ')':
3025 case ']':
3026 case '}': {
3027 if (fInCharCommentString) {
3028 break;
3029 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003030 if (fInDefine && fInBrace) {
3031 break;
3032 }
Cary Clark8032b982017-07-28 11:04:54 -04003033 if (!fInBrace) {
3034 if (!this->checkForWord()) {
3035 return false;
3036 }
3037 }
3038 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003039 Bracket match = ')' == test ? Bracket::kParen :
3040 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3041 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003042 this->popBracket();
3043 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003044 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003045 } else {
3046 fInFunction = '}' != test;
3047 }
Cary Clark73fa9722017-08-29 17:36:51 -04003048 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3049 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003050 } else if (Bracket::kAngle == this->topBracket()
3051 && match == this->grandParentBracket()) {
3052 this->popBracket();
3053 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003054 } else {
3055 return reportError<bool>("malformed close bracket");
3056 }
3057 if (popBraceParent) {
3058 Definition* braceParent = fInBrace->fParent;
3059 braceParent->fChildren.pop_back();
3060 braceParent->fTokens.pop_back();
3061 fInBrace = nullptr;
3062 }
3063 } break;
3064 case '>':
3065 if (fInCharCommentString || fInBrace) {
3066 break;
3067 }
3068 if (!this->checkForWord()) {
3069 return false;
3070 }
3071 if (fInEnum) {
3072 break;
3073 }
Cary Clarka560c472017-11-27 10:44:06 -05003074 if (Bracket::kPound == this->topBracket()) {
3075 break;
3076 }
Cary Clark8032b982017-07-28 11:04:54 -04003077 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003078 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003079 this->popBracket();
3080 } else {
3081 return reportError<bool>("malformed close angle bracket");
3082 }
3083 break;
3084 case '#': {
3085 if (fInCharCommentString || fInBrace) {
3086 break;
3087 }
3088 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3089 this->pushBracket(Bracket::kPound);
3090 break;
3091 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003092 case ' ':
3093 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3094 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3095 fInBrace = fParent;
3096 // delimiting brackets are space ... unescaped-linefeed
3097 }
Cary Clark8032b982017-07-28 11:04:54 -04003098 case '&':
3099 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003100 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003101 case '-':
3102 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003103 if (fInCharCommentString || fInBrace) {
3104 break;
3105 }
3106 if (!this->checkForWord()) {
3107 return false;
3108 }
3109 break;
Cary Clark0d225392018-06-07 09:59:07 -04003110 case '=':
3111 if (fInCharCommentString || fInBrace) {
3112 break;
3113 }
3114 if (!this->checkForWord()) {
3115 return false;
3116 }
Cary Clarkd7895502018-07-18 15:10:08 -04003117 if (!fParent->fTokens.size()) {
3118 break;
3119 }
Cary Clark0d225392018-06-07 09:59:07 -04003120 {
3121 const Definition& lastToken = fParent->fTokens.back();
3122 if (lastToken.fType != Definition::Type::kWord) {
3123 break;
3124 }
3125 string name(lastToken.fContentStart, lastToken.length());
3126 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3127 break;
3128 }
3129 // find token on start of line
3130 auto lineIter = fParent->fTokens.end();
3131 do {
Cary Clark80247e52018-07-11 16:18:41 -04003132 if (fParent->fTokens.begin() == lineIter) {
3133 break;
3134 }
Cary Clark0d225392018-06-07 09:59:07 -04003135 --lineIter;
3136 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003137 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003138 ++lineIter;
3139 }
3140 Definition* lineStart = &*lineIter;
3141 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3142 bool sawConst = false;
3143 bool sawStatic = false;
3144 bool sawTemplate = false;
3145 bool sawType = false;
3146 while (&lastToken != &*lineIter) {
3147 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3148 if (sawConst || sawStatic || sawTemplate) {
3149 sawConst = false;
3150 break;
3151 }
3152 if (&lastToken == &*++lineIter) {
3153 break;
3154 }
3155 if (KeyWord::kTypename != lineIter->fKeyWord) {
3156 break;
3157 }
3158 if (&lastToken == &*++lineIter) {
3159 break;
3160 }
3161 if (Definition::Type::kWord != lineIter->fType) {
3162 break;
3163 }
3164 sawTemplate = true;
3165 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3166 if (sawConst || sawStatic) {
3167 sawConst = false;
3168 break;
3169 }
3170 sawStatic = true;
3171 } else if (KeyWord::kConst == lineIter->fKeyWord
3172 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3173 if (sawConst) {
3174 sawConst = false;
3175 break;
3176 }
3177 sawConst = true;
3178 } else {
3179 if (sawType) {
3180 sawType = false;
3181 break;
3182 }
3183 if (Definition::Type::kKeyWord == lineIter->fType
3184 && KeyProperty::kNumber
3185 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3186 sawType = true;
3187 } else if (Definition::Type::kWord == lineIter->fType) {
3188 string typeName(lineIter->fContentStart, lineIter->length());
3189 if ("Sk" != name.substr(0, 2)) {
3190 sawType = true;
3191 }
3192 }
3193 }
3194 ++lineIter;
3195 }
3196 if (sawType && sawConst) {
3197 // if found, name first
3198 lineStart->fName = name;
3199 lineStart->fMarkType = MarkType::kConst;
3200 fParent->fChildren.emplace_back(lineStart);
3201 fConstExpr = lineStart;
3202 }
3203 }
3204 break;
Cary Clark8032b982017-07-28 11:04:54 -04003205 case ';':
3206 if (fInCharCommentString || fInBrace) {
3207 break;
3208 }
3209 if (!this->checkForWord()) {
3210 return false;
3211 }
Cary Clark0d225392018-06-07 09:59:07 -04003212 if (fConstExpr) {
3213 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3214 fConstExpr = nullptr;
3215 }
Cary Clark8032b982017-07-28 11:04:54 -04003216 if (Definition::Type::kKeyWord == fParent->fType
3217 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003218 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3219 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003220 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3221 this->popObject();
3222 }
Cary Clark8032b982017-07-28 11:04:54 -04003223 if (KeyWord::kEnum == fParent->fKeyWord) {
3224 fInEnum = false;
3225 }
Cary Clark61313f32018-10-08 14:57:48 -04003226 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003227 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003228 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3229 this->popObject();
3230 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003231 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003232 } else if (Definition::Type::kBracket == fParent->fType
3233 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3234 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3235 list<Definition>::iterator baseIter = fParent->fTokens.end();
3236 list<Definition>::iterator namedIter = fParent->fTokens.end();
3237 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003238 fParent->fTokens.begin() != tokenIter; ) {
3239 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003240 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003241 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003242 if (namedIter != fParent->fTokens.end()) {
3243 return reportError<bool>("found two named member tokens");
3244 }
3245 namedIter = tokenIter;
3246 }
3247 baseIter = tokenIter;
3248 } else {
3249 break;
3250 }
3251 }
3252 // FIXME: if a member definition spans multiple lines, this won't work
3253 if (namedIter != fParent->fTokens.end()) {
3254 if (baseIter == namedIter) {
3255 return this->reportError<bool>("expected type before named token");
3256 }
3257 Definition* member = &*namedIter;
3258 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003259 if (!member->fTerminator) {
3260 member->fTerminator = member->fContentEnd;
3261 }
Cary Clark8032b982017-07-28 11:04:54 -04003262 fParent->fChildren.push_back(member);
3263 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3264 member->fChildren.push_back(&*nameType);
3265 }
Cary Clark8032b982017-07-28 11:04:54 -04003266 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003267 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003268 } else if (fParent->fChildren.size() > 0) {
3269 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003270 Definition* priorEnum = fPriorEnum;
3271 fPriorEnum = nullptr;
3272 if (!priorEnum) {
3273 while (fParent->fChildren.begin() != lastIter) {
3274 std::advance(lastIter, -1);
3275 priorEnum = *lastIter;
3276 if (Definition::Type::kBracket != priorEnum->fType ||
3277 (Bracket::kSlashSlash != priorEnum->fBracket
3278 && Bracket::kSlashStar != priorEnum->fBracket)) {
3279 break;
3280 }
Cary Clark8032b982017-07-28 11:04:54 -04003281 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003282 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003283 }
3284 if (Definition::Type::kKeyWord == priorEnum->fType
3285 && KeyWord::kEnum == priorEnum->fKeyWord) {
3286 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003287 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003288 while (tokenWalker != fParent->fTokens.end()) {
3289 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003290 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003291 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3292 break;
3293 }
3294 }
3295 while (tokenWalker != fParent->fTokens.end()) {
3296 std::advance(tokenWalker, 1);
3297 const Definition* test = &*tokenWalker;
3298 if (Definition::Type::kBracket != test->fType ||
3299 (Bracket::kSlashSlash != test->fBracket
3300 && Bracket::kSlashStar != test->fBracket)) {
3301 break;
3302 }
3303 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003304 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003305 Definition* start = &*tokenWalker;
3306 bool foundExpected = true;
3307 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3308 const Definition* test = &*tokenWalker;
3309 if (expected != test->fKeyWord) {
3310 foundExpected = false;
3311 break;
3312 }
3313 if (tokenWalker == fParent->fTokens.end()) {
3314 break;
3315 }
3316 std::advance(tokenWalker, 1);
3317 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003318 if (!foundExpected) {
3319 foundExpected = true;
3320 tokenWalker = saveTokenWalker;
3321 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3322 const Definition* test = &*tokenWalker;
3323 if (expected != test->fKeyWord) {
3324 foundExpected = false;
3325 break;
3326 }
3327 if (tokenWalker == fParent->fTokens.end()) {
3328 break;
3329 }
3330 if (KeyWord::kNone != expected) {
3331 std::advance(tokenWalker, 1);
3332 }
3333 }
3334 if (foundExpected) {
3335 auto nameToken = priorEnum->fTokens.begin();
3336 string enumName = string(nameToken->fContentStart,
3337 nameToken->fContentEnd - nameToken->fContentStart);
3338 const Definition* test = &*tokenWalker;
3339 string constType = string(test->fContentStart,
3340 test->fContentEnd - test->fContentStart);
3341 if (enumName != constType) {
3342 foundExpected = false;
3343 } else {
3344 std::advance(tokenWalker, 1);
3345 }
3346 }
3347 }
Cary Clark8032b982017-07-28 11:04:54 -04003348 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3349 const char* nameStart = tokenWalker->fStart;
3350 std::advance(tokenWalker, 1);
3351 if (tokenWalker != fParent->fTokens.end()) {
3352 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003353 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003354 start->fName = string(nameStart, tp.fChar - nameStart);
3355 start->fContentEnd = fChar;
3356 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003357 fPriorEnum = priorEnum;
3358 }
Cary Clark8032b982017-07-28 11:04:54 -04003359 }
3360 }
3361 }
3362 this->addPunctuation(Punctuation::kSemicolon);
3363 fInFunction = false;
3364 break;
3365 case '~':
3366 if (fInEnum) {
3367 break;
3368 }
3369 case '0': case '1': case '2': case '3': case '4':
3370 case '5': case '6': case '7': case '8': case '9':
3371 // TODO: don't want to parse numbers, but do need to track for enum defs
3372 // break;
3373 case 'A': case 'B': case 'C': case 'D': case 'E':
3374 case 'F': case 'G': case 'H': case 'I': case 'J':
3375 case 'K': case 'L': case 'M': case 'N': case 'O':
3376 case 'P': case 'Q': case 'R': case 'S': case 'T':
3377 case 'U': case 'V': case 'W': case 'X': case 'Y':
3378 case 'Z': case '_':
3379 case 'a': case 'b': case 'c': case 'd': case 'e':
3380 case 'f': case 'g': case 'h': case 'i': case 'j':
3381 case 'k': case 'l': case 'm': case 'n': case 'o':
3382 case 'p': case 'q': case 'r': case 's': case 't':
3383 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003384 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003385 if (fInCharCommentString || fInBrace) {
3386 break;
3387 }
3388 if (!fIncludeWord) {
3389 fIncludeWord = fChar;
3390 }
3391 break;
3392 }
3393done:
3394 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003395 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003396 return true;
3397}
3398
3399void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003400 IncludeParser::ValidateKeyWords();
3401}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003402
Cary Clark186d08f2018-04-03 08:43:27 -04003403bool IncludeParser::references(const SkString& file) const {
3404 // if includes weren't passed one at a time, assume all references are valid
3405 if (fIncludeMap.empty()) {
3406 return true;
3407 }
3408 SkASSERT(file.endsWith(".bmh") );
3409 string root(file.c_str(), file.size() - 4);
3410 string kReference("_Reference");
3411 if (string::npos != root.find(kReference)) {
3412 root = root.substr(0, root.length() - kReference.length());
3413 }
3414 if (fIClassMap.end() != fIClassMap.find(root)) {
3415 return true;
3416 }
3417 if (fIStructMap.end() != fIStructMap.find(root)) {
3418 return true;
3419 }
Cary Clark224c7002018-06-27 11:00:21 -04003420 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3421 return true;
3422 }
Cary Clarka90ea222018-10-16 10:30:28 -04003423 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3424 return true;
3425 }
Cary Clark224c7002018-06-27 11:00:21 -04003426 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3427 return true;
3428 }
Cary Clark186d08f2018-04-03 08:43:27 -04003429 return false;
3430}
3431
Cary Clark2dc84ad2018-01-26 12:56:22 -05003432void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3433 if (!sk_isdir(includes)) {
3434 IncludeParser::RemoveOneFile(docs, includes);
3435 } else {
3436 SkOSFile::Iter it(includes, ".h");
3437 for (SkString file; it.next(&file); ) {
3438 SkString p = SkOSPath::Join(includes, file.c_str());
3439 const char* hunk = p.c_str();
3440 if (!SkStrEndsWith(hunk, ".h")) {
3441 continue;
3442 }
3443 IncludeParser::RemoveOneFile(docs, hunk);
3444 }
3445 }
3446}
3447
3448void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3449 const char* lastForward = strrchr(includesFile, '/');
3450 const char* lastBackward = strrchr(includesFile, '\\');
3451 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3452 if (!last) {
3453 last = includesFile;
3454 } else {
3455 last += 1;
3456 }
3457 SkString baseName(last);
3458 SkASSERT(baseName.endsWith(".h"));
3459 baseName.remove(baseName.size() - 2, 2);
3460 baseName.append("_Reference.bmh");
3461 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3462 remove(fullName.c_str());
3463}
Cary Clark224c7002018-06-27 11:00:21 -04003464
3465Bracket IncludeParser::topBracket() const {
3466 Definition* parent = this->parentBracket(fParent);
3467 return parent ? parent->fBracket : Bracket::kNone;
3468}