blob: ad771198a3f49e94e2ab78c6b5ee879e0db4ba5a [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 }
1768 for (auto entry : *mapPtr) {
1769 if (string::npos == entry.first.find(filterContents)) {
1770 continue;
1771 }
1772 result += this->writeCodeBlock(*entry.second, markType);
1773 }
1774 return result;
1775}
1776
Cary Clark8032b982017-07-28 11:04:54 -04001777bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1778 // add comment preceding class, if any
1779 const Definition* parent = includeDef.fParent;
1780 int index = includeDef.fParentIndex;
1781 auto wordIter = parent->fTokens.begin();
1782 std::advance(wordIter, index);
1783 SkASSERT(&*wordIter == &includeDef);
1784 while (parent->fTokens.begin() != wordIter) {
1785 auto testIter = std::prev(wordIter);
1786 if (Definition::Type::kWord != testIter->fType
1787 && Definition::Type::kKeyWord != testIter->fType
1788 && (Definition::Type::kBracket != testIter->fType
1789 || Bracket::kAngle != testIter->fBracket)
1790 && (Definition::Type::kPunctuation != testIter->fType
1791 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1792 break;
1793 }
1794 wordIter = testIter;
1795 }
1796 auto commentIter = wordIter;
1797 while (parent->fTokens.begin() != commentIter) {
1798 auto testIter = std::prev(commentIter);
1799 bool isComment = Definition::Type::kBracket == testIter->fType
1800 && (Bracket::kSlashSlash == testIter->fBracket
1801 || Bracket::kSlashStar == testIter->fBracket);
1802 if (!isComment) {
1803 break;
1804 }
1805 commentIter = testIter;
1806 }
1807 while (commentIter != wordIter) {
1808 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1809 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1810 return false;
1811 }
1812 commentIter = std::next(commentIter);
1813 }
1814 return true;
1815}
1816
Cary Clark0d225392018-06-07 09:59:07 -04001817Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1818 string typeName) {
1819 typedef Definition* DefinitionPtr;
1820 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1821 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1822 if (mapIter == fMaps.end()) {
1823 return nullptr;
1824 }
1825 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1826 return reportError<DefinitionPtr>("invalid mark type");
1827 }
1828 string name = this->uniqueName(*mapIter->fInclude, typeName);
1829 Definition& markupDef = *(*mapIter->fInclude)[name];
1830 if (markupDef.fStart) {
1831 return reportError<DefinitionPtr>("definition already defined");
1832 }
1833 markupDef.fFileName = fFileName;
1834 markupDef.fStart = includeDef.fStart;
1835 markupDef.fContentStart = includeDef.fStart;
1836 markupDef.fName = name;
1837 markupDef.fContentEnd = includeDef.fContentEnd;
1838 markupDef.fTerminator = includeDef.fTerminator;
1839 markupDef.fParent = fParent;
1840 markupDef.fLineCount = includeDef.fLineCount;
1841 markupDef.fMarkType = markType;
1842 markupDef.fKeyWord = includeDef.fKeyWord;
1843 markupDef.fType = Definition::Type::kMark;
1844 return &markupDef;
1845}
1846
Cary Clark224c7002018-06-27 11:00:21 -04001847Definition* IncludeParser::parentBracket(Definition* parent) const {
1848 while (parent && Definition::Type::kBracket != parent->fType) {
1849 parent = parent->fParent;
1850 }
1851 return parent;
1852}
1853
1854Bracket IncludeParser::grandParentBracket() const {
1855 Definition* parent = parentBracket(fParent);
1856 parent = parentBracket(parent ? parent->fParent : nullptr);
1857 return parent ? parent->fBracket : Bracket::kNone;
1858}
1859
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001860bool IncludeParser::inAlignAs() const {
1861 if (fParent->fTokens.size() < 2) {
1862 return false;
1863 }
1864 auto reverseIter = fParent->fTokens.end();
1865 bool checkForBracket = true;
1866 while (fParent->fTokens.begin() != reverseIter) {
1867 std::advance(reverseIter, -1);
1868 if (checkForBracket) {
1869 if (Definition::Type::kBracket != reverseIter->fType) {
1870 return false;
1871 }
1872 if (Bracket::kParen != reverseIter->fBracket) {
1873 return false;
1874 }
1875 checkForBracket = false;
1876 continue;
1877 }
1878 if (Definition::Type::kKeyWord != reverseIter->fType) {
1879 return false;
1880 }
1881 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1882 }
1883 return false;
1884}
1885
Cary Clark61313f32018-10-08 14:57:48 -04001886const Definition* IncludeParser::include(string match) const {
1887 for (auto& entry : fIncludeMap) {
1888 if (string::npos == entry.first.find(match)) {
1889 continue;
1890 }
1891 return &entry.second;
1892 }
1893 SkASSERT(0);
1894 return nullptr;
1895}
1896
Cary Clark137b8742018-05-30 09:21:49 -04001897// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001898bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1899 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001900 // parse class header
1901 auto iter = includeDef->fTokens.begin();
1902 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1903 // todo : documentation is ignoring this for now
1904 iter = std::next(iter);
1905 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001906 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1907 if (hasAlignAs) {
1908 iter = std::next(iter);
1909 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1910 return includeDef->reportError<bool>("expected alignas argument");
1911 }
1912 iter = std::next(iter);
1913 }
Cary Clark8032b982017-07-28 11:04:54 -04001914 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1915 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001916 iter = std::next(iter);
1917 if (iter == includeDef->fTokens.end()) {
1918 return true; // forward declaration only
1919 }
Cary Clark8032b982017-07-28 11:04:54 -04001920 do {
1921 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001922 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001923 }
1924 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1925 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001926 }
Cary Clark8032b982017-07-28 11:04:54 -04001927 } while (static_cast<void>(iter = std::next(iter)), true);
1928 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001929 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001930 }
1931 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1932 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001933 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001934 }
1935 markupDef->fStart = iter->fStart;
1936 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001937 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001938 }
1939// if (1 != includeDef->fChildren.size()) {
1940// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1941// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001942 auto includeDefIter = includeDef->fChildren.begin();
1943 if (hasAlignAs) {
1944 SkASSERT(includeDef->fChildren.end() != includeDefIter);
1945 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
1946 std::advance(includeDefIter, 1);
1947 }
1948 if (includeDef->fChildren.end() != includeDefIter
1949 && Bracket::kAngle == (*includeDefIter)->fBracket) {
1950 std::advance(includeDefIter, 1);
1951 }
1952 includeDef = *includeDefIter;
1953 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001954 iter = includeDef->fTokens.begin();
1955 // skip until public
1956 int publicIndex = 0;
1957 if (IsStruct::kNo == isStruct) {
1958 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1959 size_t publicLen = strlen(publicName);
1960 while (iter != includeDef->fTokens.end()
1961 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1962 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001963 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001964 iter = std::next(iter);
1965 ++publicIndex;
1966 }
1967 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001968 int keyIndex = publicIndex;
1969 KeyWord currentKey = KeyWord::kPublic;
1970 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1971 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001972 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1973 size_t protectedLen = strlen(protectedName);
1974 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1975 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001976 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001977 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04001978 std::advance(childIter, 1);
1979 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001980 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001981 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001982 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001983 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001984 const char* testStart = iter->fStart;
1985 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1986 iter = std::next(iter);
1987 ++keyIndex;
1988 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1989 currentKey = KeyWord::kPublic;
1990 break;
1991 }
1992 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1993 currentKey = KeyWord::kProtected;
1994 break;
1995 }
1996 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1997 currentKey = KeyWord::kPrivate;
1998 break;
1999 }
2000 }
2001 fLastObject = nullptr;
2002 if (KeyWord::kPublic == currentKey) {
2003 if (!this->parseObject(child, markupDef)) {
2004 return false;
2005 }
Cary Clark8032b982017-07-28 11:04:54 -04002006 }
Cary Clark73fa9722017-08-29 17:36:51 -04002007 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002008 childIter = std::next(childIter);
2009 }
Cary Clark137b8742018-05-30 09:21:49 -04002010 while (iter != includeDef->fTokens.end()) {
2011 iter->fPrivate = KeyWord::kPublic != currentKey;
2012 iter = std::next(iter);
2013 }
Cary Clark8032b982017-07-28 11:04:54 -04002014 SkASSERT(fParent->fParent);
2015 fParent = fParent->fParent;
2016 return true;
2017}
2018
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002019bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04002020 int lineCount, Definition* markupDef) {
2021 TextParser parser(filename, start, end, lineCount);
2022 // parse doxygen if present
2023 if (parser.startsWith("**")) {
2024 parser.next();
2025 parser.next();
2026 parser.skipWhiteSpace();
2027 if ('\\' == parser.peek()) {
2028 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002029 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2030 if (parser.skipExact("file")) {
2031 if (Definition::Type::kFileType != fParent->fType) {
2032 return reportError<bool>("expected parent is file");
2033 }
2034 string filename = markupDef->fileName();
2035 if (!parser.skipWord(filename.c_str())) {
2036 return reportError<bool>("missing object type");
2037 }
2038 } else if (parser.skipExact("fn")) {
2039 SkASSERT(0); // incomplete
2040 } else {
2041 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2042 return reportError<bool>("missing object type");
2043 }
2044 if (!parser.skipWord(markupDef->fName.c_str()) &&
2045 KeyWord::kEnum != markupDef->fKeyWord) {
2046 return reportError<bool>("missing object name");
2047 }
Cary Clark8032b982017-07-28 11:04:54 -04002048 }
Cary Clark8032b982017-07-28 11:04:54 -04002049 }
2050 }
2051 // remove leading '*' if present
2052 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2053 while (!parser.eof() && parser.skipWhiteSpace()) {
2054 while ('*' == parser.peek()) {
2055 parser.next();
2056 if (parser.eof()) {
2057 break;
2058 }
2059 parser.skipWhiteSpace();
2060 }
2061 if (parser.eof()) {
2062 break;
2063 }
2064 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002065 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002066 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002067 parser.skipToEndBracket('\n');
2068 }
2069 return true;
2070}
2071
Cary Clarkd98f78c2018-04-26 08:32:37 -04002072bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002073 if (!markupDef) {
2074 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2075 child->fLineCount, fParent, '\0');
2076 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002077 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002078 globalMarkupChild->fName = globalUniqueName;
2079 if (!this->findComments(*child, globalMarkupChild)) {
2080 return false;
2081 }
2082 fIConstMap[globalUniqueName] = globalMarkupChild;
2083 return true;
2084 }
2085 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2086 child->fLineCount, markupDef, '\0');
2087 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002088 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002089 markupChild->fTerminator = markupChild->fContentEnd;
2090 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002091 classDef.fConsts[child->fName] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002092 fIConstMap[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002093 return true;
2094}
2095
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002096bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2097 TextParser parser(child);
2098 if (!parser.skipExact("#define")) {
2099 return false;
2100 }
2101 if (!parser.skipSpace()) {
2102 return false;
2103 }
2104 const char* nameStart = parser.fChar;
2105 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2106 if (parser.eof()) {
2107 return true; // do nothing if #define doesn't define anything
2108 }
2109 string nameStr(nameStart, parser.fChar - nameStart);
2110 struct Param {
2111 const char* fStart;
2112 const char* fEnd;
2113 };
2114 vector<Param> params;
2115 if ('(' == parser.peek()) {
2116 parser.next();
2117 if (!parser.skipSpace()) {
2118 return false;
2119 }
2120 do {
2121 const char* paramStart = parser.fChar;
2122 if (!parser.skipExact("...")) {
2123 parser.skipToNonAlphaNum();
2124 }
2125 if (parser.eof()) {
2126 return false;
2127 }
2128 params.push_back({paramStart, parser.fChar});
2129 if (!parser.skipSpace()) {
2130 return false;
2131 }
2132 if (')' == parser.peek()) {
2133 parser.next();
2134 break;
2135 }
2136 if (',' != parser.next()) {
2137 return false;
2138 }
2139 if (!parser.skipSpace()) {
2140 return false;
2141 }
2142 } while (true);
2143 }
2144 if (!parser.skipSpace()) {
2145 return false;
2146 }
2147 if (!markupDef) {
2148 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2149 child->fLineCount, fParent, '\0');
2150 Definition* globalMarkupChild = &fGlobals.back();
2151 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2152 globalMarkupChild->fName = globalUniqueName;
2153 globalMarkupChild->fTerminator = child->fContentEnd;
2154 if (!this->findComments(*child, globalMarkupChild)) {
2155 return false;
2156 }
2157 fIDefineMap[globalUniqueName] = globalMarkupChild;
2158 for (Param param : params) {
2159 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2160 child->fLineCount, globalMarkupChild, '\0');
2161 Definition* paramChild = &globalMarkupChild->fTokens.back();
2162 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2163 paramChild->fTerminator = param.fEnd;
2164 }
2165 return true;
2166 }
2167 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2168 child->fLineCount, markupDef, '\0');
2169 Definition* markupChild = &markupDef->fTokens.back();
2170 markupChild->fName = nameStr;
2171 markupChild->fTerminator = markupChild->fContentEnd;
2172 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2173 if (!this->findComments(*child, markupChild)) {
2174 return false;
2175 }
2176 classDef.fDefines[nameStr] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002177 fIDefineMap[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002178 return true;
2179}
2180
2181bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002182 TextParser parser(child);
2183 parser.skipToEndBracket('{');
2184 if (parser.eof()) {
2185 return true; // if enum is a forward declaration, do nothing
2186 }
2187 parser.next();
2188 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002189 if (child->fTokens.size() > 0) {
2190 auto token = child->fTokens.begin();
2191 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2192 token = token->fTokens.begin();
2193 }
2194 if (Definition::Type::kWord == token->fType) {
2195 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
2196 }
2197 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002198 Definition* markupChild;
2199 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002200 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2201 child->fLineCount, fParent, '\0');
2202 markupChild = &fGlobals.back();
2203 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2204 markupChild->fName = globalUniqueName;
2205 markupChild->fTerminator = child->fContentEnd;
2206 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002207 } else {
2208 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002209 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002210 markupChild = &markupDef->fTokens.back();
2211 }
Cary Clark8032b982017-07-28 11:04:54 -04002212 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2213 markupChild->fKeyWord = KeyWord::kEnum;
2214 TextParser enumName(child);
2215 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05002216 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002217 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002218 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002219 markupChild->fMarkType = MarkType::kEnumClass;
2220 }
Cary Clark8032b982017-07-28 11:04:54 -04002221 const char* nameStart = enumName.fChar;
2222 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05002223 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04002224 markupChild->fName = markupDef->fName + "::" +
2225 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05002226 }
Cary Clark8032b982017-07-28 11:04:54 -04002227 if (!this->findComments(*child, markupChild)) {
2228 return false;
2229 }
Cary Clark8032b982017-07-28 11:04:54 -04002230 const char* dataEnd;
2231 do {
Cary Clark8032b982017-07-28 11:04:54 -04002232 parser.skipWhiteSpace();
2233 if ('}' == parser.peek()) {
2234 break;
2235 }
2236 Definition* comment = nullptr;
2237 // note that comment, if any, can be before or after (on the same line, though) as member
2238 if ('#' == parser.peek()) {
2239 // fixme: handle preprecessor, but just skip it for now
2240 parser.skipToLineStart();
2241 }
2242 while (parser.startsWith("/*") || parser.startsWith("//")) {
2243 parser.next();
2244 const char* start = parser.fChar;
2245 const char* end;
2246 if ('*' == parser.peek()) {
2247 end = parser.strnstr("*/", parser.fEnd);
2248 parser.fChar = end;
2249 parser.next();
2250 parser.next();
2251 } else {
2252 end = parser.trimmedLineEnd();
2253 parser.skipToLineStart();
2254 }
2255 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002256 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002257 comment = &markupChild->fTokens.back();
2258 comment->fTerminator = end;
2259 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
2260 return false;
2261 }
2262 parser.skipWhiteSpace();
2263 }
2264 parser.skipWhiteSpace();
2265 const char* memberStart = parser.fChar;
2266 if ('}' == memberStart[0]) {
2267 break;
2268 }
Cary Clark9174bda2017-09-19 17:39:32 -04002269 // if there's comment on same the line as member def, output first as if it was before
2270
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002271 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002272 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04002273 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04002274 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04002275 }
Cary Clark8032b982017-07-28 11:04:54 -04002276 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04002277 if ('=' == parser.peek()) {
2278 parser.skipToEndBracket(',');
2279 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002280 if (!parser.eof() && '#' == parser.peek()) {
2281 // fixme: handle preprecessor, but just skip it for now
2282 continue;
2283 }
Cary Clark9174bda2017-09-19 17:39:32 -04002284 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04002285 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04002286 }
2287 dataEnd = parser.fChar;
2288 const char* start = parser.anyOf("/\n");
2289 SkASSERT(start);
2290 parser.skipTo(start);
2291 if ('/' == parser.next()) {
2292 char slashStar = parser.next();
2293 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04002294 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04002295 char doxCheck = parser.next();
2296 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
2297 save.restore();
2298 }
2299 }
2300 parser.skipWhiteSpace();
2301 const char* commentStart = parser.fChar;
2302 if ('/' == slashStar) {
2303 parser.skipToEndBracket('\n');
2304 } else {
2305 parser.skipToEndBracket("*/");
2306 }
2307 SkASSERT(!parser.eof());
2308 const char* commentEnd = parser.fChar;
2309 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002310 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04002311 comment = &markupChild->fTokens.back();
2312 comment->fTerminator = commentEnd;
2313 }
Cary Clark8032b982017-07-28 11:04:54 -04002314 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002315 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002316 Definition* member = &markupChild->fTokens.back();
2317 member->fName = memberName;
2318 if (comment) {
2319 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04002320 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002321 }
2322 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04002323 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002324 for (auto outsideMember : child->fChildren) {
2325 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002326 continue;
2327 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002328 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2329 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002330 continue;
2331 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002332 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2333 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002334 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002335 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002336 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002337 // FIXME: ? add comment as well ?
2338 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002339 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002340 if (markupDef) {
2341 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2342 SkASSERT(classDef.fStart);
2343 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002344 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002345 markupChild->fName = uniqueName;
2346 classDef.fEnums[uniqueName] = markupChild;
Cary Clark61313f32018-10-08 14:57:48 -04002347 fIEnumMap[fullName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002348 }
Cary Clark8032b982017-07-28 11:04:54 -04002349 return true;
2350}
2351
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002352bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002353 fParent = &fIncludeMap[name];
2354 fParent->fName = name;
2355 fParent->fFileName = fFileName;
2356 fParent->fType = Definition::Type::kFileType;
2357 fParent->fContentStart = fChar;
2358 fParent->fContentEnd = fEnd;
2359 // parse include file into tree
2360 while (fChar < fEnd) {
2361 if (!this->parseChar()) {
2362 return false;
2363 }
2364 }
2365 // parse tree and add named objects to maps
2366 fParent = &fIncludeMap[name];
2367 if (!this->parseObjects(fParent, nullptr)) {
2368 return false;
2369 }
2370 return true;
2371}
2372
2373bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2374 const char* typeStart = child->fChildren[0]->fContentStart;
2375 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002376 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002377 Definition* markupChild = &markupDef->fTokens.back();
2378 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002379 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002380 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2381 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2382 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2383 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002384 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04002385 classDef.fMembers[uniqueName] = markupChild;
2386 if (child->fParentIndex >= 2) {
2387 auto comment = child->fParent->fTokens.begin();
2388 std::advance(comment, child->fParentIndex - 2);
2389 if (Definition::Type::kBracket == comment->fType
2390 && (Bracket::kSlashStar == comment->fBracket
2391 || Bracket::kSlashSlash == comment->fBracket)) {
2392 TextParser parser(&*comment);
2393 do {
2394 parser.skipToAlpha();
2395 if (parser.eof()) {
2396 break;
2397 }
2398 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002399 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002400 if (Bracket::kSlashStar == comment->fBracket) {
2401 const char* commentEnd = parser.strnstr("*/", end);
2402 if (commentEnd) {
2403 end = commentEnd;
2404 }
2405 }
2406 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002407 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002408 Definition* commentChild = &markupDef->fTokens.back();
2409 markupChild->fChildren.emplace_back(commentChild);
2410 parser.skipTo(end);
2411 } while (!parser.eof());
2412 }
2413 }
2414 return true;
2415}
2416
2417bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2418 auto tokenIter = child->fParent->fTokens.begin();
2419 std::advance(tokenIter, child->fParentIndex);
2420 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002421 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002422 bool addConst = false;
2423 auto operatorCheck = tokenIter;
2424 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2425 operatorCheck = std::prev(tokenIter);
2426 }
2427 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002428 auto closeParen = std::next(tokenIter);
2429 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2430 '(' == closeParen->fContentStart[0]);
2431 nameEnd = closeParen->fContentEnd + 1;
2432 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002433 if (Definition::Type::kKeyWord == closeParen->fType &&
2434 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002435 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002436 }
Cary Clarka560c472017-11-27 10:44:06 -05002437 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002438 }
2439 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002440 if (addConst) {
2441 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002442 }
Cary Clark8032b982017-07-28 11:04:54 -04002443 while (tokenIter != child->fParent->fTokens.begin()) {
2444 auto testIter = std::prev(tokenIter);
2445 switch (testIter->fType) {
2446 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002447 if (testIter == child->fParent->fTokens.begin() &&
2448 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2449 KeyWord::kIfndef == child->fParent->fKeyWord ||
2450 KeyWord::kIf == child->fParent->fKeyWord)) {
2451 std::next(tokenIter);
2452 break;
2453 }
Cary Clark8032b982017-07-28 11:04:54 -04002454 goto keepGoing;
2455 case Definition::Type::kKeyWord: {
2456 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2457 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2458 goto keepGoing;
2459 }
2460 } break;
2461 case Definition::Type::kBracket:
2462 if (Bracket::kAngle == testIter->fBracket) {
2463 goto keepGoing;
2464 }
2465 break;
2466 case Definition::Type::kPunctuation:
2467 if (Punctuation::kSemicolon == testIter->fPunctuation
2468 || Punctuation::kLeftBrace == testIter->fPunctuation
2469 || Punctuation::kColon == testIter->fPunctuation) {
2470 break;
2471 }
2472 keepGoing:
2473 tokenIter = testIter;
2474 continue;
2475 default:
2476 break;
2477 }
2478 break;
2479 }
Cary Clark224c7002018-06-27 11:00:21 -04002480 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002481 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002482 tokenIter->fPrivate = string::npos != nameStr.find("::")
2483 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002484 auto testIter = child->fParent->fTokens.begin();
2485 SkASSERT(child->fParentIndex > 0);
2486 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002487 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2488 0 == tokenIter->fParentIndex) {
2489 tokenIter = std::next(tokenIter);
2490 }
Cary Clark8032b982017-07-28 11:04:54 -04002491 const char* start = tokenIter->fContentStart;
2492 const char* end = tokenIter->fContentEnd;
2493 const char kDebugCodeStr[] = "SkDEBUGCODE";
2494 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2495 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2496 std::advance(testIter, 1);
2497 start = testIter->fContentStart + 1;
2498 end = testIter->fContentEnd - 1;
2499 } else {
2500 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002501 do {
2502 std::advance(testIter, 1);
2503 if (testIter == child->fParent->fTokens.end()) {
2504 break;
2505 }
Cary Clark8032b982017-07-28 11:04:54 -04002506 switch (testIter->fType) {
2507 case Definition::Type::kPunctuation:
2508 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2509 || Punctuation::kLeftBrace == testIter->fPunctuation
2510 || Punctuation::kColon == testIter->fPunctuation);
2511 end = testIter->fStart;
2512 break;
2513 case Definition::Type::kKeyWord: {
2514 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2515 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2516 continue;
2517 }
2518 } break;
2519 default:
2520 continue;
2521 }
2522 break;
Cary Clark61313f32018-10-08 14:57:48 -04002523 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002524 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002525 while (end > start && ' ' >= end[-1]) {
2526 --end;
2527 }
Cary Clark9174bda2017-09-19 17:39:32 -04002528 if (!markupDef) {
2529 auto parentIter = child->fParent->fTokens.begin();
2530 SkASSERT(child->fParentIndex > 0);
2531 std::advance(parentIter, child->fParentIndex - 1);
2532 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002533 TextParser nameParser(methodName);
2534 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002535 return true; // expect this is inline class definition outside of class
2536 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002537 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2538 fParent, '\0');
2539 Definition* globalMarkupChild = &fGlobals.back();
2540 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2541 globalMarkupChild->fName = globalUniqueName;
2542 if (!this->findComments(*child, globalMarkupChild)) {
2543 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002544 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002545 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002546 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002547 }
Cary Clark8032b982017-07-28 11:04:54 -04002548 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002549 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002550 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002551 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
2552 {
2553 auto mapIter = fIClassMap.find(markupDef->fName);
2554 SkASSERT(fIClassMap.end() != mapIter);
2555 IClassDefinition& classDef = mapIter->second;
2556 SkASSERT(classDef.fStart);
2557 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2558 markupChild->fName = uniqueName;
2559 if (!this->findComments(*child, markupChild)) {
2560 return false;
2561 }
2562 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002563 }
Cary Clark8032b982017-07-28 11:04:54 -04002564 return true;
2565}
2566
Cary Clark8032b982017-07-28 11:04:54 -04002567bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002568 fPriorObject = nullptr;
2569 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002570 if (!this->parseObject(child, markupDef)) {
2571 return false;
2572 }
Cary Clark0d225392018-06-07 09:59:07 -04002573 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002574 }
2575 return true;
2576}
2577
2578bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2579 // set up for error reporting
2580 fLine = fChar = child->fStart;
2581 fEnd = child->fContentEnd;
2582 // todo: put original line number in child as well
2583 switch (child->fType) {
2584 case Definition::Type::kKeyWord:
2585 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002586 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002587 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002588 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002589 }
2590 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002591 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002592 case KeyWord::kConst:
2593 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002594 if (!this->parseConst(child, markupDef)) {
2595 return child->reportError<bool>("failed to parse const or constexpr");
2596 }
2597 break;
Cary Clark8032b982017-07-28 11:04:54 -04002598 case KeyWord::kEnum:
2599 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002600 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002601 }
2602 break;
2603 case KeyWord::kStruct:
2604 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002605 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002606 }
2607 break;
2608 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002609 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002610 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002611 }
2612 break;
2613 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002614 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002615 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002616 }
2617 break;
2618 case KeyWord::kUnion:
2619 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002620 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002621 }
2622 break;
Cary Clark61313f32018-10-08 14:57:48 -04002623 case KeyWord::kUsing:
2624 if (!this->parseUsing()) {
2625 return child->reportError<bool>("failed to parse using");
2626 }
2627 break;
Cary Clark8032b982017-07-28 11:04:54 -04002628 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002629 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002630 }
2631 break;
2632 case Definition::Type::kBracket:
2633 switch (child->fBracket) {
2634 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002635 {
2636 auto tokenIter = child->fParent->fTokens.begin();
2637 std::advance(tokenIter, child->fParentIndex);
2638 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002639 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002640 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002641 break;
2642 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002643 if (Bracket::kPound == child->fParent->fBracket &&
2644 KeyWord::kIf == child->fParent->fKeyWord) {
2645 // TODO: this will skip methods named defined() -- for the
2646 // moment there aren't any
2647 if (previousToken.startsWith("defined")) {
2648 break;
2649 }
2650 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002651 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2652 break;
2653 }
Cary Clark73fa9722017-08-29 17:36:51 -04002654 }
Cary Clark0d225392018-06-07 09:59:07 -04002655 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2656 break;
2657 }
Cary Clark8032b982017-07-28 11:04:54 -04002658 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002659 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002660 }
Cary Clark73fa9722017-08-29 17:36:51 -04002661 break;
Cary Clark8032b982017-07-28 11:04:54 -04002662 case Bracket::kSlashSlash:
2663 case Bracket::kSlashStar:
2664 // comments are picked up by parsing objects first
2665 break;
2666 case Bracket::kPound:
2667 // special-case the #xxx xxx_DEFINED entries
2668 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002669 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002670 case KeyWord::kIfndef:
2671 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002672 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002673 if (!this->parseObjects(child, markupDef)) {
2674 return false;
2675 }
2676 break;
2677 }
2678 goto preproError;
2679 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002680 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002681 break;
2682 }
2683 goto preproError;
2684 case KeyWord::kEndif:
2685 if (child->boilerplateEndIf()) {
2686 break;
2687 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002688 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002689 case KeyWord::kInclude:
2690 // ignored for now
2691 break;
2692 case KeyWord::kElse:
2693 case KeyWord::kElif:
2694 // todo: handle these
2695 break;
2696 default:
2697 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002698 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002699 }
2700 break;
2701 case Bracket::kAngle:
2702 // pick up templated function pieces when method is found
2703 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002704 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002705 if (!this->parseObjects(child, markupDef)) {
2706 return false;
2707 }
Cary Clark73fa9722017-08-29 17:36:51 -04002708 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002709 case Bracket::kSquare: {
2710 // check to see if parent is operator, the only case we handle so far
2711 auto prev = child->fParent->fTokens.begin();
2712 std::advance(prev, child->fParentIndex - 1);
2713 if (KeyWord::kOperator != prev->fKeyWord) {
2714 return child->reportError<bool>("expected operator overload");
2715 }
2716 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002717 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002718 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002719 }
2720 break;
2721 case Definition::Type::kWord:
2722 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002723 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002724 }
2725 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002726 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002727 }
2728 break;
2729 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002730 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002731 break;
2732 }
2733 return true;
2734}
2735
Cary Clarkbbfda252018-03-09 15:32:01 -05002736bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2737 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002738}
2739
Cary Clark2f466242017-12-11 16:03:17 -05002740bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2741 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002742 typedefParser.skipExact("typedef");
2743 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002744 string nameStr = typedefParser.typedefName();
2745 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002746 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2747 child->fLineCount, fParent, '\0');
2748 Definition* globalMarkupChild = &fGlobals.back();
2749 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2750 globalMarkupChild->fName = globalUniqueName;
2751 if (!this->findComments(*child, globalMarkupChild)) {
2752 return false;
2753 }
2754 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002755 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002756 return true;
2757 }
2758 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002759 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002760 Definition* markupChild = &markupDef->fTokens.back();
2761 markupChild->fName = nameStr;
2762 markupChild->fTerminator = markupChild->fContentEnd;
2763 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2764 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002765 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04002766 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002767 return true;
2768}
2769
2770bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002771 // incomplete
2772 return true;
2773}
Cary Clark8032b982017-07-28 11:04:54 -04002774
Cary Clark61313f32018-10-08 14:57:48 -04002775bool IncludeParser::parseUsing() {
2776 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002777 return true;
2778}
2779
2780bool IncludeParser::parseChar() {
2781 char test = *fChar;
2782 if ('\\' == fPrev) {
2783 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002784// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002785 fLine = fChar + 1;
2786 }
2787 goto done;
2788 }
2789 switch (test) {
2790 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002791// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002792 fLine = fChar + 1;
2793 if (fInChar) {
2794 return reportError<bool>("malformed char");
2795 }
2796 if (fInString) {
2797 return reportError<bool>("malformed string");
2798 }
2799 if (!this->checkForWord()) {
2800 return false;
2801 }
2802 if (Bracket::kPound == this->topBracket()) {
2803 KeyWord keyWord = fParent->fKeyWord;
2804 if (KeyWord::kNone == keyWord) {
2805 return this->reportError<bool>("unhandled preprocessor directive");
2806 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002807 if (fInDefine) {
2808 SkASSERT(KeyWord::kDefine == keyWord);
2809 fInDefine = false;
2810 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002811 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002812 this->popBracket();
2813 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002814 if (fInBrace) {
2815 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2816 fInBrace = nullptr;
2817 }
Cary Clark8032b982017-07-28 11:04:54 -04002818 } else if (Bracket::kSlashSlash == this->topBracket()) {
2819 this->popBracket();
2820 }
2821 break;
2822 case '*':
2823 if (!fInCharCommentString && '/' == fPrev) {
2824 this->pushBracket(Bracket::kSlashStar);
2825 }
2826 if (!this->checkForWord()) {
2827 return false;
2828 }
2829 if (!fInCharCommentString) {
2830 this->addPunctuation(Punctuation::kAsterisk);
2831 }
2832 break;
2833 case '/':
2834 if ('*' == fPrev) {
2835 if (!fInCharCommentString) {
2836 return reportError<bool>("malformed closing comment");
2837 }
2838 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002839 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002840 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002841 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002842 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002843 }
2844 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002845 }
Cary Clark8032b982017-07-28 11:04:54 -04002846 if (!fInCharCommentString && '/' == fPrev) {
2847 this->pushBracket(Bracket::kSlashSlash);
2848 break;
2849 }
2850 if (!this->checkForWord()) {
2851 return false;
2852 }
2853 break;
2854 case '\'':
2855 if (Bracket::kChar == this->topBracket()) {
2856 this->popBracket();
2857 } else if (!fInComment && !fInString) {
2858 if (fIncludeWord) {
2859 return this->reportError<bool>("word then single-quote");
2860 }
2861 this->pushBracket(Bracket::kChar);
2862 }
2863 break;
2864 case '\"':
2865 if (Bracket::kString == this->topBracket()) {
2866 this->popBracket();
2867 } else if (!fInComment && !fInChar) {
2868 if (fIncludeWord) {
2869 return this->reportError<bool>("word then double-quote");
2870 }
2871 this->pushBracket(Bracket::kString);
2872 }
2873 break;
Cary Clark8032b982017-07-28 11:04:54 -04002874 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002875 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002876 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2877 this->pushBracket(Bracket::kDebugCode);
2878 break;
2879 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002880 case ':':
2881 case '[':
2882 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002883 if (fInCharCommentString) {
2884 break;
2885 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002886 if (fInDefine && fInBrace) {
2887 break;
2888 }
Cary Clark8032b982017-07-28 11:04:54 -04002889 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2890 break;
2891 }
Cary Clark0d225392018-06-07 09:59:07 -04002892 if (fConstExpr) {
2893 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2894 fConstExpr = nullptr;
2895 }
Cary Clark8032b982017-07-28 11:04:54 -04002896 if (!fInBrace) {
2897 if (!this->checkForWord()) {
2898 return false;
2899 }
2900 if (':' == test && !fInFunction) {
2901 break;
2902 }
2903 if ('{' == test) {
2904 this->addPunctuation(Punctuation::kLeftBrace);
2905 } else if (':' == test) {
2906 this->addPunctuation(Punctuation::kColon);
2907 }
2908 }
2909 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2910 && Bracket::kColon == fInBrace->fBracket) {
2911 Definition* braceParent = fParent->fParent;
2912 braceParent->fChildren.pop_back();
2913 braceParent->fTokens.pop_back();
2914 fParent = braceParent;
2915 fInBrace = nullptr;
2916 }
2917 this->pushBracket(
2918 '(' == test ? Bracket::kParen :
2919 '[' == test ? Bracket::kSquare :
2920 '{' == test ? Bracket::kBrace :
2921 Bracket::kColon);
2922 if (!fInBrace
2923 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2924 && fInFunction) {
2925 fInBrace = fParent;
2926 }
2927 } break;
2928 case '<':
2929 if (fInCharCommentString || fInBrace) {
2930 break;
2931 }
2932 if (!this->checkForWord()) {
2933 return false;
2934 }
2935 if (fInEnum) {
2936 break;
2937 }
2938 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002939 // this angle bracket may be an operator or may be a bracket
2940 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002941 break;
2942 case ')':
2943 case ']':
2944 case '}': {
2945 if (fInCharCommentString) {
2946 break;
2947 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002948 if (fInDefine && fInBrace) {
2949 break;
2950 }
Cary Clark8032b982017-07-28 11:04:54 -04002951 if (!fInBrace) {
2952 if (!this->checkForWord()) {
2953 return false;
2954 }
2955 }
2956 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002957 Bracket match = ')' == test ? Bracket::kParen :
2958 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2959 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002960 this->popBracket();
2961 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002962 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04002963 } else {
2964 fInFunction = '}' != test;
2965 }
Cary Clark73fa9722017-08-29 17:36:51 -04002966 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2967 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002968 } else if (Bracket::kAngle == this->topBracket()
2969 && match == this->grandParentBracket()) {
2970 this->popBracket();
2971 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002972 } else {
2973 return reportError<bool>("malformed close bracket");
2974 }
2975 if (popBraceParent) {
2976 Definition* braceParent = fInBrace->fParent;
2977 braceParent->fChildren.pop_back();
2978 braceParent->fTokens.pop_back();
2979 fInBrace = nullptr;
2980 }
2981 } break;
2982 case '>':
2983 if (fInCharCommentString || fInBrace) {
2984 break;
2985 }
2986 if (!this->checkForWord()) {
2987 return false;
2988 }
2989 if (fInEnum) {
2990 break;
2991 }
Cary Clarka560c472017-11-27 10:44:06 -05002992 if (Bracket::kPound == this->topBracket()) {
2993 break;
2994 }
Cary Clark8032b982017-07-28 11:04:54 -04002995 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04002996 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04002997 this->popBracket();
2998 } else {
2999 return reportError<bool>("malformed close angle bracket");
3000 }
3001 break;
3002 case '#': {
3003 if (fInCharCommentString || fInBrace) {
3004 break;
3005 }
3006 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3007 this->pushBracket(Bracket::kPound);
3008 break;
3009 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003010 case ' ':
3011 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3012 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3013 fInBrace = fParent;
3014 // delimiting brackets are space ... unescaped-linefeed
3015 }
Cary Clark8032b982017-07-28 11:04:54 -04003016 case '&':
3017 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003018 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003019 case '-':
3020 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003021 if (fInCharCommentString || fInBrace) {
3022 break;
3023 }
3024 if (!this->checkForWord()) {
3025 return false;
3026 }
3027 break;
Cary Clark0d225392018-06-07 09:59:07 -04003028 case '=':
3029 if (fInCharCommentString || fInBrace) {
3030 break;
3031 }
3032 if (!this->checkForWord()) {
3033 return false;
3034 }
Cary Clarkd7895502018-07-18 15:10:08 -04003035 if (!fParent->fTokens.size()) {
3036 break;
3037 }
Cary Clark0d225392018-06-07 09:59:07 -04003038 {
3039 const Definition& lastToken = fParent->fTokens.back();
3040 if (lastToken.fType != Definition::Type::kWord) {
3041 break;
3042 }
3043 string name(lastToken.fContentStart, lastToken.length());
3044 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3045 break;
3046 }
3047 // find token on start of line
3048 auto lineIter = fParent->fTokens.end();
3049 do {
Cary Clark80247e52018-07-11 16:18:41 -04003050 if (fParent->fTokens.begin() == lineIter) {
3051 break;
3052 }
Cary Clark0d225392018-06-07 09:59:07 -04003053 --lineIter;
3054 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003055 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003056 ++lineIter;
3057 }
3058 Definition* lineStart = &*lineIter;
3059 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3060 bool sawConst = false;
3061 bool sawStatic = false;
3062 bool sawTemplate = false;
3063 bool sawType = false;
3064 while (&lastToken != &*lineIter) {
3065 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3066 if (sawConst || sawStatic || sawTemplate) {
3067 sawConst = false;
3068 break;
3069 }
3070 if (&lastToken == &*++lineIter) {
3071 break;
3072 }
3073 if (KeyWord::kTypename != lineIter->fKeyWord) {
3074 break;
3075 }
3076 if (&lastToken == &*++lineIter) {
3077 break;
3078 }
3079 if (Definition::Type::kWord != lineIter->fType) {
3080 break;
3081 }
3082 sawTemplate = true;
3083 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3084 if (sawConst || sawStatic) {
3085 sawConst = false;
3086 break;
3087 }
3088 sawStatic = true;
3089 } else if (KeyWord::kConst == lineIter->fKeyWord
3090 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3091 if (sawConst) {
3092 sawConst = false;
3093 break;
3094 }
3095 sawConst = true;
3096 } else {
3097 if (sawType) {
3098 sawType = false;
3099 break;
3100 }
3101 if (Definition::Type::kKeyWord == lineIter->fType
3102 && KeyProperty::kNumber
3103 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3104 sawType = true;
3105 } else if (Definition::Type::kWord == lineIter->fType) {
3106 string typeName(lineIter->fContentStart, lineIter->length());
3107 if ("Sk" != name.substr(0, 2)) {
3108 sawType = true;
3109 }
3110 }
3111 }
3112 ++lineIter;
3113 }
3114 if (sawType && sawConst) {
3115 // if found, name first
3116 lineStart->fName = name;
3117 lineStart->fMarkType = MarkType::kConst;
3118 fParent->fChildren.emplace_back(lineStart);
3119 fConstExpr = lineStart;
3120 }
3121 }
3122 break;
Cary Clark8032b982017-07-28 11:04:54 -04003123 case ';':
3124 if (fInCharCommentString || fInBrace) {
3125 break;
3126 }
3127 if (!this->checkForWord()) {
3128 return false;
3129 }
Cary Clark0d225392018-06-07 09:59:07 -04003130 if (fConstExpr) {
3131 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3132 fConstExpr = nullptr;
3133 }
Cary Clark8032b982017-07-28 11:04:54 -04003134 if (Definition::Type::kKeyWord == fParent->fType
3135 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003136 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3137 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003138 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3139 this->popObject();
3140 }
Cary Clark8032b982017-07-28 11:04:54 -04003141 if (KeyWord::kEnum == fParent->fKeyWord) {
3142 fInEnum = false;
3143 }
Cary Clark61313f32018-10-08 14:57:48 -04003144 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003145 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003146 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3147 this->popObject();
3148 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003149 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003150 } else if (Definition::Type::kBracket == fParent->fType
3151 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3152 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3153 list<Definition>::iterator baseIter = fParent->fTokens.end();
3154 list<Definition>::iterator namedIter = fParent->fTokens.end();
3155 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003156 fParent->fTokens.begin() != tokenIter; ) {
3157 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003158 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003159 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003160 if (namedIter != fParent->fTokens.end()) {
3161 return reportError<bool>("found two named member tokens");
3162 }
3163 namedIter = tokenIter;
3164 }
3165 baseIter = tokenIter;
3166 } else {
3167 break;
3168 }
3169 }
3170 // FIXME: if a member definition spans multiple lines, this won't work
3171 if (namedIter != fParent->fTokens.end()) {
3172 if (baseIter == namedIter) {
3173 return this->reportError<bool>("expected type before named token");
3174 }
3175 Definition* member = &*namedIter;
3176 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003177 if (!member->fTerminator) {
3178 member->fTerminator = member->fContentEnd;
3179 }
Cary Clark8032b982017-07-28 11:04:54 -04003180 fParent->fChildren.push_back(member);
3181 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3182 member->fChildren.push_back(&*nameType);
3183 }
Cary Clark8032b982017-07-28 11:04:54 -04003184 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003185 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003186 } else if (fParent->fChildren.size() > 0) {
3187 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003188 Definition* priorEnum = fPriorEnum;
3189 fPriorEnum = nullptr;
3190 if (!priorEnum) {
3191 while (fParent->fChildren.begin() != lastIter) {
3192 std::advance(lastIter, -1);
3193 priorEnum = *lastIter;
3194 if (Definition::Type::kBracket != priorEnum->fType ||
3195 (Bracket::kSlashSlash != priorEnum->fBracket
3196 && Bracket::kSlashStar != priorEnum->fBracket)) {
3197 break;
3198 }
Cary Clark8032b982017-07-28 11:04:54 -04003199 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003200 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003201 }
3202 if (Definition::Type::kKeyWord == priorEnum->fType
3203 && KeyWord::kEnum == priorEnum->fKeyWord) {
3204 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003205 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003206 while (tokenWalker != fParent->fTokens.end()) {
3207 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003208 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003209 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3210 break;
3211 }
3212 }
3213 while (tokenWalker != fParent->fTokens.end()) {
3214 std::advance(tokenWalker, 1);
3215 const Definition* test = &*tokenWalker;
3216 if (Definition::Type::kBracket != test->fType ||
3217 (Bracket::kSlashSlash != test->fBracket
3218 && Bracket::kSlashStar != test->fBracket)) {
3219 break;
3220 }
3221 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003222 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003223 Definition* start = &*tokenWalker;
3224 bool foundExpected = true;
3225 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3226 const Definition* test = &*tokenWalker;
3227 if (expected != test->fKeyWord) {
3228 foundExpected = false;
3229 break;
3230 }
3231 if (tokenWalker == fParent->fTokens.end()) {
3232 break;
3233 }
3234 std::advance(tokenWalker, 1);
3235 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003236 if (!foundExpected) {
3237 foundExpected = true;
3238 tokenWalker = saveTokenWalker;
3239 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3240 const Definition* test = &*tokenWalker;
3241 if (expected != test->fKeyWord) {
3242 foundExpected = false;
3243 break;
3244 }
3245 if (tokenWalker == fParent->fTokens.end()) {
3246 break;
3247 }
3248 if (KeyWord::kNone != expected) {
3249 std::advance(tokenWalker, 1);
3250 }
3251 }
3252 if (foundExpected) {
3253 auto nameToken = priorEnum->fTokens.begin();
3254 string enumName = string(nameToken->fContentStart,
3255 nameToken->fContentEnd - nameToken->fContentStart);
3256 const Definition* test = &*tokenWalker;
3257 string constType = string(test->fContentStart,
3258 test->fContentEnd - test->fContentStart);
3259 if (enumName != constType) {
3260 foundExpected = false;
3261 } else {
3262 std::advance(tokenWalker, 1);
3263 }
3264 }
3265 }
Cary Clark8032b982017-07-28 11:04:54 -04003266 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3267 const char* nameStart = tokenWalker->fStart;
3268 std::advance(tokenWalker, 1);
3269 if (tokenWalker != fParent->fTokens.end()) {
3270 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003271 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003272 start->fName = string(nameStart, tp.fChar - nameStart);
3273 start->fContentEnd = fChar;
3274 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003275 fPriorEnum = priorEnum;
3276 }
Cary Clark8032b982017-07-28 11:04:54 -04003277 }
3278 }
3279 }
3280 this->addPunctuation(Punctuation::kSemicolon);
3281 fInFunction = false;
3282 break;
3283 case '~':
3284 if (fInEnum) {
3285 break;
3286 }
3287 case '0': case '1': case '2': case '3': case '4':
3288 case '5': case '6': case '7': case '8': case '9':
3289 // TODO: don't want to parse numbers, but do need to track for enum defs
3290 // break;
3291 case 'A': case 'B': case 'C': case 'D': case 'E':
3292 case 'F': case 'G': case 'H': case 'I': case 'J':
3293 case 'K': case 'L': case 'M': case 'N': case 'O':
3294 case 'P': case 'Q': case 'R': case 'S': case 'T':
3295 case 'U': case 'V': case 'W': case 'X': case 'Y':
3296 case 'Z': case '_':
3297 case 'a': case 'b': case 'c': case 'd': case 'e':
3298 case 'f': case 'g': case 'h': case 'i': case 'j':
3299 case 'k': case 'l': case 'm': case 'n': case 'o':
3300 case 'p': case 'q': case 'r': case 's': case 't':
3301 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003302 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003303 if (fInCharCommentString || fInBrace) {
3304 break;
3305 }
3306 if (!fIncludeWord) {
3307 fIncludeWord = fChar;
3308 }
3309 break;
3310 }
3311done:
3312 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003313 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003314 return true;
3315}
3316
3317void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003318 IncludeParser::ValidateKeyWords();
3319}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003320
Cary Clark186d08f2018-04-03 08:43:27 -04003321bool IncludeParser::references(const SkString& file) const {
3322 // if includes weren't passed one at a time, assume all references are valid
3323 if (fIncludeMap.empty()) {
3324 return true;
3325 }
3326 SkASSERT(file.endsWith(".bmh") );
3327 string root(file.c_str(), file.size() - 4);
3328 string kReference("_Reference");
3329 if (string::npos != root.find(kReference)) {
3330 root = root.substr(0, root.length() - kReference.length());
3331 }
3332 if (fIClassMap.end() != fIClassMap.find(root)) {
3333 return true;
3334 }
3335 if (fIStructMap.end() != fIStructMap.find(root)) {
3336 return true;
3337 }
Cary Clark224c7002018-06-27 11:00:21 -04003338 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3339 return true;
3340 }
Cary Clarka90ea222018-10-16 10:30:28 -04003341 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3342 return true;
3343 }
Cary Clark224c7002018-06-27 11:00:21 -04003344 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3345 return true;
3346 }
Cary Clark186d08f2018-04-03 08:43:27 -04003347 return false;
3348}
3349
Cary Clark2dc84ad2018-01-26 12:56:22 -05003350void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3351 if (!sk_isdir(includes)) {
3352 IncludeParser::RemoveOneFile(docs, includes);
3353 } else {
3354 SkOSFile::Iter it(includes, ".h");
3355 for (SkString file; it.next(&file); ) {
3356 SkString p = SkOSPath::Join(includes, file.c_str());
3357 const char* hunk = p.c_str();
3358 if (!SkStrEndsWith(hunk, ".h")) {
3359 continue;
3360 }
3361 IncludeParser::RemoveOneFile(docs, hunk);
3362 }
3363 }
3364}
3365
3366void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3367 const char* lastForward = strrchr(includesFile, '/');
3368 const char* lastBackward = strrchr(includesFile, '\\');
3369 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3370 if (!last) {
3371 last = includesFile;
3372 } else {
3373 last += 1;
3374 }
3375 SkString baseName(last);
3376 SkASSERT(baseName.endsWith(".h"));
3377 baseName.remove(baseName.size() - 2, 2);
3378 baseName.append("_Reference.bmh");
3379 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3380 remove(fullName.c_str());
3381}
Cary Clark224c7002018-06-27 11:00:21 -04003382
3383Bracket IncludeParser::topBracket() const {
3384 Definition* parent = this->parentBracket(fParent);
3385 return parent ? parent->fBracket : Bracket::kNone;
3386}