blob: 5662bcfdbcdabee30cf16a0626050aa8f1d666b1 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Cary Clark2dc84ad2018-01-26 12:56:22 -05008#include "SkOSFile.h"
9#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040010
Cary Clark2da9fb82018-11-01 09:29:36 -040011#include "bmhParser.h"
12#include "includeParser.h"
13
Cary Clark8032b982017-07-28 11:04:54 -040014const IncludeKey kKeyWords[] = {
15 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040016 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040017 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clarkd2ca79c2018-08-10 13:09:13 -040018 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040019 { "bool", KeyWord::kBool, KeyProperty::kNumber },
20 { "char", KeyWord::kChar, KeyProperty::kNumber },
21 { "class", KeyWord::kClass, KeyProperty::kObject },
22 { "const", KeyWord::kConst, KeyProperty::kModifier },
23 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
24 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
25 { "double", KeyWord::kDouble, KeyProperty::kNumber },
26 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
27 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
28 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
29 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050030 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040031 { "float", KeyWord::kFloat, KeyProperty::kNumber },
32 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
33 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
34 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
35 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
36 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
37 { "inline", KeyWord::kInline, KeyProperty::kModifier },
38 { "int", KeyWord::kInt, KeyProperty::kNumber },
39 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
40 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
41 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
42 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
43 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
44 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
45 { "static", KeyWord::kStatic, KeyProperty::kModifier },
46 { "struct", KeyWord::kStruct, KeyProperty::kObject },
47 { "template", KeyWord::kTemplate, KeyProperty::kObject },
48 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clark0d225392018-06-07 09:59:07 -040049 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040050 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040051 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040052 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
53 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040054 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040055 { "union", KeyWord::kUnion, KeyProperty::kObject },
56 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
Cary Clark61313f32018-10-08 14:57:48 -040057 { "using", KeyWord::kUsing, KeyProperty::kObject },
Cary Clark8032b982017-07-28 11:04:54 -040058 { "void", KeyWord::kVoid, KeyProperty::kNumber },
59};
60
61const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
62
63KeyWord IncludeParser::FindKey(const char* start, const char* end) {
64 int ch = 0;
65 for (size_t index = 0; index < kKeyWordCount; ) {
66 if (start[ch] > kKeyWords[index].fName[ch]) {
67 ++index;
Cary Clark61313f32018-10-08 14:57:48 -040068 if (ch > 0 && (index == kKeyWordCount ||
69 kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1])) {
Cary Clark8032b982017-07-28 11:04:54 -040070 return KeyWord::kNone;
71 }
72 continue;
73 }
74 if (start[ch] < kKeyWords[index].fName[ch]) {
75 return KeyWord::kNone;
76 }
77 ++ch;
78 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040079 if (end - start < (int) strlen(kKeyWords[index].fName)) {
80 return KeyWord::kNone;
81 }
Cary Clark8032b982017-07-28 11:04:54 -040082 return kKeyWords[index].fKeyWord;
83 }
84 }
85 return KeyWord::kNone;
86}
87
88void IncludeParser::ValidateKeyWords() {
89 for (size_t index = 1; index < kKeyWordCount; ++index) {
90 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
91 == (int) kKeyWords[index].fKeyWord);
92 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
93 }
94}
95
96void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050097 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040098 fIncludeWord = nullptr;
99 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
100 Definition* def = &fParent->fTokens.back();
101 this->addDefinition(def);
102 if (KeyWord::kEnum == fParent->fKeyWord) {
103 fInEnum = true;
104 }
105 }
106}
107
Cary Clark61313f32018-10-08 14:57:48 -0400108static bool looks_like_method(const TextParser& tp) {
109 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
110 t.skipSpace();
111 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
112 && !t.skipExact("enum")) {
113 return true;
114 }
115 t.skipSpace();
116 if (t.skipExact("SK_API")) {
117 t.skipSpace();
118 }
119 if (!isupper(t.peek())) {
120 return true;
121 }
122 return nullptr != t.strnchr('(', t.fEnd);
123}
124
125static bool looks_like_forward_declaration(const TextParser& tp) {
126 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
127 t.skipSpace();
128 if (!t.skipExact("struct") && !t.skipExact("class") && !t.skipExact("enum class")
129 && !t.skipExact("enum")) {
130 return false;
131 }
132 t.skipSpace();
133 if (t.skipExact("SK_API")) {
134 t.skipSpace();
135 }
136 if (!isupper(t.peek())) {
137 return false;
138 }
139 t.skipToNonAlphaNum();
140 if (t.eof() || ';' != t.next()) {
141 return false;
142 }
143 if (t.eof() || '\n' != t.next()) {
144 return false;
145 }
146 return t.eof();
147}
148
149static bool looks_like_constructor(const TextParser& tp) {
150 TextParser t(tp.fFileName, tp.fLine, tp.lineEnd(), tp.fLineCount);
151 t.skipSpace();
152 if (!isupper(t.peek())) {
153 if (':' == t.next() && ' ' >= t.peek()) {
154 return true;
155 }
156 return false;
157 }
158 t.skipToNonAlphaNum();
159 if ('(' != t.peek()) {
160 return false;
161 }
162 if (!t.skipToEndBracket(')')) {
163 return false;
164 }
165 SkAssertResult(')' == t.next());
166 t.skipSpace();
167 return tp.fChar == t.fChar;
168}
169
170static bool looks_like_class_decl(const TextParser& tp) {
171 TextParser t(tp.fFileName, tp.fLine, tp.fChar, tp.fLineCount);
172 t.skipSpace();
173 if (!t.skipExact("class")) {
174 return false;
175 }
176 t.skipSpace();
177 if (t.skipExact("SK_API")) {
178 t.skipSpace();
179 }
180 if (!isupper(t.peek())) {
181 return false;
182 }
183 t.skipToNonAlphaNum();
184 return !t.skipToEndBracket('(');
185}
186
187static bool looks_like_const(const TextParser& tp) {
188 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
189 if (!t.startsWith("static constexpr ")) {
190 return false;
191 }
192 if (t.skipToEndBracket(" k")) {
193 SkAssertResult(t.skipExact(" k"));
194 } else if (t.skipToEndBracket(" SK_")) {
195 SkAssertResult(t.skipExact(" SK_"));
196 } else {
197 return false;
198 }
199 if (!isupper(t.peek())) {
200 return false;
201 }
202 return t.skipToEndBracket(" = ");
203}
204
205static bool looks_like_member(const TextParser& tp) {
206 TextParser t(tp.fFileName, tp.fChar, tp.lineEnd(), tp.fLineCount);
207 const char* end = t.anyOf("(;");
208 if (!end || '(' == *end) {
209 return false;
210 }
211 bool foundMember = false;
212 do {
213 const char* next = t.anyOf(" ;");
214 if (';' == *next) {
215 break;
216 }
217 t.skipTo(next);
218 t.skipSpace();
219 foundMember = 'f' == t.fChar[0] && isupper(t.fChar[1]);
220 } while (true);
221 return foundMember;
222}
223
224static void skip_constructor_initializers(TextParser& t) {
225 SkAssertResult(':' == t.next());
226 do {
227 t.skipWhiteSpace();
228 t.skipToNonAlphaNum();
229 t.skipWhiteSpace();
230 if ('{' == t.peek()) {
231 t.skipToBalancedEndBracket('{', '}');
232 }
233 do {
234 const char* limiter = t.anyOf("(,{");
235 t.skipTo(limiter);
236 if ('(' != t.peek()) {
237 break;
238 }
239 t.skipToBalancedEndBracket('(', ')');
240 } while (true);
241 if ('{' == t.peek()) {
242 return;
243 }
244 SkAssertResult(',' == t.next());
245 } while (true);
246}
247
248static const char kInline[] = "inline ";
249static const char kSK_API[] = "SK_API ";
250static const char kSK_WARN_UNUSED_RESULT[] = "SK_WARN_UNUSED_RESULT ";
251
252bool IncludeParser::advanceInclude(TextParser& i) {
253 i.skipWhiteSpace(&fCheck.fIndent, &fCheck.fWriteReturn);
254 if (fCheck.fPrivateBrace) {
255 if (i.startsWith("};")) {
256 if (fCheck.fPrivateBrace == fCheck.fBraceCount) {
257 fCheck.fPrivateBrace = 0;
258 fCheck.fDoubleReturn = true;
259 } else {
260 i.skipExact("};");
261 fCheck.fBraceCount -= 1;
262 }
263 return false;
264 }
265 if (i.startsWith("public:")) {
266 if (fCheck.fBraceCount <= fCheck.fPrivateBrace) {
267 fCheck.fPrivateBrace = 0;
268 if (fCheck.fPrivateProtected) {
269 i.skipExact("public:");
270 }
271 } else {
272 i.skipExact("public:");
273 }
274 } else {
275 fCheck.fBraceCount += i.skipToLineBalance('{', '}');
276 }
277 return false;
278 } else if (i.startsWith("};")) {
279 fCheck.fDoubleReturn = 2;
280 }
281 if (i.skipExact(kInline)) {
282 fCheck.fSkipInline = true;
283 return false;
284 }
285 if (i.skipExact(kSK_API)) {
286 fCheck.fSkipAPI = true;
287 return false;
288 }
289 if (i.skipExact(kSK_WARN_UNUSED_RESULT)) {
290 fCheck.fSkipWarnUnused = true;
291 return false;
292 }
293 if (i.skipExact("SK_ATTR_DEPRECATED")) {
294 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
295 return false;
296 }
297 if (i.skipExact("SkDEBUGCODE")) {
298 i.skipWhiteSpace();
299 if ('(' != i.peek()) {
300 i.reportError("expected open paren");
301 }
302 TextParserSave save(&i);
303 SkAssertResult(i.skipToBalancedEndBracket('(', ')'));
304 fCheck.fInDebugCode = i.fChar - 1;
305 save.restore();
306 SkAssertResult('(' == i.next());
307 }
308 if ('{' == i.peek()) {
309 if (looks_like_method(i)) {
310 fCheck.fState = CheckCode::State::kMethod;
311 if (!i.skipToBalancedEndBracket('{', '}')) {
312 i.reportError("unbalanced open brace");
313 }
314 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
315 return false;
316 } else if (looks_like_class_decl(i)) {
317 fCheck.fState = CheckCode::State::kClassDeclaration;
318 fCheck.fPrivateBrace = fCheck.fBraceCount + 1;
319 fCheck.fPrivateProtected = false;
320 }
321 }
322 if (':' == i.peek() && looks_like_constructor(i)) {
323 fCheck.fState = CheckCode::State::kConstructor;
324 skip_constructor_initializers(i);
325 return false;
326 }
327 if ('#' == i.peek()) {
328 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
329 return false;
330 }
331 if (i.startsWith("//")) {
332 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
333 return false;
334 }
335 if (i.startsWith("/*")) {
336 i.skipToEndBracket("*/");
337 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
338 return false;
339 }
340 if (looks_like_forward_declaration(i)) {
341 fCheck.fState = CheckCode::State::kForwardDeclaration;
342 i.skipToLineStart(&fCheck.fIndent, &fCheck.fWriteReturn);
343 return false;
344 }
345 if (i.skipExact("private:") || i.skipExact("protected:")) {
346 if (!fCheck.fBraceCount) {
347 i.reportError("expect private in brace");
348 }
349 fCheck.fPrivateBrace = fCheck.fBraceCount;
350 fCheck.fPrivateProtected = true;
351 return false;
352 }
353 const char* funcEnd = i.anyOf("(\n");
354 if (funcEnd && '(' == funcEnd[0] && '_' == *i.anyOf("_(")
355 && (i.contains("internal_", funcEnd, nullptr)
356 || i.contains("private_", funcEnd, nullptr)
357 || i.contains("legacy_", funcEnd, nullptr)
358 || i.contains("temporary_", funcEnd, nullptr))) {
359 i.skipTo(funcEnd);
360 if (!i.skipToBalancedEndBracket('(', ')')) {
361 i.reportError("unbalanced open parent");
362 }
363 i.skipSpace();
364 i.skipExact("const ");
365 i.skipSpace();
366 if (';' == i.peek()) {
367 i.next();
368 }
369 fCheck.fState = CheckCode::State::kNone;
370 return false;
371 }
372 return true;
373}
374
375void IncludeParser::codeBlockAppend(string& result, char ch) const {
376 if (Elided::kYes == fElided && fCheck.fBraceCount) {
377 return;
378 }
379 this->stringAppend(result, ch);
380}
381
382void IncludeParser::codeBlockSpaces(string& result, int indent) const {
383 if (!indent) {
384 return;
385 }
386 if (Elided::kYes == fElided && fCheck.fBraceCount) {
387 return;
388 }
389 SkASSERT(indent > 0);
390 if (fDebugWriteCodeBlock) {
391 SkDebugf("%*c", indent, ' ');
392 }
393 result.append(indent, ' ');
394}
395
396string IncludeParser::writeCodeBlock(const Definition& iDef, MarkType markType) {
397 TextParser i(&iDef);
398 fElided = Elided::kNo;
399 const char* before = iDef.fContentStart;
400 while (' ' == *--before)
401 ;
402 int startIndent = iDef.fContentStart - before - 1;
403 return writeCodeBlock(i, markType, startIndent);
404}
405
406string IncludeParser::writeCodeBlock(TextParser& i, MarkType markType, int startIndent) {
407 string result;
408 char last;
409 int lastIndent = 0;
410 bool lastDoubleMeUp = false;
411 fCheck.reset();
Cary Clarka90ea222018-10-16 10:30:28 -0400412 if (MarkType::kDefine == markType) {
413 result = "#define ";
414 } else {
415 this->codeBlockSpaces(result, startIndent);
416 }
Cary Clark61313f32018-10-08 14:57:48 -0400417 do {
418 if (!this->advanceInclude(i)) {
419 continue;
420 }
421 do {
422 last = i.peek();
423 SkASSERT(' ' < last);
424 if (fCheck.fInDebugCode == i.fChar) {
425 fCheck.fInDebugCode = nullptr;
426 i.next(); // skip close paren
427 break;
428 }
429 if (CheckCode::State::kMethod == fCheck.fState) {
430 this->codeBlockAppend(result, ';');
431 fCheck.fState = CheckCode::State::kNone;
432 }
433 if (fCheck.fWriteReturn) {
434 this->codeBlockAppend(result, '\n');
435 bool doubleMeUp = i.startsWith("typedef ") || looks_like_const(i)
436 || (!strncmp("struct ", i.fStart, 7) && looks_like_member(i));
437 if ((!--fCheck.fDoubleReturn && !i.startsWith("};")) || i.startsWith("enum ")
438 || i.startsWith("typedef ") || doubleMeUp || fCheck.fTypedefReturn
439 || (fCheck.fIndent && (i.startsWith("class ") || i.startsWith("struct ")))) {
440 if (lastIndent > 0 && (!doubleMeUp || !lastDoubleMeUp)) {
441 this->codeBlockAppend(result, '\n');
442 }
443 fCheck.fTypedefReturn = false;
444 lastDoubleMeUp = doubleMeUp;
445 }
446 if (doubleMeUp) {
447 fCheck.fTypedefReturn = true;
448 }
449 lastIndent = fCheck.fIndent;
450 }
451 if (fCheck.fIndent) {
452 size_t indent = fCheck.fIndent;
453 if (fCheck.fSkipInline && indent > sizeof(kInline)) {
454 indent -= sizeof(kInline) - 1;
455 }
456 if (fCheck.fSkipAPI && indent > sizeof(kSK_API)) {
457 indent -= sizeof(kSK_API) - 1;
458 }
459 if (fCheck.fSkipWarnUnused && indent > sizeof(kSK_WARN_UNUSED_RESULT)) {
460 indent -= sizeof(kSK_WARN_UNUSED_RESULT) - 1;
461 }
462
463 this->codeBlockSpaces(result, indent);
464 }
465 this->codeBlockAppend(result, last);
466 fCheck.fWriteReturn = false;
467 fCheck.fIndent = 0;
468 fCheck.fBraceCount += '{' == last;
469 fCheck.fBraceCount -= '}' == last;
470 if (';' == last) {
471 fCheck.fSkipInline = false;
472 fCheck.fSkipAPI = false;
473 fCheck.fSkipWarnUnused = false;
474 }
475 if (fCheck.fBraceCount < 0) {
476 i.reportError("unbalanced close brace");
477 return result;
478 }
479 i.next();
480 } while (!i.eof() && ' ' < i.peek() && !i.startsWith("//"));
481 } while (!i.eof());
482 if (CheckCode::State::kMethod == fCheck.fState) {
483 this->codeBlockAppend(result, ';');
484 }
485 bool elidedTemplate = Elided::kYes == fElided && !strncmp(i.fStart, "template ", 9);
486 bool elidedTClass = elidedTemplate && MarkType::kClass == markType;
487 if (fCheck.fWriteReturn || elidedTClass) {
488 this->codeBlockAppend(result, '\n');
489 }
490 if ((MarkType::kFunction != markType && lastIndent > startIndent) || elidedTClass) {
491 this->codeBlockAppend(result, '}');
492 }
493 this->codeBlockAppend(result, ';');
494 if (MarkType::kFunction != markType || elidedTemplate) {
495 this->codeBlockAppend(result, '\n');
496 }
497 return result;
498}
499
Ben Wagner63fd7602017-10-09 15:45:33 -0400500void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400501 const vector<string>& foundParams) {
502 for (auto& methodParam : methodParams) {
503 bool found = false;
504 for (auto& foundParam : foundParams) {
505 if (methodParam == foundParam) {
506 found = true;
507 break;
508 }
509 }
510 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400511 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400512 }
513 }
514 for (auto& foundParam : foundParams) {
515 bool found = false;
516 for (auto& methodParam : methodParams) {
517 if (methodParam == foundParam) {
518 found = true;
519 break;
520 }
521 }
522 if (!found) {
523 this->reportError("doxygen param does not match method declaration");
524 }
525 }
526}
527
528bool IncludeParser::checkForWord() {
529 if (!fIncludeWord) {
530 return true;
531 }
532 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400533 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
534 Bracket bracket = this->topBracket();
535 if (Bracket::kParen == bracket) {
536 return true;
537 }
538 }
Cary Clark8032b982017-07-28 11:04:54 -0400539 if (KeyWord::kNone != keyWord) {
540 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
541 this->addKeyword(keyWord);
542 return true;
543 }
544 } else {
545 this->addWord();
546 return true;
547 }
548 Definition* poundDef = fParent;
549 if (!fParent) {
550 return reportError<bool>("expected parent");
551 }
552 if (Definition::Type::kBracket != poundDef->fType) {
553 return reportError<bool>("expected bracket");
554 }
555 if (Bracket::kPound != poundDef->fBracket) {
556 return reportError<bool>("expected preprocessor");
557 }
558 if (KeyWord::kNone != poundDef->fKeyWord) {
559 return reportError<bool>("already found keyword");
560 }
561 poundDef->fKeyWord = keyWord;
562 fIncludeWord = nullptr;
563 switch (keyWord) {
564 // these do not link to other # directives
565 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400566 if (!fInBrace) {
567 SkASSERT(!fInDefine);
568 fInDefine = true;
569 }
Cary Clark8032b982017-07-28 11:04:54 -0400570 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500571 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400572 break;
573 // these start a # directive link
574 case KeyWord::kIf:
575 case KeyWord::kIfdef:
576 case KeyWord::kIfndef:
577 break;
578 // these continue a # directive link
579 case KeyWord::kElif:
580 case KeyWord::kElse: {
581 this->popObject(); // pop elif
582 if (Bracket::kPound != fParent->fBracket) {
583 return this->reportError<bool>("expected preprocessor directive");
584 }
585 this->popBracket(); // pop if
586 poundDef->fParent = fParent;
587 this->addDefinition(poundDef); // push elif back
588 } break;
589 // this ends a # directive link
590 case KeyWord::kEndif:
591 // FIXME : should this be calling popBracket() instead?
592 this->popObject(); // pop endif
593 if (Bracket::kPound != fParent->fBracket) {
594 return this->reportError<bool>("expected preprocessor directive");
595 }
596 this->popBracket(); // pop if/else
597 break;
598 default:
599 SkASSERT(0);
600 }
601 return true;
602}
603
604string IncludeParser::className() const {
605 string name(fParent->fName);
606 size_t slash = name.find_last_of("/");
607 if (string::npos == slash) {
608 slash = name.find_last_of("\\");
609 }
610 SkASSERT(string::npos != slash);
611 string result = name.substr(slash);
612 result = result.substr(1, result.size() - 3);
613 return result;
614}
615
Cary Clarka90ea222018-10-16 10:30:28 -0400616void IncludeParser::writeCodeBlock() {
Cary Clark61313f32018-10-08 14:57:48 -0400617 for (auto& classMapper : fIClassMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400618 classMapper.second.fCode = this->writeCodeBlock(classMapper.second, MarkType::kClass);
Cary Clark61313f32018-10-08 14:57:48 -0400619 }
620 for (auto& enumMapper : fIEnumMap) {
Cary Clarka90ea222018-10-16 10:30:28 -0400621 enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second,
622 enumMapper.second->fMarkType);
623 }
624 for (auto& typedefMapper : fITypedefMap) {
625 typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second,
626 typedefMapper.second->fMarkType);
627 }
628 for (auto& defineMapper : fIDefineMap) {
629 defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second,
630 defineMapper.second->fMarkType);
Cary Clark61313f32018-10-08 14:57:48 -0400631 }
632}
633
Cary Clark884dd7d2017-10-11 10:37:52 -0400634#include <sstream>
635#include <iostream>
636
Cary Clark8032b982017-07-28 11:04:54 -0400637bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400638 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400639 string className = classMapper.first;
640 auto finder = bmhParser.fClassMap.find(className);
641 if (bmhParser.fClassMap.end() == finder) {
642 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400643 continue;
644 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400645 }
646 for (auto& classMapper : fIClassMap) {
647 string className = classMapper.first;
648 std::istringstream iss(className);
649 string classStr;
650 string classBase;
651 RootDefinition* root = nullptr;
652 while (std::getline(iss, classStr, ':')) {
653 if (root) {
654 if (!classStr.length()) {
655 continue;
656 }
657 classBase += "::" + classStr;
658 auto finder = root->fBranches.find(classBase);
659 if (root->fBranches.end() != finder) {
660 root = finder->second;
661 } else {
662 SkASSERT(0);
663 }
664 } else {
665 classBase = classStr;
666 auto finder = bmhParser.fClassMap.find(classBase);
667 if (bmhParser.fClassMap.end() != finder) {
668 root = &finder->second;
669 } else {
670 SkASSERT(0);
671 }
672 }
673 }
Cary Clark8032b982017-07-28 11:04:54 -0400674 auto& classMap = classMapper.second;
675 auto& tokens = classMap.fTokens;
676 for (const auto& token : tokens) {
677 if (token.fPrivate) {
678 continue;
679 }
680 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400681 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400682 switch (token.fMarkType) {
683 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400684 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400685 continue;
686 }
Cary Clark8032b982017-07-28 11:04:54 -0400687 if (!def) {
688 string paramName = className + "::";
689 paramName += string(token.fContentStart,
690 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400691 if (string::npos != paramName.find('\n')) {
692 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
693 paramName.end());
694 }
Cary Clarkce101242017-09-01 15:51:02 -0400695 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400696 if (!def && 0 == token.fName.find("operator")) {
697 string operatorName = className + "::";
698 TextParser oper("", token.fStart, token.fContentEnd, 0);
699 const char* start = oper.strnstr("operator", token.fContentEnd);
700 SkASSERT(start);
701 oper.skipTo(start);
702 oper.skipToEndBracket('(');
703 int parens = 0;
704 do {
705 if ('(' == oper.peek()) {
706 ++parens;
707 } else if (')' == oper.peek()) {
708 --parens;
709 }
710 } while (!oper.eof() && oper.next() && parens > 0);
711 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400712 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400713 }
714 }
715 if (!def) {
716 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
717 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400718 const char* tokenEnd = token.methodEnd();
Cary Clark8032b982017-07-28 11:04:54 -0400719 string constructorName = className + "::";
720 constructorName += string(token.fContentStart + skip,
Cary Clarkab5c9af2018-07-12 16:24:53 -0400721 tokenEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400722 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400723 }
724 if (!def && 0 == token.fName.find("SK_")) {
725 string incName = token.fName + "()";
726 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400727 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400728 if (def) {
729 if (def->fName == incName) {
730 def->fVisited = true;
731 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400732 def = root->find(className + "::toString",
733 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400734 if (def) {
735 def->fVisited = true;
736 } else {
737 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500738 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400739 }
740 }
741 break;
742 } else {
743 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500744 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400745 }
746 }
747 }
748 if (!def) {
749 bool allLower = true;
750 for (size_t index = 0; index < token.fName.length(); ++index) {
751 if (!islower(token.fName[index])) {
752 allLower = false;
753 break;
754 }
755 }
756 if (allLower) {
757 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400758 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400759 }
760 }
761 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400762 if (0 == token.fName.find("SkDEBUGCODE")) {
763 break;
764 }
765 }
766 if (!def) {
767 // simple method names inside nested classes have a bug and are missing trailing parens
768 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400769 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400770 }
771 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500772 if (!root->fDeprecated) {
773 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
774 fFailed = true;
775 }
Cary Clark8032b982017-07-28 11:04:54 -0400776 break;
777 }
Cary Clark73fa9722017-08-29 17:36:51 -0400778 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400779 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400780 if (token.fDeprecated && !def->fDeprecated) {
781 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
782 }
Cary Clark8032b982017-07-28 11:04:54 -0400783 } else {
784 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500785 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400786 }
787 } break;
788 case MarkType::kComment:
789 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400790 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400791 case MarkType::kEnum: {
792 if (!def) {
793 // work backwards from first word to deduce #Enum name
794 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
795 SkAssertResult(firstMember.skipName("enum"));
796 SkAssertResult(firstMember.skipToEndBracket('{'));
797 firstMember.next();
798 firstMember.skipWhiteSpace();
799 SkASSERT('k' == firstMember.peek());
800 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400801 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400802 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400803 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400804 const char* lastUnderscore = nullptr;
805 do {
806 if (!firstMember.skipToEndBracket('_')) {
807 break;
808 }
809 if (firstMember.fChar > wordEnd) {
810 break;
811 }
812 lastUnderscore = firstMember.fChar;
813 } while (firstMember.next());
814 if (lastUnderscore) {
815 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400816 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400817 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400818 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400819 }
820 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500821 if (!root->fDeprecated) {
822 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
823 fFailed = true;
824 }
Cary Clark8032b982017-07-28 11:04:54 -0400825 break;
826 }
827 }
828 def->fVisited = true;
Cary Clark61313f32018-10-08 14:57:48 -0400829 bool hasCode = false;
830 bool hasPopulate = true;
Cary Clark8032b982017-07-28 11:04:54 -0400831 for (auto& child : def->fChildren) {
832 if (MarkType::kCode == child->fMarkType) {
Cary Clark61313f32018-10-08 14:57:48 -0400833 hasPopulate = std::any_of(child->fChildren.begin(),
834 child->fChildren.end(), [](auto grandChild){
835 return MarkType::kPopulate == grandChild->fMarkType; });
836 if (!hasPopulate) {
837 def = child;
838 }
839 hasCode = true;
Cary Clark8032b982017-07-28 11:04:54 -0400840 break;
841 }
842 }
Cary Clark61313f32018-10-08 14:57:48 -0400843 if (!hasCode) {
Cary Clark56356312018-02-08 14:45:18 -0500844 if (!root->fDeprecated) {
845 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
846 fFailed = true;
847 }
Cary Clark8032b982017-07-28 11:04:54 -0400848 break;
849 }
Cary Clark61313f32018-10-08 14:57:48 -0400850 if (!hasPopulate) {
851 if (def->crossCheck(token)) {
852 def->fVisited = true;
853 } else {
854 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
855 fFailed = true;
856 }
Cary Clark8032b982017-07-28 11:04:54 -0400857 }
858 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400859 string constName = MarkType::kEnumClass == token.fMarkType ?
860 fullName : className;
861 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400862 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400863 if (!def) {
864 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400865 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400866 }
867 if (!def) {
868 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500869 if (!root->fDeprecated) {
870 SkDebugf("const missing from bmh: %s\n", constName.c_str());
871 fFailed = true;
872 }
Cary Clark8032b982017-07-28 11:04:54 -0400873 }
874 } else {
875 def->fVisited = true;
876 }
877 }
878 } break;
879 case MarkType::kMember:
880 if (def) {
881 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500882 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400883 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500884 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400885 }
886 break;
Cary Clark2f466242017-12-11 16:03:17 -0500887 case MarkType::kTypedef:
888 if (def) {
889 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500890 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500891 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500892 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500893 }
894 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400895 case MarkType::kConst:
896 if (def) {
897 def->fVisited = true;
898 } else if (!root->fDeprecated) {
899 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
900 fFailed = true;
901 }
902 break;
Cary Clark8032b982017-07-28 11:04:54 -0400903 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400904 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400905 break;
906 }
907 }
908 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500909 int crossChecks = 0;
910 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400911 for (auto& classMapper : fIClassMap) {
912 string className = classMapper.first;
913 auto finder = bmhParser.fClassMap.find(className);
914 if (bmhParser.fClassMap.end() == finder) {
915 continue;
916 }
917 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500918 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500919 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400920 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500921 if (crossChecks) {
922 SkDebugf(".");
923 } else {
924 SkDebugf("cross-check");
925 firstCheck = className;
926 }
927 ++crossChecks;
928 }
929 if (crossChecks) {
930 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500931 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500932 }
933 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400934 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400935 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500936 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400937}
938
939IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400940 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400941 string className;
942 const Definition* test = fParent;
943 while (Definition::Type::kFileType != test->fType) {
944 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
945 className = test->fName + "::";
946 break;
947 }
948 test = test->fParent;
949 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400950 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400951 unordered_map<string, IClassDefinition>& map = fIClassMap;
952 IClassDefinition& markupDef = map[className];
953 if (markupDef.fStart) {
954 typedef IClassDefinition* IClassDefPtr;
955 return INHERITED::reportError<IClassDefPtr>("class already defined");
956 }
957 markupDef.fFileName = fFileName;
958 markupDef.fStart = includeDef.fStart;
959 markupDef.fContentStart = includeDef.fStart;
960 markupDef.fName = className;
961 markupDef.fContentEnd = includeDef.fContentEnd;
962 markupDef.fTerminator = includeDef.fTerminator;
963 markupDef.fParent = fParent;
964 markupDef.fLineCount = fLineCount;
965 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
966 MarkType::kStruct : MarkType::kClass;
967 markupDef.fKeyWord = includeDef.fKeyWord;
968 markupDef.fType = Definition::Type::kMark;
969 fParent = &markupDef;
970 return &markupDef;
971}
972
973void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
974 auto& tokens = classDef.fTokens;
975 for (auto& token : tokens) {
976 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
977 continue;
978 }
979 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400980 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400981 }
982 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400983 case MarkType::kConst:
984 this->dumpConst(token, classDef.fName);
985 break;
Cary Clark8032b982017-07-28 11:04:54 -0400986 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500987 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500988 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400989 break;
990 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400991 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400992 break;
993 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400994 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400995 continue;
996 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400997 case MarkType::kTypedef:
998 this->dumpTypedef(token, classDef.fName);
999 break;
Cary Clark8032b982017-07-28 11:04:54 -04001000 default:
1001 SkASSERT(0);
1002 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001003 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -04001004 }
1005}
Cary Clark9174bda2017-09-19 17:39:32 -04001006void IncludeParser::dumpComment(const Definition& token) {
1007 fLineCount = token.fLineCount;
1008 fChar = fLine = token.fContentStart;
1009 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001010 bool sawParam = false;
1011 bool multiline = false;
1012 bool sawReturn = false;
1013 bool sawComment = false;
1014 bool methodHasReturn = false;
1015 vector<string> methodParams;
1016 vector<string> foundParams;
1017 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -04001018 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
1019 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001020 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -04001021 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001022 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -04001023 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -04001024 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -05001025 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -04001026 && !methodParser.strnchr('~', methodParser.fEnd);
1027 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
1028 const char* nextEnd = paren;
1029 do {
1030 string paramName;
1031 methodParser.fChar = nextEnd + 1;
1032 methodParser.skipSpace();
1033 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
1034 continue;
1035 }
1036 methodParams.push_back(paramName);
1037 } while (')' != nextEnd[0]);
1038 }
Cary Clark9174bda2017-09-19 17:39:32 -04001039 for (const auto& child : token.fTokens) {
1040 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
1041 break;
1042 }
Cary Clark8032b982017-07-28 11:04:54 -04001043 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001044 if (child.fPrivate) {
1045 break;
1046 }
Cary Clark8032b982017-07-28 11:04:54 -04001047 if ('@' == child.fContentStart[0]) {
1048 TextParser parser(&child);
1049 do {
1050 parser.next();
1051 if (parser.startsWith("param ")) {
1052 parser.skipWord("param");
1053 const char* parmStart = parser.fChar;
1054 parser.skipToSpace();
1055 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
1056 parser.skipWhiteSpace();
1057 do {
1058 size_t nextComma = parmName.find(',');
1059 string piece;
1060 if (string::npos == nextComma) {
1061 piece = parmName;
1062 parmName = "";
1063 } else {
1064 piece = parmName.substr(0, nextComma);
1065 parmName = parmName.substr(nextComma + 1);
1066 }
1067 if (sawParam) {
1068 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001069 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001070 }
Cary Clark9174bda2017-09-19 17:39:32 -04001071 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001072 } else {
1073 if (sawComment) {
1074 this->nl();
1075 }
1076 this->lf(2);
1077 }
1078 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -04001079 this->writeTag("Param", piece);
1080 this->writeSpace(2);
1081 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1082 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001083 sawParam = true;
1084 sawComment = false;
1085 } while (parmName.length());
1086 parser.skipTo(parser.fEnd);
1087 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
1088 parser.skipWord("return");
1089 if ('s' == parser.peek()) {
1090 parser.next();
1091 }
1092 if (sawParam) {
1093 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001094 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001095 }
Cary Clark9174bda2017-09-19 17:39:32 -04001096 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001097 }
1098 this->checkForMissingParams(methodParams, foundParams);
1099 sawParam = false;
1100 sawComment = false;
1101 multiline = false;
1102 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001103 this->writeTag("Return");
1104 this->writeSpace(2);
1105 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1106 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001107 sawReturn = true;
1108 parser.skipTo(parser.fEnd);
1109 } else {
1110 this->reportError("unexpected doxygen directive");
1111 }
1112 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -04001113 } else if (child.length() > 1) {
1114 const char* start = child.fContentStart;
1115 ptrdiff_t length = child.fContentEnd - start;
1116 SkASSERT(length >= 0);
1117 while (length && '/' == start[0]) {
1118 start += 1;
1119 --length;
Cary Clark8032b982017-07-28 11:04:54 -04001120 }
Cary Clark9174bda2017-09-19 17:39:32 -04001121 while (length && '/' == start[length - 1]) {
1122 length -= 1;
1123 if (length && '*' == start[length - 1]) {
1124 length -= 1;
1125 }
1126 }
1127 if (length) {
1128 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
1129 if (sawParam || sawReturn) {
1130 this->indentToColumn(8);
1131 }
1132 this->writeBlock(length, start);
1133 this->writeSpace();
1134 sawComment = true;
1135 if (sawParam || sawReturn) {
1136 multiline = true;
1137 }
Cary Clark8032b982017-07-28 11:04:54 -04001138 }
1139 }
1140 }
1141 }
1142 if (sawParam || sawReturn) {
1143 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -04001144 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001145 }
Cary Clark9174bda2017-09-19 17:39:32 -04001146 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -04001147 }
1148 if (!sawReturn) {
1149 if (!sawParam) {
1150 if (sawComment) {
1151 this->nl();
1152 }
1153 this->lf(2);
1154 }
1155 this->checkForMissingParams(methodParams, foundParams);
1156 }
1157 if (methodHasReturn != sawReturn) {
1158 if (!methodHasReturn) {
1159 this->reportError("unexpected doxygen return");
1160 } else {
1161 if (sawComment) {
1162 this->nl();
1163 }
1164 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001165 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -04001166 }
1167 }
1168}
1169
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001170void IncludeParser::dumpCommonTail(const Definition& token) {
1171 this->lf(2);
1172 this->writeTag("Example");
1173 this->lf(1);
1174 this->writeString("// incomplete");
1175 this->lf(1);
1176 this->writeEndTag();
1177 this->lf(2);
1178 this->writeTag("SeeAlso");
1179 this->writeSpace();
1180 this->writeString("incomplete");
1181 this->lf(2);
1182 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
1183 this->lf(2);
1184}
1185
Cary Clark224c7002018-06-27 11:00:21 -04001186void IncludeParser::dumpConst(const Definition& token, string className) {
1187 this->writeTag("Const");
1188 this->writeSpace();
1189 this->writeString(token.fName);
1190 this->writeTagTable("Line", "incomplete");
1191 this->lf(2);
1192 this->dumpComment(token);
1193}
1194
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001195void IncludeParser::dumpDefine(const Definition& token) {
1196 this->writeTag("Define", token.fName);
1197 this->lf(2);
1198 this->writeTag("Code");
1199 this->lfAlways(1);
1200 this->writeString("###$");
1201 this->lfAlways(1);
1202 this->indentToColumn(4);
1203 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
1204 this->lf(1);
1205 this->indentToColumn(0);
1206 this->writeString("$$$#");
1207
1208 this->writeEndTag();
1209 this->lf(2);
1210 this->dumpComment(token);
1211 for (auto& child : token.fTokens) {
1212 if (MarkType::kComment == child.fMarkType) {
1213 continue;
1214 }
1215 this->writeTag("Param", child.fName);
1216 this->writeSpace();
1217 this->writeString("incomplete");
1218 this->writeSpace();
1219 this->writeString("##");
1220 this->lf(1);
1221 }
1222}
1223
1224void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001225 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -04001226 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001227 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -04001228 this->lfAlways(1);
1229 this->indentToColumn(4);
1230 this->writeString("enum");
1231 this->writeSpace();
1232 if ("_anonymous" != token.fName.substr(0, 10)) {
1233 this->writeString(token.fName);
1234 this->writeSpace();
1235 }
1236 this->writeString("{");
1237 this->lfAlways(1);
1238 for (auto& child : token.fChildren) {
1239 this->indentToColumn(8);
1240 this->writeString(child->fName);
1241 if (child->length()) {
1242 this->writeSpace();
1243 this->writeBlock(child->length(), child->fContentStart);
1244 }
1245 if (',' != fLastChar) {
1246 this->writeString(",");
1247 }
1248 this->lfAlways(1);
1249 }
1250 this->indentToColumn(4);
1251 this->writeString("};");
1252 this->lf(1);
1253 this->writeString("##");
1254 this->lf(2);
1255 this->dumpComment(token);
1256 for (auto& child : token.fChildren) {
Cary Clark61313f32018-10-08 14:57:48 -04001257 // TODO: get comments before or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001258 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -04001259 this->writeSpace();
1260 this->writeString(child->fName);
1261 TextParser val(child);
1262 if (!val.eof()) {
1263 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
1264 val.next();
1265 val.skipSpace();
1266 const char* valEnd = val.anyOf(",\n");
1267 if (!valEnd) {
1268 valEnd = val.fEnd;
1269 }
1270 this->writeSpace();
1271 this->writeBlock(valEnd - val.fStart, val.fStart);
1272 } else {
1273 this->writeSpace();
1274 this->writeDefinition(*child);
1275 }
1276 }
1277 this->lf(1);
1278 for (auto comment : child->fChildren) {
1279 if (MarkType::kComment == comment->fMarkType) {
1280 TextParser parser(comment);
1281 parser.skipExact("*");
1282 parser.skipExact("*");
1283 while (!parser.eof() && parser.skipWhiteSpace()) {
1284 parser.skipExact("*");
1285 parser.skipWhiteSpace();
1286 const char* start = parser.fChar;
1287 parser.skipToEndBracket('\n');
1288 this->lf(1);
1289 this->writeBlock(parser.fChar - start, start);
1290 }
1291 }
1292 }
1293 this->writeEndTag();
1294 }
1295 this->lf(2);
1296}
1297
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001298bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
1299 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
1300 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
1301 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -04001302 return true;
1303 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001304 size_t lastBSlash = fFileName.rfind('\\');
1305 size_t lastSlash = fFileName.rfind('/');
1306 size_t lastDotH = fFileName.rfind(".h");
1307 SkASSERT(string::npos != lastDotH);
1308 if (string::npos != lastBSlash && (string::npos == lastSlash
1309 || lastBSlash < lastSlash)) {
1310 lastSlash = lastBSlash;
1311 } else if (string::npos == lastSlash) {
1312 lastSlash = -1;
1313 }
1314 lastSlash += 1;
1315 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
1316 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001317 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001318 fOut = fopen(fileName.c_str(), "wb");
1319 if (!fOut) {
1320 SkDebugf("could not open output file %s\n", globalsName.c_str());
1321 return false;
1322 }
1323 string prefixName = globalsName.substr(0, 2);
1324 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
1325 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
1326 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001327 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001328 this->lf(2);
1329 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001330 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001331 this->writeEndTag();
1332 this->lf(2);
1333 if (!fIDefineMap.empty()) {
1334 this->writeTag("Subtopic", "Define");
1335 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -04001336 this->writeEndTag();
1337 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001338 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001339 if (!fIFunctionMap.empty()) {
1340 this->writeTag("Subtopic", "Function");
1341 this->writeTag("Populate");
1342 this->writeEndTag();
1343 this->lf(2);
1344 }
1345 if (!fIEnumMap.empty()) {
1346 this->writeTag("Subtopic", "Enum");
1347 this->writeTag("Populate");
1348 this->writeEndTag();
1349 this->lf(2);
1350 }
1351 if (!fITemplateMap.empty()) {
1352 this->writeTag("Subtopic", "Template");
1353 this->writeTag("Populate");
1354 this->writeEndTag();
1355 this->lf(2);
1356 }
1357 if (!fITypedefMap.empty()) {
1358 this->writeTag("Subtopic", "Typedef");
1359 this->writeTag("Populate");
1360 this->writeEndTag();
1361 this->lf(2);
1362 }
1363 if (!fIUnionMap.empty()) {
1364 this->writeTag("Subtopic", "Union");
1365 this->writeTag("Populate");
1366 this->writeEndTag();
1367 this->lf(2);
1368 }
1369 std::map<int, Definition*> sortedDefs;
1370 for (const auto& entry : fIDefineMap) {
1371 sortedDefs[entry.second->fLineCount] = entry.second;
1372 }
1373 for (const auto& entry : fIFunctionMap) {
1374 sortedDefs[entry.second->fLineCount] = entry.second;
1375 }
1376 for (const auto& entry : fIEnumMap) {
1377 sortedDefs[entry.second->fLineCount] = entry.second;
1378 }
1379 for (const auto& entry : fITemplateMap) {
1380 sortedDefs[entry.second->fLineCount] = entry.second;
1381 }
1382 for (const auto& entry : fITypedefMap) {
1383 sortedDefs[entry.second->fLineCount] = entry.second;
1384 }
1385 for (const auto& entry : fIUnionMap) {
1386 sortedDefs[entry.second->fLineCount] = entry.second;
1387 }
1388 for (const auto& entry : sortedDefs) {
1389 const Definition* def = entry.second;
1390 this->writeBlockSeparator();
1391 switch (def->fMarkType) {
1392 case MarkType::kDefine:
1393 this->dumpDefine(*def);
1394 break;
1395 case MarkType::kMethod:
1396 this->dumpMethod(*def, globalsName);
1397 break;
1398 case MarkType::kEnum:
1399 case MarkType::kEnumClass:
1400 this->dumpEnum(*def, globalsName);
1401 break;
1402 case MarkType::kTemplate:
1403 SkASSERT(0); // incomplete
1404 break;
1405 case MarkType::kTypedef: {
1406 this->writeTag("Typedef");
1407 this->writeSpace();
1408 TextParser parser(def);
1409 if (!parser.skipExact("typedef")) {
1410 return false;
1411 }
1412 if (!parser.skipSpace()) {
1413 return false;
1414 }
1415 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1416 this->lf(2);
1417 this->dumpComment(*def);
1418 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1419 this->lf(2);
1420 } continue;
1421 case MarkType::kUnion:
1422 SkASSERT(0); // incomplete
1423 break;
1424 default:
1425 SkASSERT(0);
1426 }
1427 this->dumpCommonTail(*def);
1428 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001429 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001430 this->writeEndTag("Topic", topicName);
1431 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001432// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001433 SkDebugf("wrote %s\n", fileName.c_str());
1434 return true;
1435}
1436
1437bool IncludeParser::isClone(const Definition& token) {
1438 string name = token.fName;
1439 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1440}
1441
1442bool IncludeParser::isConstructor(const Definition& token, string className) {
1443 string name = token.fName;
1444 return 0 == name.find(className) || '~' == name[0];
1445}
1446
1447bool IncludeParser::isInternalName(const Definition& token) {
1448 string name = token.fName;
1449 // exception for this SkCanvas function .. for now
1450 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1451 return false;
1452 }
1453 return name.substr(0, 7) == "android"
1454 || 0 == token.fName.find("internal_")
1455 || 0 == token.fName.find("Internal_")
1456 || 0 == token.fName.find("legacy_")
1457 || 0 == token.fName.find("temporary_")
1458 || 0 == token.fName.find("private_");
1459}
1460
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001461bool IncludeParser::isMember(const Definition& token) const {
1462 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1463 return true;
1464 }
1465 if (!islower(token.fStart[0])) {
1466 return false;
1467 }
1468 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1469 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1470 const Definition* structToken = token.fParent;
1471 if (!structToken) {
1472 return false;
1473 }
1474 if (KeyWord::kStruct != structToken->fKeyWord) {
1475 structToken = token.fParent->fParent;
1476 if (!structToken) {
1477 return false;
1478 }
1479 if (KeyWord::kStruct != structToken->fKeyWord) {
1480 return false;
1481 }
1482 }
1483 SkASSERT(structToken->fTokens.size() > 0);
1484 const Definition& child = structToken->fTokens.front();
1485 string structName(child.fContentStart, child.length());
1486 if ("RunBuffer" != structName) {
1487 return false;
1488 }
1489 string tokenName(token.fContentStart, token.length());
1490 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1491 for (auto allow : allowed) {
1492 if (allow == tokenName) {
1493 return true;
1494 }
1495 }
1496 }
1497 return false;
1498}
1499
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001500bool IncludeParser::isOperator(const Definition& token) {
1501 return "operator" == token.fName.substr(0, 8);
1502}
1503
1504void IncludeParser::dumpMethod(const Definition& token, string className) {
1505 this->writeTag("Method");
1506 this->writeSpace();
1507
1508 string name = string(token.fStart ? token.fStart : token.fContentStart,
1509 token.length());
1510 if (this->isOperator(token)) {
1511 string spaceConst(" const");
1512 size_t constPos = name.rfind(spaceConst);
1513 if (name.length() - spaceConst.length() == constPos) {
1514 name = name.substr(0, constPos) + "_const";
1515 }
1516 }
Cary Clark224c7002018-06-27 11:00:21 -04001517 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001518 string inType;
1519 if (this->isConstructor(token, className)) {
1520 inType = "Constructor";
1521 } else if (this->isOperator(token)) {
1522 inType = "Operator";
1523 } else {
1524 inType = "incomplete";
1525 }
1526 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001527 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001528 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001529 this->dumpComment(token);
1530}
1531
1532void IncludeParser::dumpMember(const Definition& token) {
1533 this->writeTag("Member");
1534 this->writeSpace();
1535 this->writeDefinition(token, token.fName, 2);
1536 lf(1);
1537 for (auto child : token.fChildren) {
1538 this->writeDefinition(*child);
1539 }
1540 this->writeEndTag();
1541 lf(2);
1542}
1543
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001544bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001545 string globalFileName;
1546 long int globalTell = 0;
1547 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001548 return false;
1549 }
Cary Clark9174bda2017-09-19 17:39:32 -04001550 for (const auto& member : fIClassMap) {
1551 if (string::npos != member.first.find("::")) {
1552 continue;
1553 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001554 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001555 return false;
1556 }
1557 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001558 if (globalTell) {
1559 fclose(fOut);
1560 }
Cary Clark9174bda2017-09-19 17:39:32 -04001561 return true;
1562}
1563
Ben Wagner63fd7602017-10-09 15:45:33 -04001564 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001565bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001566 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001567 if (globalFileName != fileName) {
1568 fOut = fopen(fileName.c_str(), "wb");
1569 if (!fOut) {
1570 SkDebugf("could not open output file %s\n", fileName.c_str());
1571 return false;
1572 }
1573 } else {
1574 fseek(fOut, *globalTell, SEEK_SET);
1575 this->lf(2);
1576 this->writeBlockSeparator();
1577 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001578 }
1579 string prefixName = skClassName.substr(0, 2);
1580 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1581 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001582 if (globalFileName != fileName) {
1583 this->writeTagNoLF("Topic", topicName);
1584 this->writeEndTag("Alias", topicName + "_Reference");
1585 this->lf(2);
1586 }
Cary Clark8032b982017-07-28 11:04:54 -04001587 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001588 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1589 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001590 this->writeTag(containerType, skClassName);
1591 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001592 auto& tokens = classMap.fTokens;
1593 for (auto& token : tokens) {
1594 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1595 continue;
1596 }
Cary Clark9174bda2017-09-19 17:39:32 -04001597 this->writeDefinition(token);
1598 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001599 }
1600 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001601 bool hasClass = false;
1602 bool hasConst = !fIEnumMap.empty();
1603 bool hasConstructor = false;
1604 bool hasMember = false;
1605 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001606 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001607 for (const auto& oneClass : fIClassMap) {
1608 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1609 continue;
1610 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001611 hasClass = true;
1612 break;
Cary Clark8032b982017-07-28 11:04:54 -04001613 }
Cary Clark224c7002018-06-27 11:00:21 -04001614 for (const auto& oneStruct : fIStructMap) {
1615 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1616 continue;
1617 }
1618 hasStruct = true;
1619 break;
1620 }
Cary Clark8032b982017-07-28 11:04:54 -04001621 for (const auto& token : classMap.fTokens) {
1622 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1623 continue;
1624 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001625 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001626 continue;
1627 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001628 if (this->isConstructor(token, skClassName)) {
1629 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001630 continue;
1631 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001632 if (this->isOperator(token)) {
1633 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001634 continue;
1635 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001636 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001637 continue;
1638 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001639 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001640 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001641 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001642 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001643 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001644 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001645
1646 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001647 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001648 this->writeTag("Populate");
1649 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001650 this->lf(2);
1651 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001652 if (hasConst) {
1653 this->writeTag("Subtopic", "Constant");
1654 this->writeTag("Populate");
1655 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001656 this->lf(2);
1657 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001658 if (hasConstructor) {
1659 this->writeTag("Subtopic", "Constructor");
1660 this->writeTag("Populate");
1661 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001662 this->lf(2);
1663 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001664 if (hasOperator) {
1665 this->writeTag("Subtopic", "Operator");
1666 this->writeTag("Populate");
1667 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001668 this->lf(2);
1669 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001670 if (hasMember) {
1671 this->writeTag("Subtopic", "Member_Function");
1672 this->writeTag("Populate");
1673 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001674 this->lf(2);
1675 }
Cary Clark224c7002018-06-27 11:00:21 -04001676 if (hasStruct) {
1677 this->writeTag("Subtopic", "Struct");
1678 this->writeTag("Populate");
1679 this->writeEndTag();
1680 this->lf(2);
1681 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001682 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001683 this->writeBlockSeparator();
1684 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001685 this->lf(2);
1686 this->writeTag("Example");
1687 this->lfcr();
1688 this->writeString("// incomplete");
1689 this->writeEndTag();
1690 this->lf(2);
1691 this->writeTag("SeeAlso", "incomplete");
1692 this->lf(2);
1693 this->writeEndTag("Enum", oneEnum.first);
1694 this->lf(2);
1695 }
Cary Clark8032b982017-07-28 11:04:54 -04001696 for (auto& oneClass : fIClassMap) {
1697 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1698 continue;
1699 }
1700 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001701 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001702 KeyWord keyword = oneClass.second.fKeyWord;
1703 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1704 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001705 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001706 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001707 this->lf(2);
1708 this->writeTag("Code");
1709 this->writeEndTag("ToDo", "fill this in manually");
1710 this->writeEndTag();
1711 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001712 for (auto& token : oneClass.second.fTokens) {
1713 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1714 continue;
1715 }
Cary Clark9174bda2017-09-19 17:39:32 -04001716 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001717 }
1718 this->lf(2);
1719 this->dumpClassTokens(oneClass.second);
1720 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001721 this->writeEndTag(containerType, innerName);
1722 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001723 }
1724 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001725 this->writeEndTag(containerType, skClassName);
1726 this->lf(2);
1727 this->writeEndTag("Topic", topicName);
1728 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001729 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001730 SkDebugf("wrote %s\n", fileName.c_str());
1731 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001732}
1733
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001734void IncludeParser::dumpTypedef(const Definition& token, string className) {
1735 this->writeTag("Typedef");
1736 this->writeSpace();
1737 this->writeString(token.fName);
1738 this->writeTagTable("Line", "incomplete");
1739 this->lf(2);
1740 this->dumpComment(token);
1741}
1742
Cary Clark61313f32018-10-08 14:57:48 -04001743string IncludeParser::elidedCodeBlock(const Definition& iDef) {
1744 SkASSERT(KeyWord::kStruct == iDef.fKeyWord || KeyWord::kClass == iDef.fKeyWord
1745 || KeyWord::kTemplate == iDef.fKeyWord);
1746 TextParser i(&iDef);
1747 fElided = Elided::kYes;
1748 MarkType markType = MarkType::kClass;
1749 if (KeyWord::kTemplate == iDef.fKeyWord) { // may be function
1750 for (auto child : iDef.fChildren) {
1751 if (MarkType::kMethod == child->fMarkType) {
1752 markType = MarkType::kFunction;
1753 break;
1754 }
1755 }
1756 }
1757 return this->writeCodeBlock(i, markType, 0);
1758}
1759
Cary Clarka90ea222018-10-16 10:30:28 -04001760 string IncludeParser::filteredBlock(string inContents, string filterContents) {
1761 string result;
1762 const unordered_map<string, Definition*>* mapPtr = nullptr;
1763 MarkType markType = MarkType::kNone;
1764 if ("Constant" == inContents) {
1765 mapPtr = &fIConstMap;
1766 markType = MarkType::kConst;
1767 } else {
1768 SkASSERT(0); // only Constant supported for now
1769 }
Cary Clark4b076532018-10-29 14:17:22 -04001770 vector<Definition*> consts;
Cary Clarka90ea222018-10-16 10:30:28 -04001771 for (auto entry : *mapPtr) {
1772 if (string::npos == entry.first.find(filterContents)) {
1773 continue;
1774 }
Cary Clark4b076532018-10-29 14:17:22 -04001775 consts.push_back(entry.second);
1776 }
1777 std::sort(consts.begin(), consts.end(), [](Definition* def1, Definition* def2) {
1778 return def1->fLineCount < def2->fLineCount;
1779 } );
1780 for (auto oneConst : consts) {
1781 result += this->writeCodeBlock(*oneConst, markType);
Cary Clarka90ea222018-10-16 10:30:28 -04001782 }
1783 return result;
1784}
1785
Cary Clark8032b982017-07-28 11:04:54 -04001786bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1787 // add comment preceding class, if any
1788 const Definition* parent = includeDef.fParent;
1789 int index = includeDef.fParentIndex;
1790 auto wordIter = parent->fTokens.begin();
1791 std::advance(wordIter, index);
1792 SkASSERT(&*wordIter == &includeDef);
1793 while (parent->fTokens.begin() != wordIter) {
1794 auto testIter = std::prev(wordIter);
1795 if (Definition::Type::kWord != testIter->fType
1796 && Definition::Type::kKeyWord != testIter->fType
1797 && (Definition::Type::kBracket != testIter->fType
1798 || Bracket::kAngle != testIter->fBracket)
1799 && (Definition::Type::kPunctuation != testIter->fType
1800 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1801 break;
1802 }
1803 wordIter = testIter;
1804 }
1805 auto commentIter = wordIter;
1806 while (parent->fTokens.begin() != commentIter) {
1807 auto testIter = std::prev(commentIter);
1808 bool isComment = Definition::Type::kBracket == testIter->fType
1809 && (Bracket::kSlashSlash == testIter->fBracket
1810 || Bracket::kSlashStar == testIter->fBracket);
1811 if (!isComment) {
1812 break;
1813 }
1814 commentIter = testIter;
1815 }
1816 while (commentIter != wordIter) {
1817 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1818 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1819 return false;
1820 }
1821 commentIter = std::next(commentIter);
1822 }
1823 return true;
1824}
1825
Cary Clark0d225392018-06-07 09:59:07 -04001826Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1827 string typeName) {
1828 typedef Definition* DefinitionPtr;
1829 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1830 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1831 if (mapIter == fMaps.end()) {
1832 return nullptr;
1833 }
1834 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1835 return reportError<DefinitionPtr>("invalid mark type");
1836 }
1837 string name = this->uniqueName(*mapIter->fInclude, typeName);
1838 Definition& markupDef = *(*mapIter->fInclude)[name];
1839 if (markupDef.fStart) {
1840 return reportError<DefinitionPtr>("definition already defined");
1841 }
1842 markupDef.fFileName = fFileName;
1843 markupDef.fStart = includeDef.fStart;
1844 markupDef.fContentStart = includeDef.fStart;
1845 markupDef.fName = name;
1846 markupDef.fContentEnd = includeDef.fContentEnd;
1847 markupDef.fTerminator = includeDef.fTerminator;
1848 markupDef.fParent = fParent;
1849 markupDef.fLineCount = includeDef.fLineCount;
1850 markupDef.fMarkType = markType;
1851 markupDef.fKeyWord = includeDef.fKeyWord;
1852 markupDef.fType = Definition::Type::kMark;
1853 return &markupDef;
1854}
1855
Cary Clarka64e4ee2018-10-18 08:30:34 -04001856Definition* IncludeParser::findMethod(const Definition& bmhDef) {
Cary Clark09d80c02018-10-31 12:14:03 -04001857 auto doubleColon = bmhDef.fName.rfind("::");
1858 if (string::npos == doubleColon) {
1859 const auto& iGlobalMethod = fIFunctionMap.find(bmhDef.fName);
1860 SkASSERT(fIFunctionMap.end() != iGlobalMethod);
1861 return iGlobalMethod->second;
1862 }
Cary Clarka64e4ee2018-10-18 08:30:34 -04001863 string className = bmhDef.fName.substr(0, doubleColon);
1864 const auto& iClass = fIClassMap.find(className);
1865 SkASSERT(fIClassMap.end() != iClass);
1866 string methodName = bmhDef.fName.substr(doubleColon + 2);
1867 auto& iTokens = iClass->second.fTokens;
1868 const auto& iMethod = std::find_if(iTokens.begin(), iTokens.end(),
1869 [methodName](Definition& token) {
Cary Clark09d80c02018-10-31 12:14:03 -04001870 return MarkType::kMethod == token.fMarkType
1871 && (methodName == token.fName
1872 || methodName == token.fName + "()"); } );
1873 if (iTokens.end() != iMethod) {
1874 return &*iMethod;
1875 }
1876 size_t subClassPos = className.rfind("::");
1877 if (string::npos != subClassPos) {
1878 className = className.substr(subClassPos + 2);
1879 }
1880 // match may be constructor; compare strings to see if this is so
1881 SkASSERT(string::npos != methodName.find('('));
1882 auto stripper = [](string s) -> string {
1883 bool last = false;
1884 string result;
1885 for (char c : s) {
1886 if (' ' >= c) {
1887 if (!last) {
1888 last = true;
1889 result += ' ';
1890 }
1891 continue;
1892 }
1893 result += c;
1894 last = false;
1895 }
1896 return result;
1897 };
1898 string strippedMethodName = stripper(methodName);
1899 if (strippedMethodName == methodName) {
1900 strippedMethodName = "";
1901 }
1902 const auto& cMethod = std::find_if(iTokens.begin(), iTokens.end(),
1903 [className, methodName, stripper, strippedMethodName](Definition& token) {
1904 if (MarkType::kMethod != token.fMarkType) {
1905 return false;
1906 }
1907 TextParser parser(&token);
1908 const char* match = parser.strnstr(className.c_str(), parser.fEnd);
1909 if (!match) {
1910 return false;
1911 }
1912 parser.skipTo(match);
1913 parser.skipExact(className.c_str());
1914 if ('(' != parser.peek()) {
1915 return false;
1916 }
1917 parser.skipToBalancedEndBracket('(', ')');
1918 string iMethodName(match, parser.fChar - match);
1919 if (methodName == iMethodName) {
1920 return true;
1921 }
1922 if ("" == strippedMethodName) {
1923 return false;
1924 }
1925 string strippedIName = stripper(iMethodName);
1926 return strippedIName == strippedMethodName;
1927 } );
1928 SkAssertResult(iTokens.end() != cMethod);
1929 return &*cMethod;
Cary Clarka64e4ee2018-10-18 08:30:34 -04001930}
1931
Cary Clark224c7002018-06-27 11:00:21 -04001932Definition* IncludeParser::parentBracket(Definition* parent) const {
1933 while (parent && Definition::Type::kBracket != parent->fType) {
1934 parent = parent->fParent;
1935 }
1936 return parent;
1937}
1938
1939Bracket IncludeParser::grandParentBracket() const {
1940 Definition* parent = parentBracket(fParent);
1941 parent = parentBracket(parent ? parent->fParent : nullptr);
1942 return parent ? parent->fBracket : Bracket::kNone;
1943}
1944
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001945bool IncludeParser::inAlignAs() const {
1946 if (fParent->fTokens.size() < 2) {
1947 return false;
1948 }
1949 auto reverseIter = fParent->fTokens.end();
1950 bool checkForBracket = true;
1951 while (fParent->fTokens.begin() != reverseIter) {
1952 std::advance(reverseIter, -1);
1953 if (checkForBracket) {
1954 if (Definition::Type::kBracket != reverseIter->fType) {
1955 return false;
1956 }
1957 if (Bracket::kParen != reverseIter->fBracket) {
1958 return false;
1959 }
1960 checkForBracket = false;
1961 continue;
1962 }
1963 if (Definition::Type::kKeyWord != reverseIter->fType) {
1964 return false;
1965 }
1966 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1967 }
1968 return false;
1969}
1970
Cary Clark61313f32018-10-08 14:57:48 -04001971const Definition* IncludeParser::include(string match) const {
1972 for (auto& entry : fIncludeMap) {
1973 if (string::npos == entry.first.find(match)) {
1974 continue;
1975 }
1976 return &entry.second;
1977 }
1978 SkASSERT(0);
1979 return nullptr;
1980}
1981
Cary Clark137b8742018-05-30 09:21:49 -04001982// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001983bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1984 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001985 // parse class header
1986 auto iter = includeDef->fTokens.begin();
1987 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1988 // todo : documentation is ignoring this for now
1989 iter = std::next(iter);
1990 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001991 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1992 if (hasAlignAs) {
1993 iter = std::next(iter);
1994 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1995 return includeDef->reportError<bool>("expected alignas argument");
1996 }
1997 iter = std::next(iter);
1998 }
Cary Clark8032b982017-07-28 11:04:54 -04001999 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
2000 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04002001 iter = std::next(iter);
2002 if (iter == includeDef->fTokens.end()) {
2003 return true; // forward declaration only
2004 }
Cary Clark8032b982017-07-28 11:04:54 -04002005 do {
2006 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002007 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04002008 }
2009 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
2010 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002011 }
Cary Clark8032b982017-07-28 11:04:54 -04002012 } while (static_cast<void>(iter = std::next(iter)), true);
2013 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04002014 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04002015 }
2016 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
2017 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04002018 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04002019 }
2020 markupDef->fStart = iter->fStart;
2021 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002022 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04002023 }
2024// if (1 != includeDef->fChildren.size()) {
2025// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
2026// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002027 auto includeDefIter = includeDef->fChildren.begin();
2028 if (hasAlignAs) {
2029 SkASSERT(includeDef->fChildren.end() != includeDefIter);
2030 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
2031 std::advance(includeDefIter, 1);
2032 }
2033 if (includeDef->fChildren.end() != includeDefIter
2034 && Bracket::kAngle == (*includeDefIter)->fBracket) {
2035 std::advance(includeDefIter, 1);
2036 }
2037 includeDef = *includeDefIter;
2038 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04002039 iter = includeDef->fTokens.begin();
2040 // skip until public
2041 int publicIndex = 0;
2042 if (IsStruct::kNo == isStruct) {
2043 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2044 size_t publicLen = strlen(publicName);
2045 while (iter != includeDef->fTokens.end()
2046 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
2047 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04002048 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002049 iter = std::next(iter);
2050 ++publicIndex;
2051 }
2052 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002053 int keyIndex = publicIndex;
2054 KeyWord currentKey = KeyWord::kPublic;
2055 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
2056 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04002057 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
2058 size_t protectedLen = strlen(protectedName);
2059 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
2060 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04002061 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002062 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04002063 std::advance(childIter, 1);
2064 }
Cary Clark884dd7d2017-10-11 10:37:52 -04002065 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04002066 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04002067 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04002068 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04002069 const char* testStart = iter->fStart;
2070 size_t testLen = (size_t) (iter->fContentEnd - testStart);
2071 iter = std::next(iter);
2072 ++keyIndex;
2073 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
2074 currentKey = KeyWord::kPublic;
2075 break;
2076 }
2077 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
2078 currentKey = KeyWord::kProtected;
2079 break;
2080 }
2081 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
2082 currentKey = KeyWord::kPrivate;
2083 break;
2084 }
2085 }
2086 fLastObject = nullptr;
2087 if (KeyWord::kPublic == currentKey) {
2088 if (!this->parseObject(child, markupDef)) {
2089 return false;
2090 }
Cary Clark8032b982017-07-28 11:04:54 -04002091 }
Cary Clark73fa9722017-08-29 17:36:51 -04002092 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002093 childIter = std::next(childIter);
2094 }
Cary Clark137b8742018-05-30 09:21:49 -04002095 while (iter != includeDef->fTokens.end()) {
2096 iter->fPrivate = KeyWord::kPublic != currentKey;
2097 iter = std::next(iter);
2098 }
Cary Clark8032b982017-07-28 11:04:54 -04002099 SkASSERT(fParent->fParent);
2100 fParent = fParent->fParent;
2101 return true;
2102}
2103
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002104bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04002105 int lineCount, Definition* markupDef) {
2106 TextParser parser(filename, start, end, lineCount);
2107 // parse doxygen if present
2108 if (parser.startsWith("**")) {
2109 parser.next();
2110 parser.next();
2111 parser.skipWhiteSpace();
2112 if ('\\' == parser.peek()) {
2113 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002114 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
2115 if (parser.skipExact("file")) {
2116 if (Definition::Type::kFileType != fParent->fType) {
2117 return reportError<bool>("expected parent is file");
2118 }
2119 string filename = markupDef->fileName();
2120 if (!parser.skipWord(filename.c_str())) {
2121 return reportError<bool>("missing object type");
2122 }
2123 } else if (parser.skipExact("fn")) {
2124 SkASSERT(0); // incomplete
2125 } else {
2126 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
2127 return reportError<bool>("missing object type");
2128 }
2129 if (!parser.skipWord(markupDef->fName.c_str()) &&
2130 KeyWord::kEnum != markupDef->fKeyWord) {
2131 return reportError<bool>("missing object name");
2132 }
Cary Clark8032b982017-07-28 11:04:54 -04002133 }
Cary Clark8032b982017-07-28 11:04:54 -04002134 }
2135 }
2136 // remove leading '*' if present
2137 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
2138 while (!parser.eof() && parser.skipWhiteSpace()) {
2139 while ('*' == parser.peek()) {
2140 parser.next();
2141 if (parser.eof()) {
2142 break;
2143 }
2144 parser.skipWhiteSpace();
2145 }
2146 if (parser.eof()) {
2147 break;
2148 }
2149 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04002150 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002151 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002152 parser.skipToEndBracket('\n');
2153 }
2154 return true;
2155}
2156
Cary Clarkd98f78c2018-04-26 08:32:37 -04002157bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04002158 if (!markupDef) {
2159 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2160 child->fLineCount, fParent, '\0');
2161 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04002162 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04002163 globalMarkupChild->fName = globalUniqueName;
2164 if (!this->findComments(*child, globalMarkupChild)) {
2165 return false;
2166 }
2167 fIConstMap[globalUniqueName] = globalMarkupChild;
2168 return true;
2169 }
2170 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
2171 child->fLineCount, markupDef, '\0');
2172 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04002173 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002174 markupChild->fTerminator = markupChild->fContentEnd;
2175 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04002176 classDef.fConsts[child->fName] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002177 fIConstMap[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002178 return true;
2179}
2180
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002181bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
2182 TextParser parser(child);
2183 if (!parser.skipExact("#define")) {
2184 return false;
2185 }
2186 if (!parser.skipSpace()) {
2187 return false;
2188 }
2189 const char* nameStart = parser.fChar;
2190 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
2191 if (parser.eof()) {
2192 return true; // do nothing if #define doesn't define anything
2193 }
2194 string nameStr(nameStart, parser.fChar - nameStart);
2195 struct Param {
2196 const char* fStart;
2197 const char* fEnd;
2198 };
2199 vector<Param> params;
2200 if ('(' == parser.peek()) {
2201 parser.next();
2202 if (!parser.skipSpace()) {
2203 return false;
2204 }
2205 do {
2206 const char* paramStart = parser.fChar;
2207 if (!parser.skipExact("...")) {
2208 parser.skipToNonAlphaNum();
2209 }
2210 if (parser.eof()) {
2211 return false;
2212 }
2213 params.push_back({paramStart, parser.fChar});
2214 if (!parser.skipSpace()) {
2215 return false;
2216 }
2217 if (')' == parser.peek()) {
2218 parser.next();
2219 break;
2220 }
2221 if (',' != parser.next()) {
2222 return false;
2223 }
2224 if (!parser.skipSpace()) {
2225 return false;
2226 }
2227 } while (true);
2228 }
2229 if (!parser.skipSpace()) {
2230 return false;
2231 }
2232 if (!markupDef) {
2233 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
2234 child->fLineCount, fParent, '\0');
2235 Definition* globalMarkupChild = &fGlobals.back();
2236 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
2237 globalMarkupChild->fName = globalUniqueName;
2238 globalMarkupChild->fTerminator = child->fContentEnd;
2239 if (!this->findComments(*child, globalMarkupChild)) {
2240 return false;
2241 }
2242 fIDefineMap[globalUniqueName] = globalMarkupChild;
2243 for (Param param : params) {
2244 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
2245 child->fLineCount, globalMarkupChild, '\0');
2246 Definition* paramChild = &globalMarkupChild->fTokens.back();
2247 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
2248 paramChild->fTerminator = param.fEnd;
2249 }
2250 return true;
2251 }
2252 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
2253 child->fLineCount, markupDef, '\0');
2254 Definition* markupChild = &markupDef->fTokens.back();
2255 markupChild->fName = nameStr;
2256 markupChild->fTerminator = markupChild->fContentEnd;
2257 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2258 if (!this->findComments(*child, markupChild)) {
2259 return false;
2260 }
2261 classDef.fDefines[nameStr] = markupChild;
Cary Clarka90ea222018-10-16 10:30:28 -04002262 fIDefineMap[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002263 return true;
2264}
2265
2266bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002267 TextParser parser(child);
2268 parser.skipToEndBracket('{');
2269 if (parser.eof()) {
2270 return true; // if enum is a forward declaration, do nothing
2271 }
2272 parser.next();
2273 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002274 if (child->fTokens.size() > 0) {
2275 auto token = child->fTokens.begin();
2276 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
2277 token = token->fTokens.begin();
2278 }
2279 if (Definition::Type::kWord == token->fType) {
2280 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
2281 }
2282 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002283 Definition* markupChild;
2284 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002285 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
2286 child->fLineCount, fParent, '\0');
2287 markupChild = &fGlobals.back();
2288 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
2289 markupChild->fName = globalUniqueName;
2290 markupChild->fTerminator = child->fContentEnd;
2291 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002292 } else {
2293 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002294 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05002295 markupChild = &markupDef->fTokens.back();
2296 }
Cary Clark8032b982017-07-28 11:04:54 -04002297 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
2298 markupChild->fKeyWord = KeyWord::kEnum;
2299 TextParser enumName(child);
2300 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05002301 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002302 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05002303 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04002304 markupChild->fMarkType = MarkType::kEnumClass;
2305 }
Cary Clark8032b982017-07-28 11:04:54 -04002306 const char* nameStart = enumName.fChar;
2307 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05002308 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04002309 markupChild->fName = markupDef->fName + "::" +
2310 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05002311 }
Cary Clark8032b982017-07-28 11:04:54 -04002312 if (!this->findComments(*child, markupChild)) {
2313 return false;
2314 }
Cary Clark8032b982017-07-28 11:04:54 -04002315 const char* dataEnd;
2316 do {
Cary Clark8032b982017-07-28 11:04:54 -04002317 parser.skipWhiteSpace();
2318 if ('}' == parser.peek()) {
2319 break;
2320 }
2321 Definition* comment = nullptr;
2322 // note that comment, if any, can be before or after (on the same line, though) as member
2323 if ('#' == parser.peek()) {
2324 // fixme: handle preprecessor, but just skip it for now
2325 parser.skipToLineStart();
2326 }
2327 while (parser.startsWith("/*") || parser.startsWith("//")) {
2328 parser.next();
2329 const char* start = parser.fChar;
2330 const char* end;
2331 if ('*' == parser.peek()) {
2332 end = parser.strnstr("*/", parser.fEnd);
2333 parser.fChar = end;
2334 parser.next();
2335 parser.next();
2336 } else {
2337 end = parser.trimmedLineEnd();
2338 parser.skipToLineStart();
2339 }
2340 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002341 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002342 comment = &markupChild->fTokens.back();
2343 comment->fTerminator = end;
2344 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
2345 return false;
2346 }
2347 parser.skipWhiteSpace();
2348 }
2349 parser.skipWhiteSpace();
2350 const char* memberStart = parser.fChar;
2351 if ('}' == memberStart[0]) {
2352 break;
2353 }
Cary Clark9174bda2017-09-19 17:39:32 -04002354 // if there's comment on same the line as member def, output first as if it was before
2355
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002356 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002357 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04002358 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04002359 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04002360 }
Cary Clark8032b982017-07-28 11:04:54 -04002361 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04002362 if ('=' == parser.peek()) {
2363 parser.skipToEndBracket(',');
2364 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002365 if (!parser.eof() && '#' == parser.peek()) {
2366 // fixme: handle preprecessor, but just skip it for now
2367 continue;
2368 }
Cary Clark9174bda2017-09-19 17:39:32 -04002369 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04002370 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04002371 }
2372 dataEnd = parser.fChar;
2373 const char* start = parser.anyOf("/\n");
2374 SkASSERT(start);
2375 parser.skipTo(start);
2376 if ('/' == parser.next()) {
2377 char slashStar = parser.next();
2378 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04002379 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04002380 char doxCheck = parser.next();
2381 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
2382 save.restore();
2383 }
2384 }
2385 parser.skipWhiteSpace();
2386 const char* commentStart = parser.fChar;
2387 if ('/' == slashStar) {
2388 parser.skipToEndBracket('\n');
2389 } else {
2390 parser.skipToEndBracket("*/");
2391 }
2392 SkASSERT(!parser.eof());
2393 const char* commentEnd = parser.fChar;
2394 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002395 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04002396 comment = &markupChild->fTokens.back();
2397 comment->fTerminator = commentEnd;
2398 }
Cary Clark8032b982017-07-28 11:04:54 -04002399 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002400 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002401 Definition* member = &markupChild->fTokens.back();
2402 member->fName = memberName;
2403 if (comment) {
2404 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04002405 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04002406 }
2407 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04002408 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002409 for (auto outsideMember : child->fChildren) {
2410 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002411 continue;
2412 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002413 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
2414 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04002415 continue;
2416 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002417 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
2418 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002419 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04002420 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002421 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04002422 // FIXME: ? add comment as well ?
2423 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04002424 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002425 if (markupDef) {
2426 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2427 SkASSERT(classDef.fStart);
2428 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
Cary Clark61313f32018-10-08 14:57:48 -04002429 string fullName = markupChild->fName;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002430 markupChild->fName = uniqueName;
2431 classDef.fEnums[uniqueName] = markupChild;
Cary Clark61313f32018-10-08 14:57:48 -04002432 fIEnumMap[fullName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05002433 }
Cary Clark8032b982017-07-28 11:04:54 -04002434 return true;
2435}
2436
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002437bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04002438 fParent = &fIncludeMap[name];
2439 fParent->fName = name;
2440 fParent->fFileName = fFileName;
2441 fParent->fType = Definition::Type::kFileType;
2442 fParent->fContentStart = fChar;
2443 fParent->fContentEnd = fEnd;
2444 // parse include file into tree
2445 while (fChar < fEnd) {
2446 if (!this->parseChar()) {
2447 return false;
2448 }
2449 }
2450 // parse tree and add named objects to maps
2451 fParent = &fIncludeMap[name];
2452 if (!this->parseObjects(fParent, nullptr)) {
2453 return false;
2454 }
2455 return true;
2456}
2457
2458bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
2459 const char* typeStart = child->fChildren[0]->fContentStart;
2460 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05002461 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002462 Definition* markupChild = &markupDef->fTokens.back();
2463 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002464 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002465 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
2466 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2467 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2468 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04002469 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04002470 classDef.fMembers[uniqueName] = markupChild;
2471 if (child->fParentIndex >= 2) {
2472 auto comment = child->fParent->fTokens.begin();
2473 std::advance(comment, child->fParentIndex - 2);
2474 if (Definition::Type::kBracket == comment->fType
2475 && (Bracket::kSlashStar == comment->fBracket
2476 || Bracket::kSlashSlash == comment->fBracket)) {
2477 TextParser parser(&*comment);
2478 do {
2479 parser.skipToAlpha();
2480 if (parser.eof()) {
2481 break;
2482 }
2483 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04002484 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04002485 if (Bracket::kSlashStar == comment->fBracket) {
2486 const char* commentEnd = parser.strnstr("*/", end);
2487 if (commentEnd) {
2488 end = commentEnd;
2489 }
2490 }
2491 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002492 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002493 Definition* commentChild = &markupDef->fTokens.back();
2494 markupChild->fChildren.emplace_back(commentChild);
2495 parser.skipTo(end);
2496 } while (!parser.eof());
2497 }
2498 }
2499 return true;
2500}
2501
2502bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
2503 auto tokenIter = child->fParent->fTokens.begin();
2504 std::advance(tokenIter, child->fParentIndex);
2505 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04002506 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05002507 bool addConst = false;
2508 auto operatorCheck = tokenIter;
2509 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
2510 operatorCheck = std::prev(tokenIter);
2511 }
2512 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04002513 auto closeParen = std::next(tokenIter);
2514 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
2515 '(' == closeParen->fContentStart[0]);
2516 nameEnd = closeParen->fContentEnd + 1;
2517 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04002518 if (Definition::Type::kKeyWord == closeParen->fType &&
2519 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002520 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04002521 }
Cary Clarka560c472017-11-27 10:44:06 -05002522 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04002523 }
2524 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05002525 if (addConst) {
2526 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04002527 }
Cary Clark8032b982017-07-28 11:04:54 -04002528 while (tokenIter != child->fParent->fTokens.begin()) {
2529 auto testIter = std::prev(tokenIter);
2530 switch (testIter->fType) {
2531 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04002532 if (testIter == child->fParent->fTokens.begin() &&
2533 (KeyWord::kIfdef == child->fParent->fKeyWord ||
2534 KeyWord::kIfndef == child->fParent->fKeyWord ||
2535 KeyWord::kIf == child->fParent->fKeyWord)) {
2536 std::next(tokenIter);
2537 break;
2538 }
Cary Clark8032b982017-07-28 11:04:54 -04002539 goto keepGoing;
2540 case Definition::Type::kKeyWord: {
2541 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2542 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2543 goto keepGoing;
2544 }
2545 } break;
2546 case Definition::Type::kBracket:
2547 if (Bracket::kAngle == testIter->fBracket) {
2548 goto keepGoing;
2549 }
2550 break;
2551 case Definition::Type::kPunctuation:
2552 if (Punctuation::kSemicolon == testIter->fPunctuation
2553 || Punctuation::kLeftBrace == testIter->fPunctuation
2554 || Punctuation::kColon == testIter->fPunctuation) {
2555 break;
2556 }
2557 keepGoing:
2558 tokenIter = testIter;
2559 continue;
2560 default:
2561 break;
2562 }
2563 break;
2564 }
Cary Clark224c7002018-06-27 11:00:21 -04002565 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002566 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark61313f32018-10-08 14:57:48 -04002567 tokenIter->fPrivate = string::npos != nameStr.find("::")
2568 && KeyWord::kTemplate != child->fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04002569 auto testIter = child->fParent->fTokens.begin();
2570 SkASSERT(child->fParentIndex > 0);
2571 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002572 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2573 0 == tokenIter->fParentIndex) {
2574 tokenIter = std::next(tokenIter);
2575 }
Cary Clark8032b982017-07-28 11:04:54 -04002576 const char* start = tokenIter->fContentStart;
2577 const char* end = tokenIter->fContentEnd;
2578 const char kDebugCodeStr[] = "SkDEBUGCODE";
2579 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2580 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2581 std::advance(testIter, 1);
2582 start = testIter->fContentStart + 1;
2583 end = testIter->fContentEnd - 1;
2584 } else {
2585 end = testIter->fContentEnd;
Cary Clark61313f32018-10-08 14:57:48 -04002586 do {
2587 std::advance(testIter, 1);
2588 if (testIter == child->fParent->fTokens.end()) {
2589 break;
2590 }
Cary Clark8032b982017-07-28 11:04:54 -04002591 switch (testIter->fType) {
2592 case Definition::Type::kPunctuation:
2593 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2594 || Punctuation::kLeftBrace == testIter->fPunctuation
2595 || Punctuation::kColon == testIter->fPunctuation);
2596 end = testIter->fStart;
2597 break;
2598 case Definition::Type::kKeyWord: {
2599 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2600 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2601 continue;
2602 }
2603 } break;
2604 default:
2605 continue;
2606 }
2607 break;
Cary Clark61313f32018-10-08 14:57:48 -04002608 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04002609 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002610 while (end > start && ' ' >= end[-1]) {
2611 --end;
2612 }
Cary Clark9174bda2017-09-19 17:39:32 -04002613 if (!markupDef) {
2614 auto parentIter = child->fParent->fTokens.begin();
2615 SkASSERT(child->fParentIndex > 0);
2616 std::advance(parentIter, child->fParentIndex - 1);
2617 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002618 TextParser nameParser(methodName);
2619 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002620 return true; // expect this is inline class definition outside of class
2621 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002622 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2623 fParent, '\0');
2624 Definition* globalMarkupChild = &fGlobals.back();
2625 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2626 globalMarkupChild->fName = globalUniqueName;
2627 if (!this->findComments(*child, globalMarkupChild)) {
2628 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002629 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002630 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002631 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002632 }
Cary Clark8032b982017-07-28 11:04:54 -04002633 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002634 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002635 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002636 {
2637 auto mapIter = fIClassMap.find(markupDef->fName);
2638 SkASSERT(fIClassMap.end() != mapIter);
2639 IClassDefinition& classDef = mapIter->second;
2640 SkASSERT(classDef.fStart);
2641 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2642 markupChild->fName = uniqueName;
2643 if (!this->findComments(*child, markupChild)) {
2644 return false;
2645 }
2646 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002647 }
Cary Clark8032b982017-07-28 11:04:54 -04002648 return true;
2649}
2650
Cary Clark8032b982017-07-28 11:04:54 -04002651bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002652 fPriorObject = nullptr;
2653 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002654 if (!this->parseObject(child, markupDef)) {
2655 return false;
2656 }
Cary Clark0d225392018-06-07 09:59:07 -04002657 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002658 }
2659 return true;
2660}
2661
2662bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2663 // set up for error reporting
2664 fLine = fChar = child->fStart;
2665 fEnd = child->fContentEnd;
2666 // todo: put original line number in child as well
2667 switch (child->fType) {
2668 case Definition::Type::kKeyWord:
2669 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002670 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002671 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002672 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002673 }
2674 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002675 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002676 case KeyWord::kConst:
2677 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002678 if (!this->parseConst(child, markupDef)) {
2679 return child->reportError<bool>("failed to parse const or constexpr");
2680 }
2681 break;
Cary Clark8032b982017-07-28 11:04:54 -04002682 case KeyWord::kEnum:
2683 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002684 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002685 }
2686 break;
2687 case KeyWord::kStruct:
2688 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002689 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002690 }
2691 break;
2692 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002693 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002694 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002695 }
2696 break;
2697 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002698 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002699 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002700 }
2701 break;
2702 case KeyWord::kUnion:
2703 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002704 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002705 }
2706 break;
Cary Clark61313f32018-10-08 14:57:48 -04002707 case KeyWord::kUsing:
2708 if (!this->parseUsing()) {
2709 return child->reportError<bool>("failed to parse using");
2710 }
2711 break;
Cary Clark8032b982017-07-28 11:04:54 -04002712 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002713 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002714 }
2715 break;
2716 case Definition::Type::kBracket:
2717 switch (child->fBracket) {
2718 case Bracket::kParen:
Cary Clark9174bda2017-09-19 17:39:32 -04002719 {
2720 auto tokenIter = child->fParent->fTokens.begin();
2721 std::advance(tokenIter, child->fParentIndex);
2722 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002723 TextParser previousToken(&*tokenIter);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002724 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002725 break;
2726 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002727 if (Bracket::kPound == child->fParent->fBracket &&
2728 KeyWord::kIf == child->fParent->fKeyWord) {
2729 // TODO: this will skip methods named defined() -- for the
2730 // moment there aren't any
2731 if (previousToken.startsWith("defined")) {
2732 break;
2733 }
2734 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002735 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2736 break;
2737 }
Cary Clark73fa9722017-08-29 17:36:51 -04002738 }
Cary Clark0d225392018-06-07 09:59:07 -04002739 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2740 break;
2741 }
Cary Clark8032b982017-07-28 11:04:54 -04002742 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002743 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002744 }
Cary Clark73fa9722017-08-29 17:36:51 -04002745 break;
Cary Clark8032b982017-07-28 11:04:54 -04002746 case Bracket::kSlashSlash:
2747 case Bracket::kSlashStar:
2748 // comments are picked up by parsing objects first
2749 break;
2750 case Bracket::kPound:
2751 // special-case the #xxx xxx_DEFINED entries
2752 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002753 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002754 case KeyWord::kIfndef:
2755 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002756 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002757 if (!this->parseObjects(child, markupDef)) {
2758 return false;
2759 }
2760 break;
2761 }
2762 goto preproError;
2763 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002764 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002765 break;
2766 }
2767 goto preproError;
2768 case KeyWord::kEndif:
2769 if (child->boilerplateEndIf()) {
2770 break;
2771 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002772 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002773 case KeyWord::kInclude:
2774 // ignored for now
2775 break;
2776 case KeyWord::kElse:
2777 case KeyWord::kElif:
2778 // todo: handle these
2779 break;
2780 default:
2781 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002782 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002783 }
2784 break;
2785 case Bracket::kAngle:
2786 // pick up templated function pieces when method is found
2787 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002788 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002789 if (!this->parseObjects(child, markupDef)) {
2790 return false;
2791 }
Cary Clark73fa9722017-08-29 17:36:51 -04002792 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002793 case Bracket::kSquare: {
2794 // check to see if parent is operator, the only case we handle so far
2795 auto prev = child->fParent->fTokens.begin();
2796 std::advance(prev, child->fParentIndex - 1);
2797 if (KeyWord::kOperator != prev->fKeyWord) {
2798 return child->reportError<bool>("expected operator overload");
2799 }
2800 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002801 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002802 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002803 }
2804 break;
2805 case Definition::Type::kWord:
2806 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002807 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002808 }
2809 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002810 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002811 }
2812 break;
2813 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002814 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002815 break;
2816 }
2817 return true;
2818}
2819
Cary Clarkbbfda252018-03-09 15:32:01 -05002820bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2821 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002822}
2823
Cary Clark2f466242017-12-11 16:03:17 -05002824bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2825 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002826 typedefParser.skipExact("typedef");
2827 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002828 string nameStr = typedefParser.typedefName();
2829 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002830 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2831 child->fLineCount, fParent, '\0');
2832 Definition* globalMarkupChild = &fGlobals.back();
2833 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2834 globalMarkupChild->fName = globalUniqueName;
2835 if (!this->findComments(*child, globalMarkupChild)) {
2836 return false;
2837 }
2838 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002839 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002840 return true;
2841 }
2842 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002843 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002844 Definition* markupChild = &markupDef->fTokens.back();
2845 markupChild->fName = nameStr;
2846 markupChild->fTerminator = markupChild->fContentEnd;
2847 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2848 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002849 child->fName = markupDef->fName + "::" + nameStr;
Cary Clarka90ea222018-10-16 10:30:28 -04002850 fITypedefMap[child->fName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002851 return true;
2852}
2853
2854bool IncludeParser::parseUnion() {
Cary Clark61313f32018-10-08 14:57:48 -04002855 // incomplete
2856 return true;
2857}
Cary Clark8032b982017-07-28 11:04:54 -04002858
Cary Clark61313f32018-10-08 14:57:48 -04002859bool IncludeParser::parseUsing() {
2860 // incomplete
Cary Clark8032b982017-07-28 11:04:54 -04002861 return true;
2862}
2863
2864bool IncludeParser::parseChar() {
2865 char test = *fChar;
2866 if ('\\' == fPrev) {
2867 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002868// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002869 fLine = fChar + 1;
2870 }
2871 goto done;
2872 }
2873 switch (test) {
2874 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002875// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002876 fLine = fChar + 1;
2877 if (fInChar) {
2878 return reportError<bool>("malformed char");
2879 }
2880 if (fInString) {
2881 return reportError<bool>("malformed string");
2882 }
2883 if (!this->checkForWord()) {
2884 return false;
2885 }
2886 if (Bracket::kPound == this->topBracket()) {
2887 KeyWord keyWord = fParent->fKeyWord;
2888 if (KeyWord::kNone == keyWord) {
2889 return this->reportError<bool>("unhandled preprocessor directive");
2890 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002891 if (fInDefine) {
2892 SkASSERT(KeyWord::kDefine == keyWord);
2893 fInDefine = false;
2894 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002895 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002896 this->popBracket();
2897 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002898 if (fInBrace) {
2899 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2900 fInBrace = nullptr;
2901 }
Cary Clark8032b982017-07-28 11:04:54 -04002902 } else if (Bracket::kSlashSlash == this->topBracket()) {
2903 this->popBracket();
2904 }
2905 break;
2906 case '*':
2907 if (!fInCharCommentString && '/' == fPrev) {
2908 this->pushBracket(Bracket::kSlashStar);
2909 }
2910 if (!this->checkForWord()) {
2911 return false;
2912 }
2913 if (!fInCharCommentString) {
2914 this->addPunctuation(Punctuation::kAsterisk);
2915 }
2916 break;
2917 case '/':
2918 if ('*' == fPrev) {
2919 if (!fInCharCommentString) {
2920 return reportError<bool>("malformed closing comment");
2921 }
2922 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002923 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002924 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002925 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002926 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002927 }
2928 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002929 }
Cary Clark8032b982017-07-28 11:04:54 -04002930 if (!fInCharCommentString && '/' == fPrev) {
2931 this->pushBracket(Bracket::kSlashSlash);
2932 break;
2933 }
2934 if (!this->checkForWord()) {
2935 return false;
2936 }
2937 break;
2938 case '\'':
2939 if (Bracket::kChar == this->topBracket()) {
2940 this->popBracket();
2941 } else if (!fInComment && !fInString) {
2942 if (fIncludeWord) {
2943 return this->reportError<bool>("word then single-quote");
2944 }
2945 this->pushBracket(Bracket::kChar);
2946 }
2947 break;
2948 case '\"':
2949 if (Bracket::kString == this->topBracket()) {
2950 this->popBracket();
2951 } else if (!fInComment && !fInChar) {
2952 if (fIncludeWord) {
2953 return this->reportError<bool>("word then double-quote");
2954 }
2955 this->pushBracket(Bracket::kString);
2956 }
2957 break;
Cary Clark8032b982017-07-28 11:04:54 -04002958 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002959 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002960 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2961 this->pushBracket(Bracket::kDebugCode);
2962 break;
2963 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002964 case ':':
2965 case '[':
2966 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002967 if (fInCharCommentString) {
2968 break;
2969 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002970 if (fInDefine && fInBrace) {
2971 break;
2972 }
Cary Clark8032b982017-07-28 11:04:54 -04002973 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2974 break;
2975 }
Cary Clark0d225392018-06-07 09:59:07 -04002976 if (fConstExpr) {
2977 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2978 fConstExpr = nullptr;
2979 }
Cary Clark8032b982017-07-28 11:04:54 -04002980 if (!fInBrace) {
2981 if (!this->checkForWord()) {
2982 return false;
2983 }
2984 if (':' == test && !fInFunction) {
2985 break;
2986 }
2987 if ('{' == test) {
2988 this->addPunctuation(Punctuation::kLeftBrace);
2989 } else if (':' == test) {
2990 this->addPunctuation(Punctuation::kColon);
2991 }
2992 }
2993 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2994 && Bracket::kColon == fInBrace->fBracket) {
2995 Definition* braceParent = fParent->fParent;
2996 braceParent->fChildren.pop_back();
2997 braceParent->fTokens.pop_back();
2998 fParent = braceParent;
2999 fInBrace = nullptr;
3000 }
3001 this->pushBracket(
3002 '(' == test ? Bracket::kParen :
3003 '[' == test ? Bracket::kSquare :
3004 '{' == test ? Bracket::kBrace :
3005 Bracket::kColon);
3006 if (!fInBrace
3007 && ('{' == test || (':' == test && ' ' >= fChar[1]))
3008 && fInFunction) {
3009 fInBrace = fParent;
3010 }
3011 } break;
3012 case '<':
3013 if (fInCharCommentString || fInBrace) {
3014 break;
3015 }
3016 if (!this->checkForWord()) {
3017 return false;
3018 }
3019 if (fInEnum) {
3020 break;
3021 }
3022 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04003023 // this angle bracket may be an operator or may be a bracket
3024 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04003025 break;
3026 case ')':
3027 case ']':
3028 case '}': {
3029 if (fInCharCommentString) {
3030 break;
3031 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003032 if (fInDefine && fInBrace) {
3033 break;
3034 }
Cary Clark8032b982017-07-28 11:04:54 -04003035 if (!fInBrace) {
3036 if (!this->checkForWord()) {
3037 return false;
3038 }
3039 }
3040 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04003041 Bracket match = ')' == test ? Bracket::kParen :
3042 ']' == test ? Bracket::kSquare : Bracket::kBrace;
3043 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04003044 this->popBracket();
3045 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003046 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04003047 } else {
3048 fInFunction = '}' != test;
3049 }
Cary Clark73fa9722017-08-29 17:36:51 -04003050 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
3051 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04003052 } else if (Bracket::kAngle == this->topBracket()
3053 && match == this->grandParentBracket()) {
3054 this->popBracket();
3055 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04003056 } else {
3057 return reportError<bool>("malformed close bracket");
3058 }
3059 if (popBraceParent) {
3060 Definition* braceParent = fInBrace->fParent;
3061 braceParent->fChildren.pop_back();
3062 braceParent->fTokens.pop_back();
3063 fInBrace = nullptr;
3064 }
3065 } break;
3066 case '>':
3067 if (fInCharCommentString || fInBrace) {
3068 break;
3069 }
3070 if (!this->checkForWord()) {
3071 return false;
3072 }
3073 if (fInEnum) {
3074 break;
3075 }
Cary Clarka560c472017-11-27 10:44:06 -05003076 if (Bracket::kPound == this->topBracket()) {
3077 break;
3078 }
Cary Clark8032b982017-07-28 11:04:54 -04003079 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04003080 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04003081 this->popBracket();
3082 } else {
3083 return reportError<bool>("malformed close angle bracket");
3084 }
3085 break;
3086 case '#': {
3087 if (fInCharCommentString || fInBrace) {
3088 break;
3089 }
3090 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
3091 this->pushBracket(Bracket::kPound);
3092 break;
3093 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003094 case ' ':
3095 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
3096 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
3097 fInBrace = fParent;
3098 // delimiting brackets are space ... unescaped-linefeed
3099 }
Cary Clark8032b982017-07-28 11:04:54 -04003100 case '&':
3101 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05003102 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05003103 case '-':
3104 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04003105 if (fInCharCommentString || fInBrace) {
3106 break;
3107 }
3108 if (!this->checkForWord()) {
3109 return false;
3110 }
3111 break;
Cary Clark0d225392018-06-07 09:59:07 -04003112 case '=':
3113 if (fInCharCommentString || fInBrace) {
3114 break;
3115 }
3116 if (!this->checkForWord()) {
3117 return false;
3118 }
Cary Clarkd7895502018-07-18 15:10:08 -04003119 if (!fParent->fTokens.size()) {
3120 break;
3121 }
Cary Clark0d225392018-06-07 09:59:07 -04003122 {
3123 const Definition& lastToken = fParent->fTokens.back();
3124 if (lastToken.fType != Definition::Type::kWord) {
3125 break;
3126 }
3127 string name(lastToken.fContentStart, lastToken.length());
3128 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
3129 break;
3130 }
3131 // find token on start of line
3132 auto lineIter = fParent->fTokens.end();
3133 do {
Cary Clark80247e52018-07-11 16:18:41 -04003134 if (fParent->fTokens.begin() == lineIter) {
3135 break;
3136 }
Cary Clark0d225392018-06-07 09:59:07 -04003137 --lineIter;
3138 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04003139 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04003140 ++lineIter;
3141 }
3142 Definition* lineStart = &*lineIter;
3143 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
3144 bool sawConst = false;
3145 bool sawStatic = false;
3146 bool sawTemplate = false;
3147 bool sawType = false;
3148 while (&lastToken != &*lineIter) {
3149 if (KeyWord::kTemplate == lineIter->fKeyWord) {
3150 if (sawConst || sawStatic || sawTemplate) {
3151 sawConst = false;
3152 break;
3153 }
3154 if (&lastToken == &*++lineIter) {
3155 break;
3156 }
3157 if (KeyWord::kTypename != lineIter->fKeyWord) {
3158 break;
3159 }
3160 if (&lastToken == &*++lineIter) {
3161 break;
3162 }
3163 if (Definition::Type::kWord != lineIter->fType) {
3164 break;
3165 }
3166 sawTemplate = true;
3167 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
3168 if (sawConst || sawStatic) {
3169 sawConst = false;
3170 break;
3171 }
3172 sawStatic = true;
3173 } else if (KeyWord::kConst == lineIter->fKeyWord
3174 || KeyWord::kConstExpr == lineIter->fKeyWord) {
3175 if (sawConst) {
3176 sawConst = false;
3177 break;
3178 }
3179 sawConst = true;
3180 } else {
3181 if (sawType) {
3182 sawType = false;
3183 break;
3184 }
3185 if (Definition::Type::kKeyWord == lineIter->fType
3186 && KeyProperty::kNumber
3187 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
3188 sawType = true;
3189 } else if (Definition::Type::kWord == lineIter->fType) {
3190 string typeName(lineIter->fContentStart, lineIter->length());
3191 if ("Sk" != name.substr(0, 2)) {
3192 sawType = true;
3193 }
3194 }
3195 }
3196 ++lineIter;
3197 }
3198 if (sawType && sawConst) {
3199 // if found, name first
3200 lineStart->fName = name;
3201 lineStart->fMarkType = MarkType::kConst;
3202 fParent->fChildren.emplace_back(lineStart);
3203 fConstExpr = lineStart;
3204 }
3205 }
3206 break;
Cary Clark8032b982017-07-28 11:04:54 -04003207 case ';':
3208 if (fInCharCommentString || fInBrace) {
3209 break;
3210 }
3211 if (!this->checkForWord()) {
3212 return false;
3213 }
Cary Clark0d225392018-06-07 09:59:07 -04003214 if (fConstExpr) {
3215 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
3216 fConstExpr = nullptr;
3217 }
Cary Clark8032b982017-07-28 11:04:54 -04003218 if (Definition::Type::kKeyWord == fParent->fType
3219 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05003220 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
3221 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04003222 KeyWord::kEnum == fParent->fParent->fKeyWord) {
3223 this->popObject();
3224 }
Cary Clark8032b982017-07-28 11:04:54 -04003225 if (KeyWord::kEnum == fParent->fKeyWord) {
3226 fInEnum = false;
3227 }
Cary Clark61313f32018-10-08 14:57:48 -04003228 parentIsClass |= KeyWord::kStruct == fParent->fKeyWord;
Cary Clark8032b982017-07-28 11:04:54 -04003229 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05003230 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
3231 this->popObject();
3232 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003233 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003234 } else if (Definition::Type::kBracket == fParent->fType
3235 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
3236 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
3237 list<Definition>::iterator baseIter = fParent->fTokens.end();
3238 list<Definition>::iterator namedIter = fParent->fTokens.end();
3239 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04003240 fParent->fTokens.begin() != tokenIter; ) {
3241 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04003242 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04003243 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04003244 if (namedIter != fParent->fTokens.end()) {
3245 return reportError<bool>("found two named member tokens");
3246 }
3247 namedIter = tokenIter;
3248 }
3249 baseIter = tokenIter;
3250 } else {
3251 break;
3252 }
3253 }
3254 // FIXME: if a member definition spans multiple lines, this won't work
3255 if (namedIter != fParent->fTokens.end()) {
3256 if (baseIter == namedIter) {
3257 return this->reportError<bool>("expected type before named token");
3258 }
3259 Definition* member = &*namedIter;
3260 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04003261 if (!member->fTerminator) {
3262 member->fTerminator = member->fContentEnd;
3263 }
Cary Clark8032b982017-07-28 11:04:54 -04003264 fParent->fChildren.push_back(member);
3265 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
3266 member->fChildren.push_back(&*nameType);
3267 }
Cary Clark8032b982017-07-28 11:04:54 -04003268 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003269 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04003270 } else if (fParent->fChildren.size() > 0) {
3271 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003272 Definition* priorEnum = fPriorEnum;
3273 fPriorEnum = nullptr;
3274 if (!priorEnum) {
3275 while (fParent->fChildren.begin() != lastIter) {
3276 std::advance(lastIter, -1);
3277 priorEnum = *lastIter;
3278 if (Definition::Type::kBracket != priorEnum->fType ||
3279 (Bracket::kSlashSlash != priorEnum->fBracket
3280 && Bracket::kSlashStar != priorEnum->fBracket)) {
3281 break;
3282 }
Cary Clark8032b982017-07-28 11:04:54 -04003283 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003284 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003285 }
3286 if (Definition::Type::kKeyWord == priorEnum->fType
3287 && KeyWord::kEnum == priorEnum->fKeyWord) {
3288 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05003289 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04003290 while (tokenWalker != fParent->fTokens.end()) {
3291 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003292 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04003293 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
3294 break;
3295 }
3296 }
3297 while (tokenWalker != fParent->fTokens.end()) {
3298 std::advance(tokenWalker, 1);
3299 const Definition* test = &*tokenWalker;
3300 if (Definition::Type::kBracket != test->fType ||
3301 (Bracket::kSlashSlash != test->fBracket
3302 && Bracket::kSlashStar != test->fBracket)) {
3303 break;
3304 }
3305 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003306 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04003307 Definition* start = &*tokenWalker;
3308 bool foundExpected = true;
3309 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
3310 const Definition* test = &*tokenWalker;
3311 if (expected != test->fKeyWord) {
3312 foundExpected = false;
3313 break;
3314 }
3315 if (tokenWalker == fParent->fTokens.end()) {
3316 break;
3317 }
3318 std::advance(tokenWalker, 1);
3319 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05003320 if (!foundExpected) {
3321 foundExpected = true;
3322 tokenWalker = saveTokenWalker;
3323 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
3324 const Definition* test = &*tokenWalker;
3325 if (expected != test->fKeyWord) {
3326 foundExpected = false;
3327 break;
3328 }
3329 if (tokenWalker == fParent->fTokens.end()) {
3330 break;
3331 }
3332 if (KeyWord::kNone != expected) {
3333 std::advance(tokenWalker, 1);
3334 }
3335 }
3336 if (foundExpected) {
3337 auto nameToken = priorEnum->fTokens.begin();
3338 string enumName = string(nameToken->fContentStart,
3339 nameToken->fContentEnd - nameToken->fContentStart);
3340 const Definition* test = &*tokenWalker;
3341 string constType = string(test->fContentStart,
3342 test->fContentEnd - test->fContentStart);
3343 if (enumName != constType) {
3344 foundExpected = false;
3345 } else {
3346 std::advance(tokenWalker, 1);
3347 }
3348 }
3349 }
Cary Clark8032b982017-07-28 11:04:54 -04003350 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
3351 const char* nameStart = tokenWalker->fStart;
3352 std::advance(tokenWalker, 1);
3353 if (tokenWalker != fParent->fTokens.end()) {
3354 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04003355 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04003356 start->fName = string(nameStart, tp.fChar - nameStart);
3357 start->fContentEnd = fChar;
3358 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05003359 fPriorEnum = priorEnum;
3360 }
Cary Clark8032b982017-07-28 11:04:54 -04003361 }
3362 }
3363 }
3364 this->addPunctuation(Punctuation::kSemicolon);
3365 fInFunction = false;
3366 break;
3367 case '~':
3368 if (fInEnum) {
3369 break;
3370 }
3371 case '0': case '1': case '2': case '3': case '4':
3372 case '5': case '6': case '7': case '8': case '9':
3373 // TODO: don't want to parse numbers, but do need to track for enum defs
3374 // break;
3375 case 'A': case 'B': case 'C': case 'D': case 'E':
3376 case 'F': case 'G': case 'H': case 'I': case 'J':
3377 case 'K': case 'L': case 'M': case 'N': case 'O':
3378 case 'P': case 'Q': case 'R': case 'S': case 'T':
3379 case 'U': case 'V': case 'W': case 'X': case 'Y':
3380 case 'Z': case '_':
3381 case 'a': case 'b': case 'c': case 'd': case 'e':
3382 case 'f': case 'g': case 'h': case 'i': case 'j':
3383 case 'k': case 'l': case 'm': case 'n': case 'o':
3384 case 'p': case 'q': case 'r': case 's': case 't':
3385 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04003386 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04003387 if (fInCharCommentString || fInBrace) {
3388 break;
3389 }
3390 if (!fIncludeWord) {
3391 fIncludeWord = fChar;
3392 }
3393 break;
3394 }
3395done:
3396 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04003397 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04003398 return true;
3399}
3400
3401void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04003402 IncludeParser::ValidateKeyWords();
3403}
Cary Clark2dc84ad2018-01-26 12:56:22 -05003404
Cary Clark186d08f2018-04-03 08:43:27 -04003405bool IncludeParser::references(const SkString& file) const {
3406 // if includes weren't passed one at a time, assume all references are valid
3407 if (fIncludeMap.empty()) {
3408 return true;
3409 }
3410 SkASSERT(file.endsWith(".bmh") );
3411 string root(file.c_str(), file.size() - 4);
3412 string kReference("_Reference");
3413 if (string::npos != root.find(kReference)) {
3414 root = root.substr(0, root.length() - kReference.length());
3415 }
3416 if (fIClassMap.end() != fIClassMap.find(root)) {
3417 return true;
3418 }
3419 if (fIStructMap.end() != fIStructMap.find(root)) {
3420 return true;
3421 }
Cary Clark224c7002018-06-27 11:00:21 -04003422 if (fIEnumMap.end() != fIEnumMap.find(root)) {
3423 return true;
3424 }
Cary Clarka90ea222018-10-16 10:30:28 -04003425 if (fITypedefMap.end() != fITypedefMap.find(root)) {
3426 return true;
3427 }
Cary Clark224c7002018-06-27 11:00:21 -04003428 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
3429 return true;
3430 }
Cary Clark186d08f2018-04-03 08:43:27 -04003431 return false;
3432}
3433
Cary Clark2dc84ad2018-01-26 12:56:22 -05003434void IncludeParser::RemoveFile(const char* docs, const char* includes) {
3435 if (!sk_isdir(includes)) {
3436 IncludeParser::RemoveOneFile(docs, includes);
3437 } else {
3438 SkOSFile::Iter it(includes, ".h");
3439 for (SkString file; it.next(&file); ) {
3440 SkString p = SkOSPath::Join(includes, file.c_str());
3441 const char* hunk = p.c_str();
3442 if (!SkStrEndsWith(hunk, ".h")) {
3443 continue;
3444 }
3445 IncludeParser::RemoveOneFile(docs, hunk);
3446 }
3447 }
3448}
3449
3450void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
3451 const char* lastForward = strrchr(includesFile, '/');
3452 const char* lastBackward = strrchr(includesFile, '\\');
3453 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
3454 if (!last) {
3455 last = includesFile;
3456 } else {
3457 last += 1;
3458 }
3459 SkString baseName(last);
3460 SkASSERT(baseName.endsWith(".h"));
3461 baseName.remove(baseName.size() - 2, 2);
3462 baseName.append("_Reference.bmh");
3463 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
3464 remove(fullName.c_str());
3465}
Cary Clark224c7002018-06-27 11:00:21 -04003466
3467Bracket IncludeParser::topBracket() const {
3468 Definition* parent = this->parentBracket(fParent);
3469 return parent ? parent->fBracket : Bracket::kNone;
3470}