blob: 0716bc7c08bd47a72514e0fea278a4f72521ba9b [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 Clarka64e4ee2018-10-18 08:30:34 -04001847Definition* IncludeParser::findMethod(const Definition& bmhDef) {
1848 auto doubleColon = bmhDef.fName.find("::");
1849 SkASSERT(string::npos != doubleColon); // more work to do to support global refs
1850 string className = bmhDef.fName.substr(0, doubleColon);
1851 const auto& iClass = fIClassMap.find(className);
1852 SkASSERT(fIClassMap.end() != iClass);
1853 string methodName = bmhDef.fName.substr(doubleColon + 2);
1854 auto& iTokens = iClass->second.fTokens;
1855 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1856 [methodName](Definition& token) {
1857 return MarkType::kMethod == token.fMarkType && methodName == token.fName; } );
1858 SkASSERT(iTokens.end() != iMethod);
1859 return &*iMethod;
1860}
1861
Cary Clark224c7002018-06-27 11:00:21 -04001862Definition* IncludeParser::parentBracket(Definition* parent) const {
1863 while (parent && Definition::Type::kBracket != parent->fType) {
1864 parent = parent->fParent;
1865 }
1866 return parent;
1867}
1868
1869Bracket IncludeParser::grandParentBracket() const {
1870 Definition* parent = parentBracket(fParent);
1871 parent = parentBracket(parent ? parent->fParent : nullptr);
1872 return parent ? parent->fBracket : Bracket::kNone;
1873}
1874
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001875bool IncludeParser::inAlignAs() const {
1876 if (fParent->fTokens.size() < 2) {
1877 return false;
1878 }
1879 auto reverseIter = fParent->fTokens.end();
1880 bool checkForBracket = true;
1881 while (fParent->fTokens.begin() != reverseIter) {
1882 std::advance(reverseIter, -1);
1883 if (checkForBracket) {
1884 if (Definition::Type::kBracket != reverseIter->fType) {
1885 return false;
1886 }
1887 if (Bracket::kParen != reverseIter->fBracket) {
1888 return false;
1889 }
1890 checkForBracket = false;
1891 continue;
1892 }
1893 if (Definition::Type::kKeyWord != reverseIter->fType) {
1894 return false;
1895 }
1896 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1897 }
1898 return false;
1899}
1900
Cary Clark61313f32018-10-08 14:57:48 -04001901const Definition* IncludeParser::include(string match) const {
1902 for (auto& entry : fIncludeMap) {
1903 if (string::npos == entry.first.find(match)) {
1904 continue;
1905 }
1906 return &entry.second;
1907 }
1908 SkASSERT(0);
1909 return nullptr;
1910}
1911
Cary Clark137b8742018-05-30 09:21:49 -04001912// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001913bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1914 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001915 // parse class header
1916 auto iter = includeDef->fTokens.begin();
1917 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1918 // todo : documentation is ignoring this for now
1919 iter = std::next(iter);
1920 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001921 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1922 if (hasAlignAs) {
1923 iter = std::next(iter);
1924 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1925 return includeDef->reportError<bool>("expected alignas argument");
1926 }
1927 iter = std::next(iter);
1928 }
Cary Clark8032b982017-07-28 11:04:54 -04001929 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1930 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001931 iter = std::next(iter);
1932 if (iter == includeDef->fTokens.end()) {
1933 return true; // forward declaration only
1934 }
Cary Clark8032b982017-07-28 11:04:54 -04001935 do {
1936 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001937 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001938 }
1939 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1940 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001941 }
Cary Clark8032b982017-07-28 11:04:54 -04001942 } while (static_cast<void>(iter = std::next(iter)), true);
1943 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001944 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001945 }
1946 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1947 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001948 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001949 }
1950 markupDef->fStart = iter->fStart;
1951 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001952 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001953 }
1954// if (1 != includeDef->fChildren.size()) {
1955// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1956// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001957 auto includeDefIter = includeDef->fChildren.begin();
1958 if (hasAlignAs) {
1959 SkASSERT(includeDef->fChildren.end() != includeDefIter);
1960 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
1961 std::advance(includeDefIter, 1);
1962 }
1963 if (includeDef->fChildren.end() != includeDefIter
1964 && Bracket::kAngle == (*includeDefIter)->fBracket) {
1965 std::advance(includeDefIter, 1);
1966 }
1967 includeDef = *includeDefIter;
1968 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001969 iter = includeDef->fTokens.begin();
1970 // skip until public
1971 int publicIndex = 0;
1972 if (IsStruct::kNo == isStruct) {
1973 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1974 size_t publicLen = strlen(publicName);
1975 while (iter != includeDef->fTokens.end()
1976 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1977 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001978 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001979 iter = std::next(iter);
1980 ++publicIndex;
1981 }
1982 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001983 int keyIndex = publicIndex;
1984 KeyWord currentKey = KeyWord::kPublic;
1985 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1986 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001987 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1988 size_t protectedLen = strlen(protectedName);
1989 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1990 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001991 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001992 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04001993 std::advance(childIter, 1);
1994 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001995 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001996 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001997 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001998 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001999 const char* testStart = iter->fStart;
2000 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2001 iter = std::next(iter);
2002 ++keyIndex;
2003 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2004 currentKey = KeyWord::kPublic;
2005 break;
2006 }
2007 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2008 currentKey = KeyWord::kProtected;
2009 break;
2010 }
2011 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2012 currentKey = KeyWord::kPrivate;
2013 break;
2014 }
2015 }
2016 fLastObject = nullptr;
2017 if (KeyWord::kPublic == currentKey) {
2018 if (!this->parseObject(child, markupDef)) {
2019 return false;
2020 }
Cary Clark8032b982017-07-28 11:04:54 -04002021 }
Cary Clark73fa9722017-08-29 17:36:51 -04002022 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002023 childIter = std::next(childIter);
2024 }
Cary Clark137b8742018-05-30 09:21:49 -04002025 while (iter != includeDef->fTokens.end()) {
2026 iter->fPrivate = KeyWord::kPublic != currentKey;
2027 iter = std::next(iter);
2028 }
Cary Clark8032b982017-07-28 11:04:54 -04002029 SkASSERT(fParent->fParent);
2030 fParent = fParent->fParent;
2031 return true;
2032}
2033
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002034bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04002035 int lineCount, Definition* markupDef) {
2036 TextParser parser(filename, start, end, lineCount);
2037 // parse doxygen if present
2038 if (parser.startsWith("**")) {
2039 parser.next();
2040 parser.next();
2041 parser.skipWhiteSpace();
2042 if ('\\' == parser.peek()) {
2043 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002044 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2045 if (parser.skipExact("file")) {
2046 if (Definition::Type::kFileType != fParent->fType) {
2047 return reportError<bool>("expected parent is file");
2048 }
2049 string filename = markupDef->fileName();
2050 if (!parser.skipWord(filename.c_str())) {
2051 return reportError<bool>("missing object type");
2052 }
2053 } else if (parser.skipExact("fn")) {
2054 SkASSERT(0); // incomplete
2055 } else {
2056 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2057 return reportError<bool>("missing object type");
2058 }
2059 if (!parser.skipWord(markupDef->fName.c_str()) &&
2060 KeyWord::kEnum != markupDef->fKeyWord) {
2061 return reportError<bool>("missing object name");
2062 }
Cary Clark8032b982017-07-28 11:04:54 -04002063 }
Cary Clark8032b982017-07-28 11:04:54 -04002064 }
2065 }
2066 // remove leading '*' if present
2067 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2068 while (!parser.eof() && parser.skipWhiteSpace()) {
2069 while ('*' == parser.peek()) {
2070 parser.next();
2071 if (parser.eof()) {
2072 break;
2073 }
2074 parser.skipWhiteSpace();
2075 }
2076 if (parser.eof()) {
2077 break;
2078 }
2079 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002080 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002081 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002082 parser.skipToEndBracket('\n');
2083 }
2084 return true;
2085}
2086
Cary Clarkd98f78c2018-04-26 08:32:37 -04002087bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002088 if (!markupDef) {
2089 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2090 child->fLineCount, fParent, '\0');
2091 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002092 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002093 globalMarkupChild->fName = globalUniqueName;
2094 if (!this->findComments(*child, globalMarkupChild)) {
2095 return false;
2096 }
2097 fIConstMap[globalUniqueName] = globalMarkupChild;
2098 return true;
2099 }
2100 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2101 child->fLineCount, markupDef, '\0');
2102 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002103 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002104 markupChild->fTerminator = markupChild->fContentEnd;
2105 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002106 classDef.fConsts[child->fName] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002107 fIConstMap[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002108 return true;
2109}
2110
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002111bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2112 TextParser parser(child);
2113 if (!parser.skipExact("#define")) {
2114 return false;
2115 }
2116 if (!parser.skipSpace()) {
2117 return false;
2118 }
2119 const char* nameStart = parser.fChar;
2120 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2121 if (parser.eof()) {
2122 return true; // do nothing if #define doesn't define anything
2123 }
2124 string nameStr(nameStart, parser.fChar - nameStart);
2125 struct Param {
2126 const char* fStart;
2127 const char* fEnd;
2128 };
2129 vector<Param> params;
2130 if ('(' == parser.peek()) {
2131 parser.next();
2132 if (!parser.skipSpace()) {
2133 return false;
2134 }
2135 do {
2136 const char* paramStart = parser.fChar;
2137 if (!parser.skipExact("...")) {
2138 parser.skipToNonAlphaNum();
2139 }
2140 if (parser.eof()) {
2141 return false;
2142 }
2143 params.push_back({paramStart, parser.fChar});
2144 if (!parser.skipSpace()) {
2145 return false;
2146 }
2147 if (')' == parser.peek()) {
2148 parser.next();
2149 break;
2150 }
2151 if (',' != parser.next()) {
2152 return false;
2153 }
2154 if (!parser.skipSpace()) {
2155 return false;
2156 }
2157 } while (true);
2158 }
2159 if (!parser.skipSpace()) {
2160 return false;
2161 }
2162 if (!markupDef) {
2163 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2164 child->fLineCount, fParent, '\0');
2165 Definition* globalMarkupChild = &fGlobals.back();
2166 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2167 globalMarkupChild->fName = globalUniqueName;
2168 globalMarkupChild->fTerminator = child->fContentEnd;
2169 if (!this->findComments(*child, globalMarkupChild)) {
2170 return false;
2171 }
2172 fIDefineMap[globalUniqueName] = globalMarkupChild;
2173 for (Param param : params) {
2174 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2175 child->fLineCount, globalMarkupChild, '\0');
2176 Definition* paramChild = &globalMarkupChild->fTokens.back();
2177 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2178 paramChild->fTerminator = param.fEnd;
2179 }
2180 return true;
2181 }
2182 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2183 child->fLineCount, markupDef, '\0');
2184 Definition* markupChild = &markupDef->fTokens.back();
2185 markupChild->fName = nameStr;
2186 markupChild->fTerminator = markupChild->fContentEnd;
2187 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2188 if (!this->findComments(*child, markupChild)) {
2189 return false;
2190 }
2191 classDef.fDefines[nameStr] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002192 fIDefineMap[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002193 return true;
2194}
2195
2196bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002197 TextParser parser(child);
2198 parser.skipToEndBracket('{');
2199 if (parser.eof()) {
2200 return true; // if enum is a forward declaration, do nothing
2201 }
2202 parser.next();
2203 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002204 if (child->fTokens.size() > 0) {
2205 auto token = child->fTokens.begin();
2206 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2207 token = token->fTokens.begin();
2208 }
2209 if (Definition::Type::kWord == token->fType) {
2210 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
2211 }
2212 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002213 Definition* markupChild;
2214 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002215 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2216 child->fLineCount, fParent, '\0');
2217 markupChild = &fGlobals.back();
2218 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2219 markupChild->fName = globalUniqueName;
2220 markupChild->fTerminator = child->fContentEnd;
2221 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002222 } else {
2223 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002224 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002225 markupChild = &markupDef->fTokens.back();
2226 }
Cary Clark8032b982017-07-28 11:04:54 -04002227 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2228 markupChild->fKeyWord = KeyWord::kEnum;
2229 TextParser enumName(child);
2230 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05002231 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002232 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002233 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002234 markupChild->fMarkType = MarkType::kEnumClass;
2235 }
Cary Clark8032b982017-07-28 11:04:54 -04002236 const char* nameStart = enumName.fChar;
2237 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05002238 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04002239 markupChild->fName = markupDef->fName + "::" +
2240 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05002241 }
Cary Clark8032b982017-07-28 11:04:54 -04002242 if (!this->findComments(*child, markupChild)) {
2243 return false;
2244 }
Cary Clark8032b982017-07-28 11:04:54 -04002245 const char* dataEnd;
2246 do {
Cary Clark8032b982017-07-28 11:04:54 -04002247 parser.skipWhiteSpace();
2248 if ('}' == parser.peek()) {
2249 break;
2250 }
2251 Definition* comment = nullptr;
2252 // note that comment, if any, can be before or after (on the same line, though) as member
2253 if ('#' == parser.peek()) {
2254 // fixme: handle preprecessor, but just skip it for now
2255 parser.skipToLineStart();
2256 }
2257 while (parser.startsWith("/*") || parser.startsWith("//")) {
2258 parser.next();
2259 const char* start = parser.fChar;
2260 const char* end;
2261 if ('*' == parser.peek()) {
2262 end = parser.strnstr("*/", parser.fEnd);
2263 parser.fChar = end;
2264 parser.next();
2265 parser.next();
2266 } else {
2267 end = parser.trimmedLineEnd();
2268 parser.skipToLineStart();
2269 }
2270 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002271 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002272 comment = &markupChild->fTokens.back();
2273 comment->fTerminator = end;
2274 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
2275 return false;
2276 }
2277 parser.skipWhiteSpace();
2278 }
2279 parser.skipWhiteSpace();
2280 const char* memberStart = parser.fChar;
2281 if ('}' == memberStart[0]) {
2282 break;
2283 }
Cary Clark9174bda2017-09-19 17:39:32 -04002284 // if there's comment on same the line as member def, output first as if it was before
2285
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002286 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002287 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04002288 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04002289 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04002290 }
Cary Clark8032b982017-07-28 11:04:54 -04002291 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04002292 if ('=' == parser.peek()) {
2293 parser.skipToEndBracket(',');
2294 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002295 if (!parser.eof() && '#' == parser.peek()) {
2296 // fixme: handle preprecessor, but just skip it for now
2297 continue;
2298 }
Cary Clark9174bda2017-09-19 17:39:32 -04002299 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04002300 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04002301 }
2302 dataEnd = parser.fChar;
2303 const char* start = parser.anyOf("/\n");
2304 SkASSERT(start);
2305 parser.skipTo(start);
2306 if ('/' == parser.next()) {
2307 char slashStar = parser.next();
2308 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04002309 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04002310 char doxCheck = parser.next();
2311 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
2312 save.restore();
2313 }
2314 }
2315 parser.skipWhiteSpace();
2316 const char* commentStart = parser.fChar;
2317 if ('/' == slashStar) {
2318 parser.skipToEndBracket('\n');
2319 } else {
2320 parser.skipToEndBracket("*/");
2321 }
2322 SkASSERT(!parser.eof());
2323 const char* commentEnd = parser.fChar;
2324 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002325 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04002326 comment = &markupChild->fTokens.back();
2327 comment->fTerminator = commentEnd;
2328 }
Cary Clark8032b982017-07-28 11:04:54 -04002329 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002330 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002331 Definition* member = &markupChild->fTokens.back();
2332 member->fName = memberName;
2333 if (comment) {
2334 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04002335 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002336 }
2337 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04002338 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002339 for (auto outsideMember : child->fChildren) {
2340 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002341 continue;
2342 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002343 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2344 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002345 continue;
2346 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002347 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2348 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002349 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002350 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002351 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002352 // FIXME: ? add comment as well ?
2353 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002354 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002355 if (markupDef) {
2356 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2357 SkASSERT(classDef.fStart);
2358 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002359 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002360 markupChild->fName = uniqueName;
2361 classDef.fEnums[uniqueName] = markupChild;
Cary Clark61313f32018-10-08 14:57:48 -04002362 fIEnumMap[fullName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002363 }
Cary Clark8032b982017-07-28 11:04:54 -04002364 return true;
2365}
2366
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002367bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002368 fParent = &fIncludeMap[name];
2369 fParent->fName = name;
2370 fParent->fFileName = fFileName;
2371 fParent->fType = Definition::Type::kFileType;
2372 fParent->fContentStart = fChar;
2373 fParent->fContentEnd = fEnd;
2374 // parse include file into tree
2375 while (fChar < fEnd) {
2376 if (!this->parseChar()) {
2377 return false;
2378 }
2379 }
2380 // parse tree and add named objects to maps
2381 fParent = &fIncludeMap[name];
2382 if (!this->parseObjects(fParent, nullptr)) {
2383 return false;
2384 }
2385 return true;
2386}
2387
2388bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2389 const char* typeStart = child->fChildren[0]->fContentStart;
2390 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002391 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002392 Definition* markupChild = &markupDef->fTokens.back();
2393 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002394 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002395 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2396 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2397 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2398 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002399 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04002400 classDef.fMembers[uniqueName] = markupChild;
2401 if (child->fParentIndex >= 2) {
2402 auto comment = child->fParent->fTokens.begin();
2403 std::advance(comment, child->fParentIndex - 2);
2404 if (Definition::Type::kBracket == comment->fType
2405 && (Bracket::kSlashStar == comment->fBracket
2406 || Bracket::kSlashSlash == comment->fBracket)) {
2407 TextParser parser(&*comment);
2408 do {
2409 parser.skipToAlpha();
2410 if (parser.eof()) {
2411 break;
2412 }
2413 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002414 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002415 if (Bracket::kSlashStar == comment->fBracket) {
2416 const char* commentEnd = parser.strnstr("*/", end);
2417 if (commentEnd) {
2418 end = commentEnd;
2419 }
2420 }
2421 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002422 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002423 Definition* commentChild = &markupDef->fTokens.back();
2424 markupChild->fChildren.emplace_back(commentChild);
2425 parser.skipTo(end);
2426 } while (!parser.eof());
2427 }
2428 }
2429 return true;
2430}
2431
2432bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2433 auto tokenIter = child->fParent->fTokens.begin();
2434 std::advance(tokenIter, child->fParentIndex);
2435 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002436 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002437 bool addConst = false;
2438 auto operatorCheck = tokenIter;
2439 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2440 operatorCheck = std::prev(tokenIter);
2441 }
2442 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002443 auto closeParen = std::next(tokenIter);
2444 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2445 '(' == closeParen->fContentStart[0]);
2446 nameEnd = closeParen->fContentEnd + 1;
2447 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002448 if (Definition::Type::kKeyWord == closeParen->fType &&
2449 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002450 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002451 }
Cary Clarka560c472017-11-27 10:44:06 -05002452 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002453 }
2454 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002455 if (addConst) {
2456 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002457 }
Cary Clark8032b982017-07-28 11:04:54 -04002458 while (tokenIter != child->fParent->fTokens.begin()) {
2459 auto testIter = std::prev(tokenIter);
2460 switch (testIter->fType) {
2461 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002462 if (testIter == child->fParent->fTokens.begin() &&
2463 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2464 KeyWord::kIfndef == child->fParent->fKeyWord ||
2465 KeyWord::kIf == child->fParent->fKeyWord)) {
2466 std::next(tokenIter);
2467 break;
2468 }
Cary Clark8032b982017-07-28 11:04:54 -04002469 goto keepGoing;
2470 case Definition::Type::kKeyWord: {
2471 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2472 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2473 goto keepGoing;
2474 }
2475 } break;
2476 case Definition::Type::kBracket:
2477 if (Bracket::kAngle == testIter->fBracket) {
2478 goto keepGoing;
2479 }
2480 break;
2481 case Definition::Type::kPunctuation:
2482 if (Punctuation::kSemicolon == testIter->fPunctuation
2483 || Punctuation::kLeftBrace == testIter->fPunctuation
2484 || Punctuation::kColon == testIter->fPunctuation) {
2485 break;
2486 }
2487 keepGoing:
2488 tokenIter = testIter;
2489 continue;
2490 default:
2491 break;
2492 }
2493 break;
2494 }
Cary Clark224c7002018-06-27 11:00:21 -04002495 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002496 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002497 tokenIter->fPrivate = string::npos != nameStr.find("::")
2498 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002499 auto testIter = child->fParent->fTokens.begin();
2500 SkASSERT(child->fParentIndex > 0);
2501 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002502 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2503 0 == tokenIter->fParentIndex) {
2504 tokenIter = std::next(tokenIter);
2505 }
Cary Clark8032b982017-07-28 11:04:54 -04002506 const char* start = tokenIter->fContentStart;
2507 const char* end = tokenIter->fContentEnd;
2508 const char kDebugCodeStr[] = "SkDEBUGCODE";
2509 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2510 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2511 std::advance(testIter, 1);
2512 start = testIter->fContentStart + 1;
2513 end = testIter->fContentEnd - 1;
2514 } else {
2515 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002516 do {
2517 std::advance(testIter, 1);
2518 if (testIter == child->fParent->fTokens.end()) {
2519 break;
2520 }
Cary Clark8032b982017-07-28 11:04:54 -04002521 switch (testIter->fType) {
2522 case Definition::Type::kPunctuation:
2523 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2524 || Punctuation::kLeftBrace == testIter->fPunctuation
2525 || Punctuation::kColon == testIter->fPunctuation);
2526 end = testIter->fStart;
2527 break;
2528 case Definition::Type::kKeyWord: {
2529 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2530 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2531 continue;
2532 }
2533 } break;
2534 default:
2535 continue;
2536 }
2537 break;
Cary Clark61313f32018-10-08 14:57:48 -04002538 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002539 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002540 while (end > start && ' ' >= end[-1]) {
2541 --end;
2542 }
Cary Clark9174bda2017-09-19 17:39:32 -04002543 if (!markupDef) {
2544 auto parentIter = child->fParent->fTokens.begin();
2545 SkASSERT(child->fParentIndex > 0);
2546 std::advance(parentIter, child->fParentIndex - 1);
2547 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002548 TextParser nameParser(methodName);
2549 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002550 return true; // expect this is inline class definition outside of class
2551 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002552 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2553 fParent, '\0');
2554 Definition* globalMarkupChild = &fGlobals.back();
2555 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2556 globalMarkupChild->fName = globalUniqueName;
2557 if (!this->findComments(*child, globalMarkupChild)) {
2558 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002559 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002560 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002561 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002562 }
Cary Clark8032b982017-07-28 11:04:54 -04002563 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002564 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002565 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002566 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
2567 {
2568 auto mapIter = fIClassMap.find(markupDef->fName);
2569 SkASSERT(fIClassMap.end() != mapIter);
2570 IClassDefinition& classDef = mapIter->second;
2571 SkASSERT(classDef.fStart);
2572 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2573 markupChild->fName = uniqueName;
2574 if (!this->findComments(*child, markupChild)) {
2575 return false;
2576 }
2577 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002578 }
Cary Clark8032b982017-07-28 11:04:54 -04002579 return true;
2580}
2581
Cary Clark8032b982017-07-28 11:04:54 -04002582bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002583 fPriorObject = nullptr;
2584 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002585 if (!this->parseObject(child, markupDef)) {
2586 return false;
2587 }
Cary Clark0d225392018-06-07 09:59:07 -04002588 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002589 }
2590 return true;
2591}
2592
2593bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2594 // set up for error reporting
2595 fLine = fChar = child->fStart;
2596 fEnd = child->fContentEnd;
2597 // todo: put original line number in child as well
2598 switch (child->fType) {
2599 case Definition::Type::kKeyWord:
2600 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002601 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002602 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002603 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002604 }
2605 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002606 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002607 case KeyWord::kConst:
2608 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002609 if (!this->parseConst(child, markupDef)) {
2610 return child->reportError<bool>("failed to parse const or constexpr");
2611 }
2612 break;
Cary Clark8032b982017-07-28 11:04:54 -04002613 case KeyWord::kEnum:
2614 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002615 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002616 }
2617 break;
2618 case KeyWord::kStruct:
2619 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002620 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002621 }
2622 break;
2623 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002624 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002625 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002626 }
2627 break;
2628 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002629 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002630 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002631 }
2632 break;
2633 case KeyWord::kUnion:
2634 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002635 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002636 }
2637 break;
Cary Clark61313f32018-10-08 14:57:48 -04002638 case KeyWord::kUsing:
2639 if (!this->parseUsing()) {
2640 return child->reportError<bool>("failed to parse using");
2641 }
2642 break;
Cary Clark8032b982017-07-28 11:04:54 -04002643 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002644 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002645 }
2646 break;
2647 case Definition::Type::kBracket:
2648 switch (child->fBracket) {
2649 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002650 {
2651 auto tokenIter = child->fParent->fTokens.begin();
2652 std::advance(tokenIter, child->fParentIndex);
2653 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002654 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002655 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002656 break;
2657 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002658 if (Bracket::kPound == child->fParent->fBracket &&
2659 KeyWord::kIf == child->fParent->fKeyWord) {
2660 // TODO: this will skip methods named defined() -- for the
2661 // moment there aren't any
2662 if (previousToken.startsWith("defined")) {
2663 break;
2664 }
2665 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002666 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2667 break;
2668 }
Cary Clark73fa9722017-08-29 17:36:51 -04002669 }
Cary Clark0d225392018-06-07 09:59:07 -04002670 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2671 break;
2672 }
Cary Clark8032b982017-07-28 11:04:54 -04002673 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002674 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002675 }
Cary Clark73fa9722017-08-29 17:36:51 -04002676 break;
Cary Clark8032b982017-07-28 11:04:54 -04002677 case Bracket::kSlashSlash:
2678 case Bracket::kSlashStar:
2679 // comments are picked up by parsing objects first
2680 break;
2681 case Bracket::kPound:
2682 // special-case the #xxx xxx_DEFINED entries
2683 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002684 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002685 case KeyWord::kIfndef:
2686 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002687 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002688 if (!this->parseObjects(child, markupDef)) {
2689 return false;
2690 }
2691 break;
2692 }
2693 goto preproError;
2694 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002695 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002696 break;
2697 }
2698 goto preproError;
2699 case KeyWord::kEndif:
2700 if (child->boilerplateEndIf()) {
2701 break;
2702 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002703 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002704 case KeyWord::kInclude:
2705 // ignored for now
2706 break;
2707 case KeyWord::kElse:
2708 case KeyWord::kElif:
2709 // todo: handle these
2710 break;
2711 default:
2712 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002713 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002714 }
2715 break;
2716 case Bracket::kAngle:
2717 // pick up templated function pieces when method is found
2718 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002719 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002720 if (!this->parseObjects(child, markupDef)) {
2721 return false;
2722 }
Cary Clark73fa9722017-08-29 17:36:51 -04002723 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002724 case Bracket::kSquare: {
2725 // check to see if parent is operator, the only case we handle so far
2726 auto prev = child->fParent->fTokens.begin();
2727 std::advance(prev, child->fParentIndex - 1);
2728 if (KeyWord::kOperator != prev->fKeyWord) {
2729 return child->reportError<bool>("expected operator overload");
2730 }
2731 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002732 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002733 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002734 }
2735 break;
2736 case Definition::Type::kWord:
2737 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002738 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002739 }
2740 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002741 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002742 }
2743 break;
2744 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002745 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002746 break;
2747 }
2748 return true;
2749}
2750
Cary Clarkbbfda252018-03-09 15:32:01 -05002751bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2752 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002753}
2754
Cary Clark2f466242017-12-11 16:03:17 -05002755bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2756 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002757 typedefParser.skipExact("typedef");
2758 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002759 string nameStr = typedefParser.typedefName();
2760 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002761 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2762 child->fLineCount, fParent, '\0');
2763 Definition* globalMarkupChild = &fGlobals.back();
2764 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2765 globalMarkupChild->fName = globalUniqueName;
2766 if (!this->findComments(*child, globalMarkupChild)) {
2767 return false;
2768 }
2769 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002770 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002771 return true;
2772 }
2773 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002774 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002775 Definition* markupChild = &markupDef->fTokens.back();
2776 markupChild->fName = nameStr;
2777 markupChild->fTerminator = markupChild->fContentEnd;
2778 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2779 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002780 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04002781 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002782 return true;
2783}
2784
2785bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002786 // incomplete
2787 return true;
2788}
Cary Clark8032b982017-07-28 11:04:54 -04002789
Cary Clark61313f32018-10-08 14:57:48 -04002790bool IncludeParser::parseUsing() {
2791 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002792 return true;
2793}
2794
2795bool IncludeParser::parseChar() {
2796 char test = *fChar;
2797 if ('\\' == fPrev) {
2798 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002799// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002800 fLine = fChar + 1;
2801 }
2802 goto done;
2803 }
2804 switch (test) {
2805 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002806// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002807 fLine = fChar + 1;
2808 if (fInChar) {
2809 return reportError<bool>("malformed char");
2810 }
2811 if (fInString) {
2812 return reportError<bool>("malformed string");
2813 }
2814 if (!this->checkForWord()) {
2815 return false;
2816 }
2817 if (Bracket::kPound == this->topBracket()) {
2818 KeyWord keyWord = fParent->fKeyWord;
2819 if (KeyWord::kNone == keyWord) {
2820 return this->reportError<bool>("unhandled preprocessor directive");
2821 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002822 if (fInDefine) {
2823 SkASSERT(KeyWord::kDefine == keyWord);
2824 fInDefine = false;
2825 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002826 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002827 this->popBracket();
2828 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002829 if (fInBrace) {
2830 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2831 fInBrace = nullptr;
2832 }
Cary Clark8032b982017-07-28 11:04:54 -04002833 } else if (Bracket::kSlashSlash == this->topBracket()) {
2834 this->popBracket();
2835 }
2836 break;
2837 case '*':
2838 if (!fInCharCommentString && '/' == fPrev) {
2839 this->pushBracket(Bracket::kSlashStar);
2840 }
2841 if (!this->checkForWord()) {
2842 return false;
2843 }
2844 if (!fInCharCommentString) {
2845 this->addPunctuation(Punctuation::kAsterisk);
2846 }
2847 break;
2848 case '/':
2849 if ('*' == fPrev) {
2850 if (!fInCharCommentString) {
2851 return reportError<bool>("malformed closing comment");
2852 }
2853 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002854 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002855 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002856 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002857 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002858 }
2859 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002860 }
Cary Clark8032b982017-07-28 11:04:54 -04002861 if (!fInCharCommentString && '/' == fPrev) {
2862 this->pushBracket(Bracket::kSlashSlash);
2863 break;
2864 }
2865 if (!this->checkForWord()) {
2866 return false;
2867 }
2868 break;
2869 case '\'':
2870 if (Bracket::kChar == this->topBracket()) {
2871 this->popBracket();
2872 } else if (!fInComment && !fInString) {
2873 if (fIncludeWord) {
2874 return this->reportError<bool>("word then single-quote");
2875 }
2876 this->pushBracket(Bracket::kChar);
2877 }
2878 break;
2879 case '\"':
2880 if (Bracket::kString == this->topBracket()) {
2881 this->popBracket();
2882 } else if (!fInComment && !fInChar) {
2883 if (fIncludeWord) {
2884 return this->reportError<bool>("word then double-quote");
2885 }
2886 this->pushBracket(Bracket::kString);
2887 }
2888 break;
Cary Clark8032b982017-07-28 11:04:54 -04002889 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002890 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002891 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2892 this->pushBracket(Bracket::kDebugCode);
2893 break;
2894 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002895 case ':':
2896 case '[':
2897 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002898 if (fInCharCommentString) {
2899 break;
2900 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002901 if (fInDefine && fInBrace) {
2902 break;
2903 }
Cary Clark8032b982017-07-28 11:04:54 -04002904 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2905 break;
2906 }
Cary Clark0d225392018-06-07 09:59:07 -04002907 if (fConstExpr) {
2908 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2909 fConstExpr = nullptr;
2910 }
Cary Clark8032b982017-07-28 11:04:54 -04002911 if (!fInBrace) {
2912 if (!this->checkForWord()) {
2913 return false;
2914 }
2915 if (':' == test && !fInFunction) {
2916 break;
2917 }
2918 if ('{' == test) {
2919 this->addPunctuation(Punctuation::kLeftBrace);
2920 } else if (':' == test) {
2921 this->addPunctuation(Punctuation::kColon);
2922 }
2923 }
2924 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2925 && Bracket::kColon == fInBrace->fBracket) {
2926 Definition* braceParent = fParent->fParent;
2927 braceParent->fChildren.pop_back();
2928 braceParent->fTokens.pop_back();
2929 fParent = braceParent;
2930 fInBrace = nullptr;
2931 }
2932 this->pushBracket(
2933 '(' == test ? Bracket::kParen :
2934 '[' == test ? Bracket::kSquare :
2935 '{' == test ? Bracket::kBrace :
2936 Bracket::kColon);
2937 if (!fInBrace
2938 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2939 && fInFunction) {
2940 fInBrace = fParent;
2941 }
2942 } break;
2943 case '<':
2944 if (fInCharCommentString || fInBrace) {
2945 break;
2946 }
2947 if (!this->checkForWord()) {
2948 return false;
2949 }
2950 if (fInEnum) {
2951 break;
2952 }
2953 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002954 // this angle bracket may be an operator or may be a bracket
2955 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002956 break;
2957 case ')':
2958 case ']':
2959 case '}': {
2960 if (fInCharCommentString) {
2961 break;
2962 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002963 if (fInDefine && fInBrace) {
2964 break;
2965 }
Cary Clark8032b982017-07-28 11:04:54 -04002966 if (!fInBrace) {
2967 if (!this->checkForWord()) {
2968 return false;
2969 }
2970 }
2971 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002972 Bracket match = ')' == test ? Bracket::kParen :
2973 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2974 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002975 this->popBracket();
2976 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002977 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04002978 } else {
2979 fInFunction = '}' != test;
2980 }
Cary Clark73fa9722017-08-29 17:36:51 -04002981 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2982 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002983 } else if (Bracket::kAngle == this->topBracket()
2984 && match == this->grandParentBracket()) {
2985 this->popBracket();
2986 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002987 } else {
2988 return reportError<bool>("malformed close bracket");
2989 }
2990 if (popBraceParent) {
2991 Definition* braceParent = fInBrace->fParent;
2992 braceParent->fChildren.pop_back();
2993 braceParent->fTokens.pop_back();
2994 fInBrace = nullptr;
2995 }
2996 } break;
2997 case '>':
2998 if (fInCharCommentString || fInBrace) {
2999 break;
3000 }
3001 if (!this->checkForWord()) {
3002 return false;
3003 }
3004 if (fInEnum) {
3005 break;
3006 }
Cary Clarka560c472017-11-27 10:44:06 -05003007 if (Bracket::kPound == this->topBracket()) {
3008 break;
3009 }
Cary Clark8032b982017-07-28 11:04:54 -04003010 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003011 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003012 this->popBracket();
3013 } else {
3014 return reportError<bool>("malformed close angle bracket");
3015 }
3016 break;
3017 case '#': {
3018 if (fInCharCommentString || fInBrace) {
3019 break;
3020 }
3021 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3022 this->pushBracket(Bracket::kPound);
3023 break;
3024 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003025 case ' ':
3026 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3027 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3028 fInBrace = fParent;
3029 // delimiting brackets are space ... unescaped-linefeed
3030 }
Cary Clark8032b982017-07-28 11:04:54 -04003031 case '&':
3032 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003033 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003034 case '-':
3035 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003036 if (fInCharCommentString || fInBrace) {
3037 break;
3038 }
3039 if (!this->checkForWord()) {
3040 return false;
3041 }
3042 break;
Cary Clark0d225392018-06-07 09:59:07 -04003043 case '=':
3044 if (fInCharCommentString || fInBrace) {
3045 break;
3046 }
3047 if (!this->checkForWord()) {
3048 return false;
3049 }
Cary Clarkd7895502018-07-18 15:10:08 -04003050 if (!fParent->fTokens.size()) {
3051 break;
3052 }
Cary Clark0d225392018-06-07 09:59:07 -04003053 {
3054 const Definition& lastToken = fParent->fTokens.back();
3055 if (lastToken.fType != Definition::Type::kWord) {
3056 break;
3057 }
3058 string name(lastToken.fContentStart, lastToken.length());
3059 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3060 break;
3061 }
3062 // find token on start of line
3063 auto lineIter = fParent->fTokens.end();
3064 do {
Cary Clark80247e52018-07-11 16:18:41 -04003065 if (fParent->fTokens.begin() == lineIter) {
3066 break;
3067 }
Cary Clark0d225392018-06-07 09:59:07 -04003068 --lineIter;
3069 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003070 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003071 ++lineIter;
3072 }
3073 Definition* lineStart = &*lineIter;
3074 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3075 bool sawConst = false;
3076 bool sawStatic = false;
3077 bool sawTemplate = false;
3078 bool sawType = false;
3079 while (&lastToken != &*lineIter) {
3080 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3081 if (sawConst || sawStatic || sawTemplate) {
3082 sawConst = false;
3083 break;
3084 }
3085 if (&lastToken == &*++lineIter) {
3086 break;
3087 }
3088 if (KeyWord::kTypename != lineIter->fKeyWord) {
3089 break;
3090 }
3091 if (&lastToken == &*++lineIter) {
3092 break;
3093 }
3094 if (Definition::Type::kWord != lineIter->fType) {
3095 break;
3096 }
3097 sawTemplate = true;
3098 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3099 if (sawConst || sawStatic) {
3100 sawConst = false;
3101 break;
3102 }
3103 sawStatic = true;
3104 } else if (KeyWord::kConst == lineIter->fKeyWord
3105 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3106 if (sawConst) {
3107 sawConst = false;
3108 break;
3109 }
3110 sawConst = true;
3111 } else {
3112 if (sawType) {
3113 sawType = false;
3114 break;
3115 }
3116 if (Definition::Type::kKeyWord == lineIter->fType
3117 && KeyProperty::kNumber
3118 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3119 sawType = true;
3120 } else if (Definition::Type::kWord == lineIter->fType) {
3121 string typeName(lineIter->fContentStart, lineIter->length());
3122 if ("Sk" != name.substr(0, 2)) {
3123 sawType = true;
3124 }
3125 }
3126 }
3127 ++lineIter;
3128 }
3129 if (sawType && sawConst) {
3130 // if found, name first
3131 lineStart->fName = name;
3132 lineStart->fMarkType = MarkType::kConst;
3133 fParent->fChildren.emplace_back(lineStart);
3134 fConstExpr = lineStart;
3135 }
3136 }
3137 break;
Cary Clark8032b982017-07-28 11:04:54 -04003138 case ';':
3139 if (fInCharCommentString || fInBrace) {
3140 break;
3141 }
3142 if (!this->checkForWord()) {
3143 return false;
3144 }
Cary Clark0d225392018-06-07 09:59:07 -04003145 if (fConstExpr) {
3146 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3147 fConstExpr = nullptr;
3148 }
Cary Clark8032b982017-07-28 11:04:54 -04003149 if (Definition::Type::kKeyWord == fParent->fType
3150 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003151 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3152 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003153 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3154 this->popObject();
3155 }
Cary Clark8032b982017-07-28 11:04:54 -04003156 if (KeyWord::kEnum == fParent->fKeyWord) {
3157 fInEnum = false;
3158 }
Cary Clark61313f32018-10-08 14:57:48 -04003159 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003160 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003161 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3162 this->popObject();
3163 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003164 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003165 } else if (Definition::Type::kBracket == fParent->fType
3166 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3167 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3168 list<Definition>::iterator baseIter = fParent->fTokens.end();
3169 list<Definition>::iterator namedIter = fParent->fTokens.end();
3170 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003171 fParent->fTokens.begin() != tokenIter; ) {
3172 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003173 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003174 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003175 if (namedIter != fParent->fTokens.end()) {
3176 return reportError<bool>("found two named member tokens");
3177 }
3178 namedIter = tokenIter;
3179 }
3180 baseIter = tokenIter;
3181 } else {
3182 break;
3183 }
3184 }
3185 // FIXME: if a member definition spans multiple lines, this won't work
3186 if (namedIter != fParent->fTokens.end()) {
3187 if (baseIter == namedIter) {
3188 return this->reportError<bool>("expected type before named token");
3189 }
3190 Definition* member = &*namedIter;
3191 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003192 if (!member->fTerminator) {
3193 member->fTerminator = member->fContentEnd;
3194 }
Cary Clark8032b982017-07-28 11:04:54 -04003195 fParent->fChildren.push_back(member);
3196 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3197 member->fChildren.push_back(&*nameType);
3198 }
Cary Clark8032b982017-07-28 11:04:54 -04003199 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003200 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003201 } else if (fParent->fChildren.size() > 0) {
3202 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003203 Definition* priorEnum = fPriorEnum;
3204 fPriorEnum = nullptr;
3205 if (!priorEnum) {
3206 while (fParent->fChildren.begin() != lastIter) {
3207 std::advance(lastIter, -1);
3208 priorEnum = *lastIter;
3209 if (Definition::Type::kBracket != priorEnum->fType ||
3210 (Bracket::kSlashSlash != priorEnum->fBracket
3211 && Bracket::kSlashStar != priorEnum->fBracket)) {
3212 break;
3213 }
Cary Clark8032b982017-07-28 11:04:54 -04003214 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003215 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003216 }
3217 if (Definition::Type::kKeyWord == priorEnum->fType
3218 && KeyWord::kEnum == priorEnum->fKeyWord) {
3219 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003220 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003221 while (tokenWalker != fParent->fTokens.end()) {
3222 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003223 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003224 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3225 break;
3226 }
3227 }
3228 while (tokenWalker != fParent->fTokens.end()) {
3229 std::advance(tokenWalker, 1);
3230 const Definition* test = &*tokenWalker;
3231 if (Definition::Type::kBracket != test->fType ||
3232 (Bracket::kSlashSlash != test->fBracket
3233 && Bracket::kSlashStar != test->fBracket)) {
3234 break;
3235 }
3236 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003237 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003238 Definition* start = &*tokenWalker;
3239 bool foundExpected = true;
3240 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3241 const Definition* test = &*tokenWalker;
3242 if (expected != test->fKeyWord) {
3243 foundExpected = false;
3244 break;
3245 }
3246 if (tokenWalker == fParent->fTokens.end()) {
3247 break;
3248 }
3249 std::advance(tokenWalker, 1);
3250 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003251 if (!foundExpected) {
3252 foundExpected = true;
3253 tokenWalker = saveTokenWalker;
3254 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3255 const Definition* test = &*tokenWalker;
3256 if (expected != test->fKeyWord) {
3257 foundExpected = false;
3258 break;
3259 }
3260 if (tokenWalker == fParent->fTokens.end()) {
3261 break;
3262 }
3263 if (KeyWord::kNone != expected) {
3264 std::advance(tokenWalker, 1);
3265 }
3266 }
3267 if (foundExpected) {
3268 auto nameToken = priorEnum->fTokens.begin();
3269 string enumName = string(nameToken->fContentStart,
3270 nameToken->fContentEnd - nameToken->fContentStart);
3271 const Definition* test = &*tokenWalker;
3272 string constType = string(test->fContentStart,
3273 test->fContentEnd - test->fContentStart);
3274 if (enumName != constType) {
3275 foundExpected = false;
3276 } else {
3277 std::advance(tokenWalker, 1);
3278 }
3279 }
3280 }
Cary Clark8032b982017-07-28 11:04:54 -04003281 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3282 const char* nameStart = tokenWalker->fStart;
3283 std::advance(tokenWalker, 1);
3284 if (tokenWalker != fParent->fTokens.end()) {
3285 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003286 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003287 start->fName = string(nameStart, tp.fChar - nameStart);
3288 start->fContentEnd = fChar;
3289 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003290 fPriorEnum = priorEnum;
3291 }
Cary Clark8032b982017-07-28 11:04:54 -04003292 }
3293 }
3294 }
3295 this->addPunctuation(Punctuation::kSemicolon);
3296 fInFunction = false;
3297 break;
3298 case '~':
3299 if (fInEnum) {
3300 break;
3301 }
3302 case '0': case '1': case '2': case '3': case '4':
3303 case '5': case '6': case '7': case '8': case '9':
3304 // TODO: don't want to parse numbers, but do need to track for enum defs
3305 // break;
3306 case 'A': case 'B': case 'C': case 'D': case 'E':
3307 case 'F': case 'G': case 'H': case 'I': case 'J':
3308 case 'K': case 'L': case 'M': case 'N': case 'O':
3309 case 'P': case 'Q': case 'R': case 'S': case 'T':
3310 case 'U': case 'V': case 'W': case 'X': case 'Y':
3311 case 'Z': case '_':
3312 case 'a': case 'b': case 'c': case 'd': case 'e':
3313 case 'f': case 'g': case 'h': case 'i': case 'j':
3314 case 'k': case 'l': case 'm': case 'n': case 'o':
3315 case 'p': case 'q': case 'r': case 's': case 't':
3316 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003317 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003318 if (fInCharCommentString || fInBrace) {
3319 break;
3320 }
3321 if (!fIncludeWord) {
3322 fIncludeWord = fChar;
3323 }
3324 break;
3325 }
3326done:
3327 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003328 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003329 return true;
3330}
3331
3332void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003333 IncludeParser::ValidateKeyWords();
3334}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003335
Cary Clark186d08f2018-04-03 08:43:27 -04003336bool IncludeParser::references(const SkString& file) const {
3337 // if includes weren't passed one at a time, assume all references are valid
3338 if (fIncludeMap.empty()) {
3339 return true;
3340 }
3341 SkASSERT(file.endsWith(".bmh") );
3342 string root(file.c_str(), file.size() - 4);
3343 string kReference("_Reference");
3344 if (string::npos != root.find(kReference)) {
3345 root = root.substr(0, root.length() - kReference.length());
3346 }
3347 if (fIClassMap.end() != fIClassMap.find(root)) {
3348 return true;
3349 }
3350 if (fIStructMap.end() != fIStructMap.find(root)) {
3351 return true;
3352 }
Cary Clark224c7002018-06-27 11:00:21 -04003353 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3354 return true;
3355 }
Cary Clarka90ea222018-10-16 10:30:28 -04003356 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3357 return true;
3358 }
Cary Clark224c7002018-06-27 11:00:21 -04003359 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3360 return true;
3361 }
Cary Clark186d08f2018-04-03 08:43:27 -04003362 return false;
3363}
3364
Cary Clark2dc84ad2018-01-26 12:56:22 -05003365void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3366 if (!sk_isdir(includes)) {
3367 IncludeParser::RemoveOneFile(docs, includes);
3368 } else {
3369 SkOSFile::Iter it(includes, ".h");
3370 for (SkString file; it.next(&file); ) {
3371 SkString p = SkOSPath::Join(includes, file.c_str());
3372 const char* hunk = p.c_str();
3373 if (!SkStrEndsWith(hunk, ".h")) {
3374 continue;
3375 }
3376 IncludeParser::RemoveOneFile(docs, hunk);
3377 }
3378 }
3379}
3380
3381void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3382 const char* lastForward = strrchr(includesFile, '/');
3383 const char* lastBackward = strrchr(includesFile, '\\');
3384 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3385 if (!last) {
3386 last = includesFile;
3387 } else {
3388 last += 1;
3389 }
3390 SkString baseName(last);
3391 SkASSERT(baseName.endsWith(".h"));
3392 baseName.remove(baseName.size() - 2, 2);
3393 baseName.append("_Reference.bmh");
3394 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3395 remove(fullName.c_str());
3396}
Cary Clark224c7002018-06-27 11:00:21 -04003397
3398Bracket IncludeParser::topBracket() const {
3399 Definition* parent = this->parentBracket(fParent);
3400 return parent ? parent->fBracket : Bracket::kNone;
3401}