blob: 0b4576c923cd953384231ce4d74a5e604b2486e2 [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) {
1855 auto doubleColon = bmhDef.fName.find("::");
1856 SkASSERT(string::npos != doubleColon); // more work to do to support global refs
1857 string className = bmhDef.fName.substr(0, doubleColon);
1858 const auto& iClass = fIClassMap.find(className);
1859 SkASSERT(fIClassMap.end() != iClass);
1860 string methodName = bmhDef.fName.substr(doubleColon + 2);
1861 auto& iTokens = iClass->second.fTokens;
1862 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1863 [methodName](Definition& token) {
1864 return MarkType::kMethod == token.fMarkType && methodName == token.fName; } );
1865 SkASSERT(iTokens.end() != iMethod);
1866 return &*iMethod;
1867}
1868
Cary Clark224c7002018-06-27 11:00:21 -04001869Definition* IncludeParser::parentBracket(Definition* parent) const {
1870 while (parent && Definition::Type::kBracket != parent->fType) {
1871 parent = parent->fParent;
1872 }
1873 return parent;
1874}
1875
1876Bracket IncludeParser::grandParentBracket() const {
1877 Definition* parent = parentBracket(fParent);
1878 parent = parentBracket(parent ? parent->fParent : nullptr);
1879 return parent ? parent->fBracket : Bracket::kNone;
1880}
1881
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001882bool IncludeParser::inAlignAs() const {
1883 if (fParent->fTokens.size() < 2) {
1884 return false;
1885 }
1886 auto reverseIter = fParent->fTokens.end();
1887 bool checkForBracket = true;
1888 while (fParent->fTokens.begin() != reverseIter) {
1889 std::advance(reverseIter, -1);
1890 if (checkForBracket) {
1891 if (Definition::Type::kBracket != reverseIter->fType) {
1892 return false;
1893 }
1894 if (Bracket::kParen != reverseIter->fBracket) {
1895 return false;
1896 }
1897 checkForBracket = false;
1898 continue;
1899 }
1900 if (Definition::Type::kKeyWord != reverseIter->fType) {
1901 return false;
1902 }
1903 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1904 }
1905 return false;
1906}
1907
Cary Clark61313f32018-10-08 14:57:48 -04001908const Definition* IncludeParser::include(string match) const {
1909 for (auto& entry : fIncludeMap) {
1910 if (string::npos == entry.first.find(match)) {
1911 continue;
1912 }
1913 return &entry.second;
1914 }
1915 SkASSERT(0);
1916 return nullptr;
1917}
1918
Cary Clark137b8742018-05-30 09:21:49 -04001919// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001920bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1921 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001922 // parse class header
1923 auto iter = includeDef->fTokens.begin();
1924 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1925 // todo : documentation is ignoring this for now
1926 iter = std::next(iter);
1927 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001928 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1929 if (hasAlignAs) {
1930 iter = std::next(iter);
1931 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1932 return includeDef->reportError<bool>("expected alignas argument");
1933 }
1934 iter = std::next(iter);
1935 }
Cary Clark8032b982017-07-28 11:04:54 -04001936 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1937 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001938 iter = std::next(iter);
1939 if (iter == includeDef->fTokens.end()) {
1940 return true; // forward declaration only
1941 }
Cary Clark8032b982017-07-28 11:04:54 -04001942 do {
1943 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001944 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001945 }
1946 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1947 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001948 }
Cary Clark8032b982017-07-28 11:04:54 -04001949 } while (static_cast<void>(iter = std::next(iter)), true);
1950 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001951 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001952 }
1953 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1954 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001955 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001956 }
1957 markupDef->fStart = iter->fStart;
1958 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001959 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001960 }
1961// if (1 != includeDef->fChildren.size()) {
1962// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1963// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001964 auto includeDefIter = includeDef->fChildren.begin();
1965 if (hasAlignAs) {
1966 SkASSERT(includeDef->fChildren.end() != includeDefIter);
1967 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
1968 std::advance(includeDefIter, 1);
1969 }
1970 if (includeDef->fChildren.end() != includeDefIter
1971 && Bracket::kAngle == (*includeDefIter)->fBracket) {
1972 std::advance(includeDefIter, 1);
1973 }
1974 includeDef = *includeDefIter;
1975 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001976 iter = includeDef->fTokens.begin();
1977 // skip until public
1978 int publicIndex = 0;
1979 if (IsStruct::kNo == isStruct) {
1980 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1981 size_t publicLen = strlen(publicName);
1982 while (iter != includeDef->fTokens.end()
1983 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1984 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001985 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001986 iter = std::next(iter);
1987 ++publicIndex;
1988 }
1989 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001990 int keyIndex = publicIndex;
1991 KeyWord currentKey = KeyWord::kPublic;
1992 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1993 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001994 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1995 size_t protectedLen = strlen(protectedName);
1996 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1997 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001998 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001999 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002000 std::advance(childIter, 1);
2001 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002002 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002003 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002004 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002005 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002006 const char* testStart = iter->fStart;
2007 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2008 iter = std::next(iter);
2009 ++keyIndex;
2010 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2011 currentKey = KeyWord::kPublic;
2012 break;
2013 }
2014 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2015 currentKey = KeyWord::kProtected;
2016 break;
2017 }
2018 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2019 currentKey = KeyWord::kPrivate;
2020 break;
2021 }
2022 }
2023 fLastObject = nullptr;
2024 if (KeyWord::kPublic == currentKey) {
2025 if (!this->parseObject(child, markupDef)) {
2026 return false;
2027 }
Cary Clark8032b982017-07-28 11:04:54 -04002028 }
Cary Clark73fa9722017-08-29 17:36:51 -04002029 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002030 childIter = std::next(childIter);
2031 }
Cary Clark137b8742018-05-30 09:21:49 -04002032 while (iter != includeDef->fTokens.end()) {
2033 iter->fPrivate = KeyWord::kPublic != currentKey;
2034 iter = std::next(iter);
2035 }
Cary Clark8032b982017-07-28 11:04:54 -04002036 SkASSERT(fParent->fParent);
2037 fParent = fParent->fParent;
2038 return true;
2039}
2040
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002041bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04002042 int lineCount, Definition* markupDef) {
2043 TextParser parser(filename, start, end, lineCount);
2044 // parse doxygen if present
2045 if (parser.startsWith("**")) {
2046 parser.next();
2047 parser.next();
2048 parser.skipWhiteSpace();
2049 if ('\\' == parser.peek()) {
2050 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002051 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2052 if (parser.skipExact("file")) {
2053 if (Definition::Type::kFileType != fParent->fType) {
2054 return reportError<bool>("expected parent is file");
2055 }
2056 string filename = markupDef->fileName();
2057 if (!parser.skipWord(filename.c_str())) {
2058 return reportError<bool>("missing object type");
2059 }
2060 } else if (parser.skipExact("fn")) {
2061 SkASSERT(0); // incomplete
2062 } else {
2063 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2064 return reportError<bool>("missing object type");
2065 }
2066 if (!parser.skipWord(markupDef->fName.c_str()) &&
2067 KeyWord::kEnum != markupDef->fKeyWord) {
2068 return reportError<bool>("missing object name");
2069 }
Cary Clark8032b982017-07-28 11:04:54 -04002070 }
Cary Clark8032b982017-07-28 11:04:54 -04002071 }
2072 }
2073 // remove leading '*' if present
2074 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2075 while (!parser.eof() && parser.skipWhiteSpace()) {
2076 while ('*' == parser.peek()) {
2077 parser.next();
2078 if (parser.eof()) {
2079 break;
2080 }
2081 parser.skipWhiteSpace();
2082 }
2083 if (parser.eof()) {
2084 break;
2085 }
2086 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002087 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002088 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002089 parser.skipToEndBracket('\n');
2090 }
2091 return true;
2092}
2093
Cary Clarkd98f78c2018-04-26 08:32:37 -04002094bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002095 if (!markupDef) {
2096 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2097 child->fLineCount, fParent, '\0');
2098 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002099 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002100 globalMarkupChild->fName = globalUniqueName;
2101 if (!this->findComments(*child, globalMarkupChild)) {
2102 return false;
2103 }
2104 fIConstMap[globalUniqueName] = globalMarkupChild;
2105 return true;
2106 }
2107 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2108 child->fLineCount, markupDef, '\0');
2109 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002110 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002111 markupChild->fTerminator = markupChild->fContentEnd;
2112 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002113 classDef.fConsts[child->fName] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002114 fIConstMap[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002115 return true;
2116}
2117
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002118bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2119 TextParser parser(child);
2120 if (!parser.skipExact("#define")) {
2121 return false;
2122 }
2123 if (!parser.skipSpace()) {
2124 return false;
2125 }
2126 const char* nameStart = parser.fChar;
2127 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2128 if (parser.eof()) {
2129 return true; // do nothing if #define doesn't define anything
2130 }
2131 string nameStr(nameStart, parser.fChar - nameStart);
2132 struct Param {
2133 const char* fStart;
2134 const char* fEnd;
2135 };
2136 vector<Param> params;
2137 if ('(' == parser.peek()) {
2138 parser.next();
2139 if (!parser.skipSpace()) {
2140 return false;
2141 }
2142 do {
2143 const char* paramStart = parser.fChar;
2144 if (!parser.skipExact("...")) {
2145 parser.skipToNonAlphaNum();
2146 }
2147 if (parser.eof()) {
2148 return false;
2149 }
2150 params.push_back({paramStart, parser.fChar});
2151 if (!parser.skipSpace()) {
2152 return false;
2153 }
2154 if (')' == parser.peek()) {
2155 parser.next();
2156 break;
2157 }
2158 if (',' != parser.next()) {
2159 return false;
2160 }
2161 if (!parser.skipSpace()) {
2162 return false;
2163 }
2164 } while (true);
2165 }
2166 if (!parser.skipSpace()) {
2167 return false;
2168 }
2169 if (!markupDef) {
2170 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2171 child->fLineCount, fParent, '\0');
2172 Definition* globalMarkupChild = &fGlobals.back();
2173 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2174 globalMarkupChild->fName = globalUniqueName;
2175 globalMarkupChild->fTerminator = child->fContentEnd;
2176 if (!this->findComments(*child, globalMarkupChild)) {
2177 return false;
2178 }
2179 fIDefineMap[globalUniqueName] = globalMarkupChild;
2180 for (Param param : params) {
2181 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2182 child->fLineCount, globalMarkupChild, '\0');
2183 Definition* paramChild = &globalMarkupChild->fTokens.back();
2184 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2185 paramChild->fTerminator = param.fEnd;
2186 }
2187 return true;
2188 }
2189 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2190 child->fLineCount, markupDef, '\0');
2191 Definition* markupChild = &markupDef->fTokens.back();
2192 markupChild->fName = nameStr;
2193 markupChild->fTerminator = markupChild->fContentEnd;
2194 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2195 if (!this->findComments(*child, markupChild)) {
2196 return false;
2197 }
2198 classDef.fDefines[nameStr] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002199 fIDefineMap[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002200 return true;
2201}
2202
2203bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002204 TextParser parser(child);
2205 parser.skipToEndBracket('{');
2206 if (parser.eof()) {
2207 return true; // if enum is a forward declaration, do nothing
2208 }
2209 parser.next();
2210 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002211 if (child->fTokens.size() > 0) {
2212 auto token = child->fTokens.begin();
2213 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2214 token = token->fTokens.begin();
2215 }
2216 if (Definition::Type::kWord == token->fType) {
2217 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
2218 }
2219 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002220 Definition* markupChild;
2221 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002222 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2223 child->fLineCount, fParent, '\0');
2224 markupChild = &fGlobals.back();
2225 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2226 markupChild->fName = globalUniqueName;
2227 markupChild->fTerminator = child->fContentEnd;
2228 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002229 } else {
2230 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002231 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002232 markupChild = &markupDef->fTokens.back();
2233 }
Cary Clark8032b982017-07-28 11:04:54 -04002234 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2235 markupChild->fKeyWord = KeyWord::kEnum;
2236 TextParser enumName(child);
2237 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05002238 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002239 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002240 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002241 markupChild->fMarkType = MarkType::kEnumClass;
2242 }
Cary Clark8032b982017-07-28 11:04:54 -04002243 const char* nameStart = enumName.fChar;
2244 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05002245 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04002246 markupChild->fName = markupDef->fName + "::" +
2247 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05002248 }
Cary Clark8032b982017-07-28 11:04:54 -04002249 if (!this->findComments(*child, markupChild)) {
2250 return false;
2251 }
Cary Clark8032b982017-07-28 11:04:54 -04002252 const char* dataEnd;
2253 do {
Cary Clark8032b982017-07-28 11:04:54 -04002254 parser.skipWhiteSpace();
2255 if ('}' == parser.peek()) {
2256 break;
2257 }
2258 Definition* comment = nullptr;
2259 // note that comment, if any, can be before or after (on the same line, though) as member
2260 if ('#' == parser.peek()) {
2261 // fixme: handle preprecessor, but just skip it for now
2262 parser.skipToLineStart();
2263 }
2264 while (parser.startsWith("/*") || parser.startsWith("//")) {
2265 parser.next();
2266 const char* start = parser.fChar;
2267 const char* end;
2268 if ('*' == parser.peek()) {
2269 end = parser.strnstr("*/", parser.fEnd);
2270 parser.fChar = end;
2271 parser.next();
2272 parser.next();
2273 } else {
2274 end = parser.trimmedLineEnd();
2275 parser.skipToLineStart();
2276 }
2277 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002278 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002279 comment = &markupChild->fTokens.back();
2280 comment->fTerminator = end;
2281 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
2282 return false;
2283 }
2284 parser.skipWhiteSpace();
2285 }
2286 parser.skipWhiteSpace();
2287 const char* memberStart = parser.fChar;
2288 if ('}' == memberStart[0]) {
2289 break;
2290 }
Cary Clark9174bda2017-09-19 17:39:32 -04002291 // if there's comment on same the line as member def, output first as if it was before
2292
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002293 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002294 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04002295 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04002296 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04002297 }
Cary Clark8032b982017-07-28 11:04:54 -04002298 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04002299 if ('=' == parser.peek()) {
2300 parser.skipToEndBracket(',');
2301 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002302 if (!parser.eof() && '#' == parser.peek()) {
2303 // fixme: handle preprecessor, but just skip it for now
2304 continue;
2305 }
Cary Clark9174bda2017-09-19 17:39:32 -04002306 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04002307 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04002308 }
2309 dataEnd = parser.fChar;
2310 const char* start = parser.anyOf("/\n");
2311 SkASSERT(start);
2312 parser.skipTo(start);
2313 if ('/' == parser.next()) {
2314 char slashStar = parser.next();
2315 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04002316 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04002317 char doxCheck = parser.next();
2318 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
2319 save.restore();
2320 }
2321 }
2322 parser.skipWhiteSpace();
2323 const char* commentStart = parser.fChar;
2324 if ('/' == slashStar) {
2325 parser.skipToEndBracket('\n');
2326 } else {
2327 parser.skipToEndBracket("*/");
2328 }
2329 SkASSERT(!parser.eof());
2330 const char* commentEnd = parser.fChar;
2331 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002332 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04002333 comment = &markupChild->fTokens.back();
2334 comment->fTerminator = commentEnd;
2335 }
Cary Clark8032b982017-07-28 11:04:54 -04002336 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002337 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002338 Definition* member = &markupChild->fTokens.back();
2339 member->fName = memberName;
2340 if (comment) {
2341 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04002342 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002343 }
2344 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04002345 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002346 for (auto outsideMember : child->fChildren) {
2347 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002348 continue;
2349 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002350 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2351 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002352 continue;
2353 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002354 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2355 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002356 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002357 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002358 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002359 // FIXME: ? add comment as well ?
2360 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002361 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002362 if (markupDef) {
2363 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2364 SkASSERT(classDef.fStart);
2365 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002366 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002367 markupChild->fName = uniqueName;
2368 classDef.fEnums[uniqueName] = markupChild;
Cary Clark61313f32018-10-08 14:57:48 -04002369 fIEnumMap[fullName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002370 }
Cary Clark8032b982017-07-28 11:04:54 -04002371 return true;
2372}
2373
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002374bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002375 fParent = &fIncludeMap[name];
2376 fParent->fName = name;
2377 fParent->fFileName = fFileName;
2378 fParent->fType = Definition::Type::kFileType;
2379 fParent->fContentStart = fChar;
2380 fParent->fContentEnd = fEnd;
2381 // parse include file into tree
2382 while (fChar < fEnd) {
2383 if (!this->parseChar()) {
2384 return false;
2385 }
2386 }
2387 // parse tree and add named objects to maps
2388 fParent = &fIncludeMap[name];
2389 if (!this->parseObjects(fParent, nullptr)) {
2390 return false;
2391 }
2392 return true;
2393}
2394
2395bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2396 const char* typeStart = child->fChildren[0]->fContentStart;
2397 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002398 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002399 Definition* markupChild = &markupDef->fTokens.back();
2400 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002401 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002402 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2403 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2404 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2405 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002406 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04002407 classDef.fMembers[uniqueName] = markupChild;
2408 if (child->fParentIndex >= 2) {
2409 auto comment = child->fParent->fTokens.begin();
2410 std::advance(comment, child->fParentIndex - 2);
2411 if (Definition::Type::kBracket == comment->fType
2412 && (Bracket::kSlashStar == comment->fBracket
2413 || Bracket::kSlashSlash == comment->fBracket)) {
2414 TextParser parser(&*comment);
2415 do {
2416 parser.skipToAlpha();
2417 if (parser.eof()) {
2418 break;
2419 }
2420 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002421 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002422 if (Bracket::kSlashStar == comment->fBracket) {
2423 const char* commentEnd = parser.strnstr("*/", end);
2424 if (commentEnd) {
2425 end = commentEnd;
2426 }
2427 }
2428 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002429 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002430 Definition* commentChild = &markupDef->fTokens.back();
2431 markupChild->fChildren.emplace_back(commentChild);
2432 parser.skipTo(end);
2433 } while (!parser.eof());
2434 }
2435 }
2436 return true;
2437}
2438
2439bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2440 auto tokenIter = child->fParent->fTokens.begin();
2441 std::advance(tokenIter, child->fParentIndex);
2442 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002443 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002444 bool addConst = false;
2445 auto operatorCheck = tokenIter;
2446 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2447 operatorCheck = std::prev(tokenIter);
2448 }
2449 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002450 auto closeParen = std::next(tokenIter);
2451 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2452 '(' == closeParen->fContentStart[0]);
2453 nameEnd = closeParen->fContentEnd + 1;
2454 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002455 if (Definition::Type::kKeyWord == closeParen->fType &&
2456 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002457 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002458 }
Cary Clarka560c472017-11-27 10:44:06 -05002459 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002460 }
2461 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002462 if (addConst) {
2463 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002464 }
Cary Clark8032b982017-07-28 11:04:54 -04002465 while (tokenIter != child->fParent->fTokens.begin()) {
2466 auto testIter = std::prev(tokenIter);
2467 switch (testIter->fType) {
2468 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002469 if (testIter == child->fParent->fTokens.begin() &&
2470 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2471 KeyWord::kIfndef == child->fParent->fKeyWord ||
2472 KeyWord::kIf == child->fParent->fKeyWord)) {
2473 std::next(tokenIter);
2474 break;
2475 }
Cary Clark8032b982017-07-28 11:04:54 -04002476 goto keepGoing;
2477 case Definition::Type::kKeyWord: {
2478 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2479 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2480 goto keepGoing;
2481 }
2482 } break;
2483 case Definition::Type::kBracket:
2484 if (Bracket::kAngle == testIter->fBracket) {
2485 goto keepGoing;
2486 }
2487 break;
2488 case Definition::Type::kPunctuation:
2489 if (Punctuation::kSemicolon == testIter->fPunctuation
2490 || Punctuation::kLeftBrace == testIter->fPunctuation
2491 || Punctuation::kColon == testIter->fPunctuation) {
2492 break;
2493 }
2494 keepGoing:
2495 tokenIter = testIter;
2496 continue;
2497 default:
2498 break;
2499 }
2500 break;
2501 }
Cary Clark224c7002018-06-27 11:00:21 -04002502 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002503 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002504 tokenIter->fPrivate = string::npos != nameStr.find("::")
2505 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002506 auto testIter = child->fParent->fTokens.begin();
2507 SkASSERT(child->fParentIndex > 0);
2508 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002509 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2510 0 == tokenIter->fParentIndex) {
2511 tokenIter = std::next(tokenIter);
2512 }
Cary Clark8032b982017-07-28 11:04:54 -04002513 const char* start = tokenIter->fContentStart;
2514 const char* end = tokenIter->fContentEnd;
2515 const char kDebugCodeStr[] = "SkDEBUGCODE";
2516 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2517 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2518 std::advance(testIter, 1);
2519 start = testIter->fContentStart + 1;
2520 end = testIter->fContentEnd - 1;
2521 } else {
2522 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002523 do {
2524 std::advance(testIter, 1);
2525 if (testIter == child->fParent->fTokens.end()) {
2526 break;
2527 }
Cary Clark8032b982017-07-28 11:04:54 -04002528 switch (testIter->fType) {
2529 case Definition::Type::kPunctuation:
2530 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2531 || Punctuation::kLeftBrace == testIter->fPunctuation
2532 || Punctuation::kColon == testIter->fPunctuation);
2533 end = testIter->fStart;
2534 break;
2535 case Definition::Type::kKeyWord: {
2536 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2537 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2538 continue;
2539 }
2540 } break;
2541 default:
2542 continue;
2543 }
2544 break;
Cary Clark61313f32018-10-08 14:57:48 -04002545 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002546 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002547 while (end > start && ' ' >= end[-1]) {
2548 --end;
2549 }
Cary Clark9174bda2017-09-19 17:39:32 -04002550 if (!markupDef) {
2551 auto parentIter = child->fParent->fTokens.begin();
2552 SkASSERT(child->fParentIndex > 0);
2553 std::advance(parentIter, child->fParentIndex - 1);
2554 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002555 TextParser nameParser(methodName);
2556 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002557 return true; // expect this is inline class definition outside of class
2558 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002559 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2560 fParent, '\0');
2561 Definition* globalMarkupChild = &fGlobals.back();
2562 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2563 globalMarkupChild->fName = globalUniqueName;
2564 if (!this->findComments(*child, globalMarkupChild)) {
2565 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002566 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002567 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002568 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002569 }
Cary Clark8032b982017-07-28 11:04:54 -04002570 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002571 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002572 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002573 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
2574 {
2575 auto mapIter = fIClassMap.find(markupDef->fName);
2576 SkASSERT(fIClassMap.end() != mapIter);
2577 IClassDefinition& classDef = mapIter->second;
2578 SkASSERT(classDef.fStart);
2579 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2580 markupChild->fName = uniqueName;
2581 if (!this->findComments(*child, markupChild)) {
2582 return false;
2583 }
2584 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002585 }
Cary Clark8032b982017-07-28 11:04:54 -04002586 return true;
2587}
2588
Cary Clark8032b982017-07-28 11:04:54 -04002589bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002590 fPriorObject = nullptr;
2591 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002592 if (!this->parseObject(child, markupDef)) {
2593 return false;
2594 }
Cary Clark0d225392018-06-07 09:59:07 -04002595 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002596 }
2597 return true;
2598}
2599
2600bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2601 // set up for error reporting
2602 fLine = fChar = child->fStart;
2603 fEnd = child->fContentEnd;
2604 // todo: put original line number in child as well
2605 switch (child->fType) {
2606 case Definition::Type::kKeyWord:
2607 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002608 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002609 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002610 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002611 }
2612 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002613 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002614 case KeyWord::kConst:
2615 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002616 if (!this->parseConst(child, markupDef)) {
2617 return child->reportError<bool>("failed to parse const or constexpr");
2618 }
2619 break;
Cary Clark8032b982017-07-28 11:04:54 -04002620 case KeyWord::kEnum:
2621 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002622 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002623 }
2624 break;
2625 case KeyWord::kStruct:
2626 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002627 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002628 }
2629 break;
2630 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002631 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002632 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002633 }
2634 break;
2635 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002636 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002637 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002638 }
2639 break;
2640 case KeyWord::kUnion:
2641 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002642 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002643 }
2644 break;
Cary Clark61313f32018-10-08 14:57:48 -04002645 case KeyWord::kUsing:
2646 if (!this->parseUsing()) {
2647 return child->reportError<bool>("failed to parse using");
2648 }
2649 break;
Cary Clark8032b982017-07-28 11:04:54 -04002650 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002651 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002652 }
2653 break;
2654 case Definition::Type::kBracket:
2655 switch (child->fBracket) {
2656 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002657 {
2658 auto tokenIter = child->fParent->fTokens.begin();
2659 std::advance(tokenIter, child->fParentIndex);
2660 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002661 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002662 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002663 break;
2664 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002665 if (Bracket::kPound == child->fParent->fBracket &&
2666 KeyWord::kIf == child->fParent->fKeyWord) {
2667 // TODO: this will skip methods named defined() -- for the
2668 // moment there aren't any
2669 if (previousToken.startsWith("defined")) {
2670 break;
2671 }
2672 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002673 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2674 break;
2675 }
Cary Clark73fa9722017-08-29 17:36:51 -04002676 }
Cary Clark0d225392018-06-07 09:59:07 -04002677 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2678 break;
2679 }
Cary Clark8032b982017-07-28 11:04:54 -04002680 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002681 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002682 }
Cary Clark73fa9722017-08-29 17:36:51 -04002683 break;
Cary Clark8032b982017-07-28 11:04:54 -04002684 case Bracket::kSlashSlash:
2685 case Bracket::kSlashStar:
2686 // comments are picked up by parsing objects first
2687 break;
2688 case Bracket::kPound:
2689 // special-case the #xxx xxx_DEFINED entries
2690 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002691 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002692 case KeyWord::kIfndef:
2693 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002694 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002695 if (!this->parseObjects(child, markupDef)) {
2696 return false;
2697 }
2698 break;
2699 }
2700 goto preproError;
2701 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002702 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002703 break;
2704 }
2705 goto preproError;
2706 case KeyWord::kEndif:
2707 if (child->boilerplateEndIf()) {
2708 break;
2709 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002710 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002711 case KeyWord::kInclude:
2712 // ignored for now
2713 break;
2714 case KeyWord::kElse:
2715 case KeyWord::kElif:
2716 // todo: handle these
2717 break;
2718 default:
2719 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002720 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002721 }
2722 break;
2723 case Bracket::kAngle:
2724 // pick up templated function pieces when method is found
2725 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002726 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002727 if (!this->parseObjects(child, markupDef)) {
2728 return false;
2729 }
Cary Clark73fa9722017-08-29 17:36:51 -04002730 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002731 case Bracket::kSquare: {
2732 // check to see if parent is operator, the only case we handle so far
2733 auto prev = child->fParent->fTokens.begin();
2734 std::advance(prev, child->fParentIndex - 1);
2735 if (KeyWord::kOperator != prev->fKeyWord) {
2736 return child->reportError<bool>("expected operator overload");
2737 }
2738 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002739 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002740 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002741 }
2742 break;
2743 case Definition::Type::kWord:
2744 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002745 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002746 }
2747 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002748 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002749 }
2750 break;
2751 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002752 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002753 break;
2754 }
2755 return true;
2756}
2757
Cary Clarkbbfda252018-03-09 15:32:01 -05002758bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2759 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002760}
2761
Cary Clark2f466242017-12-11 16:03:17 -05002762bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2763 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002764 typedefParser.skipExact("typedef");
2765 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002766 string nameStr = typedefParser.typedefName();
2767 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002768 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2769 child->fLineCount, fParent, '\0');
2770 Definition* globalMarkupChild = &fGlobals.back();
2771 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2772 globalMarkupChild->fName = globalUniqueName;
2773 if (!this->findComments(*child, globalMarkupChild)) {
2774 return false;
2775 }
2776 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002777 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002778 return true;
2779 }
2780 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002781 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002782 Definition* markupChild = &markupDef->fTokens.back();
2783 markupChild->fName = nameStr;
2784 markupChild->fTerminator = markupChild->fContentEnd;
2785 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2786 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002787 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04002788 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002789 return true;
2790}
2791
2792bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002793 // incomplete
2794 return true;
2795}
Cary Clark8032b982017-07-28 11:04:54 -04002796
Cary Clark61313f32018-10-08 14:57:48 -04002797bool IncludeParser::parseUsing() {
2798 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002799 return true;
2800}
2801
2802bool IncludeParser::parseChar() {
2803 char test = *fChar;
2804 if ('\\' == fPrev) {
2805 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002806// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002807 fLine = fChar + 1;
2808 }
2809 goto done;
2810 }
2811 switch (test) {
2812 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002813// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002814 fLine = fChar + 1;
2815 if (fInChar) {
2816 return reportError<bool>("malformed char");
2817 }
2818 if (fInString) {
2819 return reportError<bool>("malformed string");
2820 }
2821 if (!this->checkForWord()) {
2822 return false;
2823 }
2824 if (Bracket::kPound == this->topBracket()) {
2825 KeyWord keyWord = fParent->fKeyWord;
2826 if (KeyWord::kNone == keyWord) {
2827 return this->reportError<bool>("unhandled preprocessor directive");
2828 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002829 if (fInDefine) {
2830 SkASSERT(KeyWord::kDefine == keyWord);
2831 fInDefine = false;
2832 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002833 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002834 this->popBracket();
2835 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002836 if (fInBrace) {
2837 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2838 fInBrace = nullptr;
2839 }
Cary Clark8032b982017-07-28 11:04:54 -04002840 } else if (Bracket::kSlashSlash == this->topBracket()) {
2841 this->popBracket();
2842 }
2843 break;
2844 case '*':
2845 if (!fInCharCommentString && '/' == fPrev) {
2846 this->pushBracket(Bracket::kSlashStar);
2847 }
2848 if (!this->checkForWord()) {
2849 return false;
2850 }
2851 if (!fInCharCommentString) {
2852 this->addPunctuation(Punctuation::kAsterisk);
2853 }
2854 break;
2855 case '/':
2856 if ('*' == fPrev) {
2857 if (!fInCharCommentString) {
2858 return reportError<bool>("malformed closing comment");
2859 }
2860 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002861 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002862 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002863 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002864 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002865 }
2866 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002867 }
Cary Clark8032b982017-07-28 11:04:54 -04002868 if (!fInCharCommentString && '/' == fPrev) {
2869 this->pushBracket(Bracket::kSlashSlash);
2870 break;
2871 }
2872 if (!this->checkForWord()) {
2873 return false;
2874 }
2875 break;
2876 case '\'':
2877 if (Bracket::kChar == this->topBracket()) {
2878 this->popBracket();
2879 } else if (!fInComment && !fInString) {
2880 if (fIncludeWord) {
2881 return this->reportError<bool>("word then single-quote");
2882 }
2883 this->pushBracket(Bracket::kChar);
2884 }
2885 break;
2886 case '\"':
2887 if (Bracket::kString == this->topBracket()) {
2888 this->popBracket();
2889 } else if (!fInComment && !fInChar) {
2890 if (fIncludeWord) {
2891 return this->reportError<bool>("word then double-quote");
2892 }
2893 this->pushBracket(Bracket::kString);
2894 }
2895 break;
Cary Clark8032b982017-07-28 11:04:54 -04002896 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002897 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002898 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2899 this->pushBracket(Bracket::kDebugCode);
2900 break;
2901 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002902 case ':':
2903 case '[':
2904 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002905 if (fInCharCommentString) {
2906 break;
2907 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002908 if (fInDefine && fInBrace) {
2909 break;
2910 }
Cary Clark8032b982017-07-28 11:04:54 -04002911 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2912 break;
2913 }
Cary Clark0d225392018-06-07 09:59:07 -04002914 if (fConstExpr) {
2915 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2916 fConstExpr = nullptr;
2917 }
Cary Clark8032b982017-07-28 11:04:54 -04002918 if (!fInBrace) {
2919 if (!this->checkForWord()) {
2920 return false;
2921 }
2922 if (':' == test && !fInFunction) {
2923 break;
2924 }
2925 if ('{' == test) {
2926 this->addPunctuation(Punctuation::kLeftBrace);
2927 } else if (':' == test) {
2928 this->addPunctuation(Punctuation::kColon);
2929 }
2930 }
2931 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2932 && Bracket::kColon == fInBrace->fBracket) {
2933 Definition* braceParent = fParent->fParent;
2934 braceParent->fChildren.pop_back();
2935 braceParent->fTokens.pop_back();
2936 fParent = braceParent;
2937 fInBrace = nullptr;
2938 }
2939 this->pushBracket(
2940 '(' == test ? Bracket::kParen :
2941 '[' == test ? Bracket::kSquare :
2942 '{' == test ? Bracket::kBrace :
2943 Bracket::kColon);
2944 if (!fInBrace
2945 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2946 && fInFunction) {
2947 fInBrace = fParent;
2948 }
2949 } break;
2950 case '<':
2951 if (fInCharCommentString || fInBrace) {
2952 break;
2953 }
2954 if (!this->checkForWord()) {
2955 return false;
2956 }
2957 if (fInEnum) {
2958 break;
2959 }
2960 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002961 // this angle bracket may be an operator or may be a bracket
2962 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002963 break;
2964 case ')':
2965 case ']':
2966 case '}': {
2967 if (fInCharCommentString) {
2968 break;
2969 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002970 if (fInDefine && fInBrace) {
2971 break;
2972 }
Cary Clark8032b982017-07-28 11:04:54 -04002973 if (!fInBrace) {
2974 if (!this->checkForWord()) {
2975 return false;
2976 }
2977 }
2978 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002979 Bracket match = ')' == test ? Bracket::kParen :
2980 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2981 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002982 this->popBracket();
2983 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002984 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04002985 } else {
2986 fInFunction = '}' != test;
2987 }
Cary Clark73fa9722017-08-29 17:36:51 -04002988 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2989 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002990 } else if (Bracket::kAngle == this->topBracket()
2991 && match == this->grandParentBracket()) {
2992 this->popBracket();
2993 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002994 } else {
2995 return reportError<bool>("malformed close bracket");
2996 }
2997 if (popBraceParent) {
2998 Definition* braceParent = fInBrace->fParent;
2999 braceParent->fChildren.pop_back();
3000 braceParent->fTokens.pop_back();
3001 fInBrace = nullptr;
3002 }
3003 } break;
3004 case '>':
3005 if (fInCharCommentString || fInBrace) {
3006 break;
3007 }
3008 if (!this->checkForWord()) {
3009 return false;
3010 }
3011 if (fInEnum) {
3012 break;
3013 }
Cary Clarka560c472017-11-27 10:44:06 -05003014 if (Bracket::kPound == this->topBracket()) {
3015 break;
3016 }
Cary Clark8032b982017-07-28 11:04:54 -04003017 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003018 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003019 this->popBracket();
3020 } else {
3021 return reportError<bool>("malformed close angle bracket");
3022 }
3023 break;
3024 case '#': {
3025 if (fInCharCommentString || fInBrace) {
3026 break;
3027 }
3028 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3029 this->pushBracket(Bracket::kPound);
3030 break;
3031 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003032 case ' ':
3033 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3034 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3035 fInBrace = fParent;
3036 // delimiting brackets are space ... unescaped-linefeed
3037 }
Cary Clark8032b982017-07-28 11:04:54 -04003038 case '&':
3039 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003040 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003041 case '-':
3042 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003043 if (fInCharCommentString || fInBrace) {
3044 break;
3045 }
3046 if (!this->checkForWord()) {
3047 return false;
3048 }
3049 break;
Cary Clark0d225392018-06-07 09:59:07 -04003050 case '=':
3051 if (fInCharCommentString || fInBrace) {
3052 break;
3053 }
3054 if (!this->checkForWord()) {
3055 return false;
3056 }
Cary Clarkd7895502018-07-18 15:10:08 -04003057 if (!fParent->fTokens.size()) {
3058 break;
3059 }
Cary Clark0d225392018-06-07 09:59:07 -04003060 {
3061 const Definition& lastToken = fParent->fTokens.back();
3062 if (lastToken.fType != Definition::Type::kWord) {
3063 break;
3064 }
3065 string name(lastToken.fContentStart, lastToken.length());
3066 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3067 break;
3068 }
3069 // find token on start of line
3070 auto lineIter = fParent->fTokens.end();
3071 do {
Cary Clark80247e52018-07-11 16:18:41 -04003072 if (fParent->fTokens.begin() == lineIter) {
3073 break;
3074 }
Cary Clark0d225392018-06-07 09:59:07 -04003075 --lineIter;
3076 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003077 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003078 ++lineIter;
3079 }
3080 Definition* lineStart = &*lineIter;
3081 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3082 bool sawConst = false;
3083 bool sawStatic = false;
3084 bool sawTemplate = false;
3085 bool sawType = false;
3086 while (&lastToken != &*lineIter) {
3087 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3088 if (sawConst || sawStatic || sawTemplate) {
3089 sawConst = false;
3090 break;
3091 }
3092 if (&lastToken == &*++lineIter) {
3093 break;
3094 }
3095 if (KeyWord::kTypename != lineIter->fKeyWord) {
3096 break;
3097 }
3098 if (&lastToken == &*++lineIter) {
3099 break;
3100 }
3101 if (Definition::Type::kWord != lineIter->fType) {
3102 break;
3103 }
3104 sawTemplate = true;
3105 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3106 if (sawConst || sawStatic) {
3107 sawConst = false;
3108 break;
3109 }
3110 sawStatic = true;
3111 } else if (KeyWord::kConst == lineIter->fKeyWord
3112 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3113 if (sawConst) {
3114 sawConst = false;
3115 break;
3116 }
3117 sawConst = true;
3118 } else {
3119 if (sawType) {
3120 sawType = false;
3121 break;
3122 }
3123 if (Definition::Type::kKeyWord == lineIter->fType
3124 && KeyProperty::kNumber
3125 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3126 sawType = true;
3127 } else if (Definition::Type::kWord == lineIter->fType) {
3128 string typeName(lineIter->fContentStart, lineIter->length());
3129 if ("Sk" != name.substr(0, 2)) {
3130 sawType = true;
3131 }
3132 }
3133 }
3134 ++lineIter;
3135 }
3136 if (sawType && sawConst) {
3137 // if found, name first
3138 lineStart->fName = name;
3139 lineStart->fMarkType = MarkType::kConst;
3140 fParent->fChildren.emplace_back(lineStart);
3141 fConstExpr = lineStart;
3142 }
3143 }
3144 break;
Cary Clark8032b982017-07-28 11:04:54 -04003145 case ';':
3146 if (fInCharCommentString || fInBrace) {
3147 break;
3148 }
3149 if (!this->checkForWord()) {
3150 return false;
3151 }
Cary Clark0d225392018-06-07 09:59:07 -04003152 if (fConstExpr) {
3153 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3154 fConstExpr = nullptr;
3155 }
Cary Clark8032b982017-07-28 11:04:54 -04003156 if (Definition::Type::kKeyWord == fParent->fType
3157 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003158 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3159 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003160 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3161 this->popObject();
3162 }
Cary Clark8032b982017-07-28 11:04:54 -04003163 if (KeyWord::kEnum == fParent->fKeyWord) {
3164 fInEnum = false;
3165 }
Cary Clark61313f32018-10-08 14:57:48 -04003166 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003167 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003168 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3169 this->popObject();
3170 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003171 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003172 } else if (Definition::Type::kBracket == fParent->fType
3173 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3174 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3175 list<Definition>::iterator baseIter = fParent->fTokens.end();
3176 list<Definition>::iterator namedIter = fParent->fTokens.end();
3177 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003178 fParent->fTokens.begin() != tokenIter; ) {
3179 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003180 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003181 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003182 if (namedIter != fParent->fTokens.end()) {
3183 return reportError<bool>("found two named member tokens");
3184 }
3185 namedIter = tokenIter;
3186 }
3187 baseIter = tokenIter;
3188 } else {
3189 break;
3190 }
3191 }
3192 // FIXME: if a member definition spans multiple lines, this won't work
3193 if (namedIter != fParent->fTokens.end()) {
3194 if (baseIter == namedIter) {
3195 return this->reportError<bool>("expected type before named token");
3196 }
3197 Definition* member = &*namedIter;
3198 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003199 if (!member->fTerminator) {
3200 member->fTerminator = member->fContentEnd;
3201 }
Cary Clark8032b982017-07-28 11:04:54 -04003202 fParent->fChildren.push_back(member);
3203 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3204 member->fChildren.push_back(&*nameType);
3205 }
Cary Clark8032b982017-07-28 11:04:54 -04003206 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003207 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003208 } else if (fParent->fChildren.size() > 0) {
3209 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003210 Definition* priorEnum = fPriorEnum;
3211 fPriorEnum = nullptr;
3212 if (!priorEnum) {
3213 while (fParent->fChildren.begin() != lastIter) {
3214 std::advance(lastIter, -1);
3215 priorEnum = *lastIter;
3216 if (Definition::Type::kBracket != priorEnum->fType ||
3217 (Bracket::kSlashSlash != priorEnum->fBracket
3218 && Bracket::kSlashStar != priorEnum->fBracket)) {
3219 break;
3220 }
Cary Clark8032b982017-07-28 11:04:54 -04003221 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003222 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003223 }
3224 if (Definition::Type::kKeyWord == priorEnum->fType
3225 && KeyWord::kEnum == priorEnum->fKeyWord) {
3226 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003227 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003228 while (tokenWalker != fParent->fTokens.end()) {
3229 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003230 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003231 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3232 break;
3233 }
3234 }
3235 while (tokenWalker != fParent->fTokens.end()) {
3236 std::advance(tokenWalker, 1);
3237 const Definition* test = &*tokenWalker;
3238 if (Definition::Type::kBracket != test->fType ||
3239 (Bracket::kSlashSlash != test->fBracket
3240 && Bracket::kSlashStar != test->fBracket)) {
3241 break;
3242 }
3243 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003244 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003245 Definition* start = &*tokenWalker;
3246 bool foundExpected = true;
3247 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3248 const Definition* test = &*tokenWalker;
3249 if (expected != test->fKeyWord) {
3250 foundExpected = false;
3251 break;
3252 }
3253 if (tokenWalker == fParent->fTokens.end()) {
3254 break;
3255 }
3256 std::advance(tokenWalker, 1);
3257 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003258 if (!foundExpected) {
3259 foundExpected = true;
3260 tokenWalker = saveTokenWalker;
3261 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3262 const Definition* test = &*tokenWalker;
3263 if (expected != test->fKeyWord) {
3264 foundExpected = false;
3265 break;
3266 }
3267 if (tokenWalker == fParent->fTokens.end()) {
3268 break;
3269 }
3270 if (KeyWord::kNone != expected) {
3271 std::advance(tokenWalker, 1);
3272 }
3273 }
3274 if (foundExpected) {
3275 auto nameToken = priorEnum->fTokens.begin();
3276 string enumName = string(nameToken->fContentStart,
3277 nameToken->fContentEnd - nameToken->fContentStart);
3278 const Definition* test = &*tokenWalker;
3279 string constType = string(test->fContentStart,
3280 test->fContentEnd - test->fContentStart);
3281 if (enumName != constType) {
3282 foundExpected = false;
3283 } else {
3284 std::advance(tokenWalker, 1);
3285 }
3286 }
3287 }
Cary Clark8032b982017-07-28 11:04:54 -04003288 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3289 const char* nameStart = tokenWalker->fStart;
3290 std::advance(tokenWalker, 1);
3291 if (tokenWalker != fParent->fTokens.end()) {
3292 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003293 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003294 start->fName = string(nameStart, tp.fChar - nameStart);
3295 start->fContentEnd = fChar;
3296 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003297 fPriorEnum = priorEnum;
3298 }
Cary Clark8032b982017-07-28 11:04:54 -04003299 }
3300 }
3301 }
3302 this->addPunctuation(Punctuation::kSemicolon);
3303 fInFunction = false;
3304 break;
3305 case '~':
3306 if (fInEnum) {
3307 break;
3308 }
3309 case '0': case '1': case '2': case '3': case '4':
3310 case '5': case '6': case '7': case '8': case '9':
3311 // TODO: don't want to parse numbers, but do need to track for enum defs
3312 // break;
3313 case 'A': case 'B': case 'C': case 'D': case 'E':
3314 case 'F': case 'G': case 'H': case 'I': case 'J':
3315 case 'K': case 'L': case 'M': case 'N': case 'O':
3316 case 'P': case 'Q': case 'R': case 'S': case 'T':
3317 case 'U': case 'V': case 'W': case 'X': case 'Y':
3318 case 'Z': case '_':
3319 case 'a': case 'b': case 'c': case 'd': case 'e':
3320 case 'f': case 'g': case 'h': case 'i': case 'j':
3321 case 'k': case 'l': case 'm': case 'n': case 'o':
3322 case 'p': case 'q': case 'r': case 's': case 't':
3323 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003324 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003325 if (fInCharCommentString || fInBrace) {
3326 break;
3327 }
3328 if (!fIncludeWord) {
3329 fIncludeWord = fChar;
3330 }
3331 break;
3332 }
3333done:
3334 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003335 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003336 return true;
3337}
3338
3339void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003340 IncludeParser::ValidateKeyWords();
3341}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003342
Cary Clark186d08f2018-04-03 08:43:27 -04003343bool IncludeParser::references(const SkString& file) const {
3344 // if includes weren't passed one at a time, assume all references are valid
3345 if (fIncludeMap.empty()) {
3346 return true;
3347 }
3348 SkASSERT(file.endsWith(".bmh") );
3349 string root(file.c_str(), file.size() - 4);
3350 string kReference("_Reference");
3351 if (string::npos != root.find(kReference)) {
3352 root = root.substr(0, root.length() - kReference.length());
3353 }
3354 if (fIClassMap.end() != fIClassMap.find(root)) {
3355 return true;
3356 }
3357 if (fIStructMap.end() != fIStructMap.find(root)) {
3358 return true;
3359 }
Cary Clark224c7002018-06-27 11:00:21 -04003360 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3361 return true;
3362 }
Cary Clarka90ea222018-10-16 10:30:28 -04003363 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3364 return true;
3365 }
Cary Clark224c7002018-06-27 11:00:21 -04003366 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3367 return true;
3368 }
Cary Clark186d08f2018-04-03 08:43:27 -04003369 return false;
3370}
3371
Cary Clark2dc84ad2018-01-26 12:56:22 -05003372void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3373 if (!sk_isdir(includes)) {
3374 IncludeParser::RemoveOneFile(docs, includes);
3375 } else {
3376 SkOSFile::Iter it(includes, ".h");
3377 for (SkString file; it.next(&file); ) {
3378 SkString p = SkOSPath::Join(includes, file.c_str());
3379 const char* hunk = p.c_str();
3380 if (!SkStrEndsWith(hunk, ".h")) {
3381 continue;
3382 }
3383 IncludeParser::RemoveOneFile(docs, hunk);
3384 }
3385 }
3386}
3387
3388void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3389 const char* lastForward = strrchr(includesFile, '/');
3390 const char* lastBackward = strrchr(includesFile, '\\');
3391 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3392 if (!last) {
3393 last = includesFile;
3394 } else {
3395 last += 1;
3396 }
3397 SkString baseName(last);
3398 SkASSERT(baseName.endsWith(".h"));
3399 baseName.remove(baseName.size() - 2, 2);
3400 baseName.append("_Reference.bmh");
3401 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3402 remove(fullName.c_str());
3403}
Cary Clark224c7002018-06-27 11:00:21 -04003404
3405Bracket IncludeParser::topBracket() const {
3406 Definition* parent = this->parentBracket(fParent);
3407 return parent ? parent->fBracket : Bracket::kNone;
3408}