blob: 26281b6184535047b3cbb84b23103e7e8ad83ed4 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
9
Cary Clark8032b982017-07-28 11:04:54 -040010const IncludeKey kKeyWords[] = {
11 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040012 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040013 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040014 { "bool", KeyWord::kBool, KeyProperty::kNumber },
15 { "char", KeyWord::kChar, KeyProperty::kNumber },
16 { "class", KeyWord::kClass, KeyProperty::kObject },
17 { "const", KeyWord::kConst, KeyProperty::kModifier },
18 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
19 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
20 { "double", KeyWord::kDouble, KeyProperty::kNumber },
21 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
22 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
23 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
24 { "enum", KeyWord::kEnum, KeyProperty::kObject },
25 { "float", KeyWord::kFloat, KeyProperty::kNumber },
26 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
27 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
28 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
29 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
30 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
31 { "inline", KeyWord::kInline, KeyProperty::kModifier },
32 { "int", KeyWord::kInt, KeyProperty::kNumber },
33 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
34 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
35 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
36 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
37 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
38 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
39 { "static", KeyWord::kStatic, KeyProperty::kModifier },
40 { "struct", KeyWord::kStruct, KeyProperty::kObject },
41 { "template", KeyWord::kTemplate, KeyProperty::kObject },
42 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040043 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040044 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040045 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
46 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040047 { "union", KeyWord::kUnion, KeyProperty::kObject },
48 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
49 { "void", KeyWord::kVoid, KeyProperty::kNumber },
50};
51
52const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
53
54KeyWord IncludeParser::FindKey(const char* start, const char* end) {
55 int ch = 0;
56 for (size_t index = 0; index < kKeyWordCount; ) {
57 if (start[ch] > kKeyWords[index].fName[ch]) {
58 ++index;
59 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
60 return KeyWord::kNone;
61 }
62 continue;
63 }
64 if (start[ch] < kKeyWords[index].fName[ch]) {
65 return KeyWord::kNone;
66 }
67 ++ch;
68 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040069 if (end - start < (int) strlen(kKeyWords[index].fName)) {
70 return KeyWord::kNone;
71 }
Cary Clark8032b982017-07-28 11:04:54 -040072 return kKeyWords[index].fKeyWord;
73 }
74 }
75 return KeyWord::kNone;
76}
77
78void IncludeParser::ValidateKeyWords() {
79 for (size_t index = 1; index < kKeyWordCount; ++index) {
80 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
81 == (int) kKeyWords[index].fKeyWord);
82 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
83 }
84}
85
86void IncludeParser::addKeyword(KeyWord keyWord) {
87 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent);
88 fIncludeWord = nullptr;
89 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
90 Definition* def = &fParent->fTokens.back();
91 this->addDefinition(def);
92 if (KeyWord::kEnum == fParent->fKeyWord) {
93 fInEnum = true;
94 }
95 }
96}
97
Ben Wagner63fd7602017-10-09 15:45:33 -040098void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -040099 const vector<string>& foundParams) {
100 for (auto& methodParam : methodParams) {
101 bool found = false;
102 for (auto& foundParam : foundParams) {
103 if (methodParam == foundParam) {
104 found = true;
105 break;
106 }
107 }
108 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400109 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400110 }
111 }
112 for (auto& foundParam : foundParams) {
113 bool found = false;
114 for (auto& methodParam : methodParams) {
115 if (methodParam == foundParam) {
116 found = true;
117 break;
118 }
119 }
120 if (!found) {
121 this->reportError("doxygen param does not match method declaration");
122 }
123 }
124}
125
126bool IncludeParser::checkForWord() {
127 if (!fIncludeWord) {
128 return true;
129 }
130 KeyWord keyWord = FindKey(fIncludeWord, fChar);
131 if (KeyWord::kNone != keyWord) {
132 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
133 this->addKeyword(keyWord);
134 return true;
135 }
136 } else {
137 this->addWord();
138 return true;
139 }
140 Definition* poundDef = fParent;
141 if (!fParent) {
142 return reportError<bool>("expected parent");
143 }
144 if (Definition::Type::kBracket != poundDef->fType) {
145 return reportError<bool>("expected bracket");
146 }
147 if (Bracket::kPound != poundDef->fBracket) {
148 return reportError<bool>("expected preprocessor");
149 }
150 if (KeyWord::kNone != poundDef->fKeyWord) {
151 return reportError<bool>("already found keyword");
152 }
153 poundDef->fKeyWord = keyWord;
154 fIncludeWord = nullptr;
155 switch (keyWord) {
156 // these do not link to other # directives
157 case KeyWord::kDefine:
158 case KeyWord::kInclude:
159 break;
160 // these start a # directive link
161 case KeyWord::kIf:
162 case KeyWord::kIfdef:
163 case KeyWord::kIfndef:
164 break;
165 // these continue a # directive link
166 case KeyWord::kElif:
167 case KeyWord::kElse: {
168 this->popObject(); // pop elif
169 if (Bracket::kPound != fParent->fBracket) {
170 return this->reportError<bool>("expected preprocessor directive");
171 }
172 this->popBracket(); // pop if
173 poundDef->fParent = fParent;
174 this->addDefinition(poundDef); // push elif back
175 } break;
176 // this ends a # directive link
177 case KeyWord::kEndif:
178 // FIXME : should this be calling popBracket() instead?
179 this->popObject(); // pop endif
180 if (Bracket::kPound != fParent->fBracket) {
181 return this->reportError<bool>("expected preprocessor directive");
182 }
183 this->popBracket(); // pop if/else
184 break;
185 default:
186 SkASSERT(0);
187 }
188 return true;
189}
190
191string IncludeParser::className() const {
192 string name(fParent->fName);
193 size_t slash = name.find_last_of("/");
194 if (string::npos == slash) {
195 slash = name.find_last_of("\\");
196 }
197 SkASSERT(string::npos != slash);
198 string result = name.substr(slash);
199 result = result.substr(1, result.size() - 3);
200 return result;
201}
202
Cary Clark884dd7d2017-10-11 10:37:52 -0400203#include <sstream>
204#include <iostream>
205
Cary Clark8032b982017-07-28 11:04:54 -0400206bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400207 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400208 string className = classMapper.first;
209 auto finder = bmhParser.fClassMap.find(className);
210 if (bmhParser.fClassMap.end() == finder) {
211 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400212 continue;
213 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400214 RootDefinition* root = &finder->second;
215 root->clearVisited();
216 }
217 for (auto& classMapper : fIClassMap) {
218 string className = classMapper.first;
219 std::istringstream iss(className);
220 string classStr;
221 string classBase;
222 RootDefinition* root = nullptr;
223 while (std::getline(iss, classStr, ':')) {
224 if (root) {
225 if (!classStr.length()) {
226 continue;
227 }
228 classBase += "::" + classStr;
229 auto finder = root->fBranches.find(classBase);
230 if (root->fBranches.end() != finder) {
231 root = finder->second;
232 } else {
233 SkASSERT(0);
234 }
235 } else {
236 classBase = classStr;
237 auto finder = bmhParser.fClassMap.find(classBase);
238 if (bmhParser.fClassMap.end() != finder) {
239 root = &finder->second;
240 } else {
241 SkASSERT(0);
242 }
243 }
244 }
Cary Clark8032b982017-07-28 11:04:54 -0400245 auto& classMap = classMapper.second;
246 auto& tokens = classMap.fTokens;
247 for (const auto& token : tokens) {
248 if (token.fPrivate) {
249 continue;
250 }
251 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400252 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400253 switch (token.fMarkType) {
254 case MarkType::kMethod: {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400255 if (this->internalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400256 continue;
257 }
Cary Clark8032b982017-07-28 11:04:54 -0400258 if (!def) {
259 string paramName = className + "::";
260 paramName += string(token.fContentStart,
261 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400262 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400263 if (!def && 0 == token.fName.find("operator")) {
264 string operatorName = className + "::";
265 TextParser oper("", token.fStart, token.fContentEnd, 0);
266 const char* start = oper.strnstr("operator", token.fContentEnd);
267 SkASSERT(start);
268 oper.skipTo(start);
269 oper.skipToEndBracket('(');
270 int parens = 0;
271 do {
272 if ('(' == oper.peek()) {
273 ++parens;
274 } else if (')' == oper.peek()) {
275 --parens;
276 }
277 } while (!oper.eof() && oper.next() && parens > 0);
278 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400279 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400280 }
281 }
282 if (!def) {
283 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
284 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
285 string constructorName = className + "::";
286 constructorName += string(token.fContentStart + skip,
287 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400288 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400289 }
290 if (!def && 0 == token.fName.find("SK_")) {
291 string incName = token.fName + "()";
292 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400293 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400294 if (def) {
295 if (def->fName == incName) {
296 def->fVisited = true;
297 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400298 def = root->find(className + "::toString",
299 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400300 if (def) {
301 def->fVisited = true;
302 } else {
303 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500304 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400305 }
306 }
307 break;
308 } else {
309 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500310 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400311 }
312 }
313 }
314 if (!def) {
315 bool allLower = true;
316 for (size_t index = 0; index < token.fName.length(); ++index) {
317 if (!islower(token.fName[index])) {
318 allLower = false;
319 break;
320 }
321 }
322 if (allLower) {
323 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400324 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400325 }
326 }
327 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400328 if ("SK_ATTR_DEPRECATED" == token.fName) {
329 break;
330 }
331 if (0 == token.fName.find("SkDEBUGCODE")) {
332 break;
333 }
334 }
335 if (!def) {
336 // simple method names inside nested classes have a bug and are missing trailing parens
337 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400338 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400339 }
340 if (!def) {
Cary Clark8032b982017-07-28 11:04:54 -0400341 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500342 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400343 break;
344 }
Cary Clark73fa9722017-08-29 17:36:51 -0400345 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400346 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400347 if (MarkType::kDefinedBy == def->fMarkType) {
348 def->fParent->fVisited = true;
349 }
Cary Clark8032b982017-07-28 11:04:54 -0400350 } else {
351 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500352 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400353 }
354 } break;
355 case MarkType::kComment:
356 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400357 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400358 case MarkType::kEnum: {
359 if (!def) {
360 // work backwards from first word to deduce #Enum name
361 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
362 SkAssertResult(firstMember.skipName("enum"));
363 SkAssertResult(firstMember.skipToEndBracket('{'));
364 firstMember.next();
365 firstMember.skipWhiteSpace();
366 SkASSERT('k' == firstMember.peek());
367 const char* savePos = firstMember.fChar;
368 firstMember.skipToNonAlphaNum();
369 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400370 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400371 const char* lastUnderscore = nullptr;
372 do {
373 if (!firstMember.skipToEndBracket('_')) {
374 break;
375 }
376 if (firstMember.fChar > wordEnd) {
377 break;
378 }
379 lastUnderscore = firstMember.fChar;
380 } while (firstMember.next());
381 if (lastUnderscore) {
382 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400383 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400384 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400385 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400386 }
387 if (!def) {
388 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500389 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400390 break;
391 }
392 }
393 def->fVisited = true;
394 for (auto& child : def->fChildren) {
395 if (MarkType::kCode == child->fMarkType) {
396 def = child;
397 break;
398 }
399 }
400 if (MarkType::kCode != def->fMarkType) {
401 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500402 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400403 break;
404 }
405 if (def->crossCheck(token)) {
406 def->fVisited = true;
407 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500408 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
409 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400410 }
411 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400412 string constName = MarkType::kEnumClass == token.fMarkType ?
413 fullName : className;
414 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400415 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400416 if (!def) {
417 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400418 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400419 }
420 if (!def) {
421 if (string::npos == child->fName.find("Legacy_")) {
422 SkDebugf("const missing from bmh: %s\n", constName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500423 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400424 }
425 } else {
426 def->fVisited = true;
427 }
428 }
429 } break;
430 case MarkType::kMember:
431 if (def) {
432 def->fVisited = true;
433 } else {
434 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500435 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400436 }
437 break;
Cary Clark2f466242017-12-11 16:03:17 -0500438 case MarkType::kTypedef:
439 if (def) {
440 def->fVisited = true;
441 } else {
442 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500443 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500444 }
445 break;
Cary Clark8032b982017-07-28 11:04:54 -0400446 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400447 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400448 break;
449 }
450 }
451 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500452 int crossChecks = 0;
453 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400454 for (auto& classMapper : fIClassMap) {
455 string className = classMapper.first;
456 auto finder = bmhParser.fClassMap.find(className);
457 if (bmhParser.fClassMap.end() == finder) {
458 continue;
459 }
460 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500461 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500462 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400463 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500464 if (crossChecks) {
465 SkDebugf(".");
466 } else {
467 SkDebugf("cross-check");
468 firstCheck = className;
469 }
470 ++crossChecks;
471 }
472 if (crossChecks) {
473 if (1 == crossChecks) {
474 SkDebugf("%s", firstCheck.c_str());
475 }
476 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400477 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400478 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500479 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400480}
481
482IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
483 const string& name) {
484 string className;
485 const Definition* test = fParent;
486 while (Definition::Type::kFileType != test->fType) {
487 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
488 className = test->fName + "::";
489 break;
490 }
491 test = test->fParent;
492 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400493 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400494 unordered_map<string, IClassDefinition>& map = fIClassMap;
495 IClassDefinition& markupDef = map[className];
496 if (markupDef.fStart) {
497 typedef IClassDefinition* IClassDefPtr;
498 return INHERITED::reportError<IClassDefPtr>("class already defined");
499 }
500 markupDef.fFileName = fFileName;
501 markupDef.fStart = includeDef.fStart;
502 markupDef.fContentStart = includeDef.fStart;
503 markupDef.fName = className;
504 markupDef.fContentEnd = includeDef.fContentEnd;
505 markupDef.fTerminator = includeDef.fTerminator;
506 markupDef.fParent = fParent;
507 markupDef.fLineCount = fLineCount;
508 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
509 MarkType::kStruct : MarkType::kClass;
510 markupDef.fKeyWord = includeDef.fKeyWord;
511 markupDef.fType = Definition::Type::kMark;
512 fParent = &markupDef;
513 return &markupDef;
514}
515
516void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
517 auto& tokens = classDef.fTokens;
518 for (auto& token : tokens) {
519 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
520 continue;
521 }
522 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400523 this->writeString(
524 "# ------------------------------------------------------------------------------");
525 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400526 }
527 switch (token.fMarkType) {
528 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500529 case MarkType::kEnumClass:
Cary Clark9174bda2017-09-19 17:39:32 -0400530 this->dumpEnum(token);
Cary Clark8032b982017-07-28 11:04:54 -0400531 break;
532 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400533 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400534 break;
535 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400536 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400537 continue;
538 break;
539 default:
540 SkASSERT(0);
541 }
542 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400543 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400544 this->lf(1);
545 this->writeString("// incomplete");
546 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400547 this->writeEndTag();
548 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400549 this->writeTag("SeeAlso");
550 this->writeSpace();
551 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400552 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500553 switch (token.fMarkType) {
554 case MarkType::kEnum:
555 case MarkType::kEnumClass:
556 this->writeEndTag("Enum");
557 break;
558 case MarkType::kMethod:
559 this->writeEndTag("Method");
560 break;
561 case MarkType::kMember:
562 this->writeEndTag("Member");
563 continue;
564 break;
565 default:
566 SkASSERT(0);
567 }
Cary Clark9174bda2017-09-19 17:39:32 -0400568 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400569 }
570}
Cary Clark9174bda2017-09-19 17:39:32 -0400571void IncludeParser::dumpComment(const Definition& token) {
572 fLineCount = token.fLineCount;
573 fChar = fLine = token.fContentStart;
574 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400575 bool sawParam = false;
576 bool multiline = false;
577 bool sawReturn = false;
578 bool sawComment = false;
579 bool methodHasReturn = false;
580 vector<string> methodParams;
581 vector<string> foundParams;
582 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400583 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
584 token.fLineCount);
585 if (MarkType::kMethod == token.fMarkType) {
586 methodName.fName = string(token.fContentStart,
587 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400588 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500589 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400590 && !methodParser.strnchr('~', methodParser.fEnd);
591 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
592 const char* nextEnd = paren;
593 do {
594 string paramName;
595 methodParser.fChar = nextEnd + 1;
596 methodParser.skipSpace();
597 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
598 continue;
599 }
600 methodParams.push_back(paramName);
601 } while (')' != nextEnd[0]);
602 }
Cary Clark9174bda2017-09-19 17:39:32 -0400603 for (const auto& child : token.fTokens) {
604 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
605 break;
606 }
Cary Clark8032b982017-07-28 11:04:54 -0400607 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400608 if (child.fPrivate) {
609 break;
610 }
Cary Clark8032b982017-07-28 11:04:54 -0400611 if ('@' == child.fContentStart[0]) {
612 TextParser parser(&child);
613 do {
614 parser.next();
615 if (parser.startsWith("param ")) {
616 parser.skipWord("param");
617 const char* parmStart = parser.fChar;
618 parser.skipToSpace();
619 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
620 parser.skipWhiteSpace();
621 do {
622 size_t nextComma = parmName.find(',');
623 string piece;
624 if (string::npos == nextComma) {
625 piece = parmName;
626 parmName = "";
627 } else {
628 piece = parmName.substr(0, nextComma);
629 parmName = parmName.substr(nextComma + 1);
630 }
631 if (sawParam) {
632 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400633 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400634 }
Cary Clark9174bda2017-09-19 17:39:32 -0400635 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400636 } else {
637 if (sawComment) {
638 this->nl();
639 }
640 this->lf(2);
641 }
642 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400643 this->writeTag("Param", piece);
644 this->writeSpace(2);
645 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
646 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400647 sawParam = true;
648 sawComment = false;
649 } while (parmName.length());
650 parser.skipTo(parser.fEnd);
651 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
652 parser.skipWord("return");
653 if ('s' == parser.peek()) {
654 parser.next();
655 }
656 if (sawParam) {
657 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400658 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400659 }
Cary Clark9174bda2017-09-19 17:39:32 -0400660 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400661 }
662 this->checkForMissingParams(methodParams, foundParams);
663 sawParam = false;
664 sawComment = false;
665 multiline = false;
666 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400667 this->writeTag("Return");
668 this->writeSpace(2);
669 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
670 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400671 sawReturn = true;
672 parser.skipTo(parser.fEnd);
673 } else {
674 this->reportError("unexpected doxygen directive");
675 }
676 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400677 } else if (child.length() > 1) {
678 const char* start = child.fContentStart;
679 ptrdiff_t length = child.fContentEnd - start;
680 SkASSERT(length >= 0);
681 while (length && '/' == start[0]) {
682 start += 1;
683 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400684 }
Cary Clark9174bda2017-09-19 17:39:32 -0400685 while (length && '/' == start[length - 1]) {
686 length -= 1;
687 if (length && '*' == start[length - 1]) {
688 length -= 1;
689 }
690 }
691 if (length) {
692 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
693 if (sawParam || sawReturn) {
694 this->indentToColumn(8);
695 }
696 this->writeBlock(length, start);
697 this->writeSpace();
698 sawComment = true;
699 if (sawParam || sawReturn) {
700 multiline = true;
701 }
Cary Clark8032b982017-07-28 11:04:54 -0400702 }
703 }
704 }
705 }
706 if (sawParam || sawReturn) {
707 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400708 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400709 }
Cary Clark9174bda2017-09-19 17:39:32 -0400710 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400711 }
712 if (!sawReturn) {
713 if (!sawParam) {
714 if (sawComment) {
715 this->nl();
716 }
717 this->lf(2);
718 }
719 this->checkForMissingParams(methodParams, foundParams);
720 }
721 if (methodHasReturn != sawReturn) {
722 if (!methodHasReturn) {
723 this->reportError("unexpected doxygen return");
724 } else {
725 if (sawComment) {
726 this->nl();
727 }
728 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400729 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400730 }
731 }
732}
733
Cary Clark9174bda2017-09-19 17:39:32 -0400734void IncludeParser::dumpEnum(const Definition& token) {
735 this->writeTag("Enum", token.fName);
736 this->lf(2);
737 this->writeString("#Code");
738 this->lfAlways(1);
739 this->indentToColumn(4);
740 this->writeString("enum");
741 this->writeSpace();
742 if ("_anonymous" != token.fName.substr(0, 10)) {
743 this->writeString(token.fName);
744 this->writeSpace();
745 }
746 this->writeString("{");
747 this->lfAlways(1);
748 for (auto& child : token.fChildren) {
749 this->indentToColumn(8);
750 this->writeString(child->fName);
751 if (child->length()) {
752 this->writeSpace();
753 this->writeBlock(child->length(), child->fContentStart);
754 }
755 if (',' != fLastChar) {
756 this->writeString(",");
757 }
758 this->lfAlways(1);
759 }
760 this->indentToColumn(4);
761 this->writeString("};");
762 this->lf(1);
763 this->writeString("##");
764 this->lf(2);
765 this->dumpComment(token);
766 for (auto& child : token.fChildren) {
767 // start here;
768 // get comments before
769 // or after const values
770 this->writeString("#Const");
771 this->writeSpace();
772 this->writeString(child->fName);
773 TextParser val(child);
774 if (!val.eof()) {
775 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
776 val.next();
777 val.skipSpace();
778 const char* valEnd = val.anyOf(",\n");
779 if (!valEnd) {
780 valEnd = val.fEnd;
781 }
782 this->writeSpace();
783 this->writeBlock(valEnd - val.fStart, val.fStart);
784 } else {
785 this->writeSpace();
786 this->writeDefinition(*child);
787 }
788 }
789 this->lf(1);
790 for (auto comment : child->fChildren) {
791 if (MarkType::kComment == comment->fMarkType) {
792 TextParser parser(comment);
793 parser.skipExact("*");
794 parser.skipExact("*");
795 while (!parser.eof() && parser.skipWhiteSpace()) {
796 parser.skipExact("*");
797 parser.skipWhiteSpace();
798 const char* start = parser.fChar;
799 parser.skipToEndBracket('\n');
800 this->lf(1);
801 this->writeBlock(parser.fChar - start, start);
802 }
803 }
804 }
805 this->writeEndTag();
806 }
807 this->lf(2);
808}
809
810void IncludeParser::dumpMethod(const Definition& token) {
811 this->writeString("#Method");
812 this->writeSpace();
813 if ("SK_TO_STRING_NONVIRT" == token.fName) {
814 this->writeString("void toString(SkString* str) const;");
815 this->lf(2);
816 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
817 this->lf(2);
818 this->writeTag("Private");
819 this->lf(1);
820 this->writeString("macro expands to: void toString(SkString* str) const;");
821 this->writeEndTag();
822 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400823 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400824 "Creates string representation. The representation is read by\n"
825 "internal debugging tools. The interface and implementation may be\n"
826 "suppressed by defining SK_IGNORE_TO_STRING.";
827 this->writeBlock(sizeof(desc) - 1, desc);
828 this->lf(2);
829 this->writeTag("Param", "str");
830 this->writeSpace(2);
831 this->writeString("storage for string representation");
832 this->writeSpace();
833 this->writeString("##");
834 this->lf(2);
835 return;
836 }
837 this->writeBlock(token.length(), token.fStart);
838 this->lf(1);
839 this->dumpComment(token);
840}
841
842void IncludeParser::dumpMember(const Definition& token) {
843 this->writeTag("Member");
844 this->writeSpace();
845 this->writeDefinition(token, token.fName, 2);
846 lf(1);
847 for (auto child : token.fChildren) {
848 this->writeDefinition(*child);
849 }
850 this->writeEndTag();
851 lf(2);
852}
853
Cary Clarkd0530ba2017-09-14 11:25:39 -0400854bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400855 for (const auto& member : fIClassMap) {
856 if (string::npos != member.first.find("::")) {
857 continue;
858 }
859 if (!this->dumpTokens(dir, member.first)) {
860 return false;
861 }
862 }
863 return true;
864}
865
Ben Wagner63fd7602017-10-09 15:45:33 -0400866 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400867bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400868 string fileName = dir;
869 if (dir.length() && '/' != dir[dir.length() - 1]) {
870 fileName += '/';
871 }
872 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400873 fOut = fopen(fileName.c_str(), "wb");
874 if (!fOut) {
875 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400876 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400877 }
878 string prefixName = skClassName.substr(0, 2);
879 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
880 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400881 this->writeTagNoLF("Topic", topicName);
882 this->writeTag("Alias", topicName + "_Reference");
883 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400884 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500885 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
886 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400887 this->writeTag(containerType, skClassName);
888 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400889 auto& tokens = classMap.fTokens;
890 for (auto& token : tokens) {
891 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
892 continue;
893 }
Cary Clark9174bda2017-09-19 17:39:32 -0400894 this->writeDefinition(token);
895 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400896 }
897 this->lf(2);
898 string className(skClassName.substr(2));
899 vector<string> sortedClasses;
900 size_t maxLen = 0;
901 for (const auto& oneClass : fIClassMap) {
902 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
903 continue;
904 }
905 string structName = oneClass.first.substr(skClassName.length() + 2);
906 maxLen = SkTMax(maxLen, structName.length());
907 sortedClasses.emplace_back(structName);
908 }
Cary Clark9174bda2017-09-19 17:39:32 -0400909 this->writeTag("Topic", "Overview");
910 this->lf(2);
911 this->writeTag("Subtopic", "Subtopics");
912 this->writeEndTag("ToDo", "manually add subtopics");
913 this->writeTableHeader("topics", 0, "description");
914 this->writeTableTrailer();
915 this->writeEndTag();
916 this->lf(2);
917 if (maxLen) {
918 this->writeTag("Subtopic", "Structs");
919 this->writeTableHeader("description", maxLen, "struct");
920 for (auto& name : sortedClasses) {
921 this->writeTableRow(maxLen, name);
922 }
923 this->writeTableTrailer();
924 this->writeEndTag("Subtopic");
925 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400926 }
Cary Clark8032b982017-07-28 11:04:54 -0400927 maxLen = 0;
Cary Clark9174bda2017-09-19 17:39:32 -0400928 size_t constructorMax = 0;
929 size_t operatorMax = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400930 vector<string> sortedNames;
Cary Clark9174bda2017-09-19 17:39:32 -0400931 vector<string> constructorNames;
932 vector<string> operatorNames;
Cary Clark8032b982017-07-28 11:04:54 -0400933 for (const auto& token : classMap.fTokens) {
934 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
935 continue;
936 }
Cary Clark9174bda2017-09-19 17:39:32 -0400937 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400938 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
939 continue;
940 }
Cary Clark9174bda2017-09-19 17:39:32 -0400941 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
942 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
943 constructorMax = SkTMax(constructorMax, name.length());
944 constructorNames.emplace_back(name);
945 continue;
946 }
947 if (name.substr(0, 8) == "operator") {
948 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
949 operatorMax = SkTMax(operatorMax, name.length());
950 operatorNames.emplace_back(name);
951 continue;
952 }
Cary Clark8032b982017-07-28 11:04:54 -0400953 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
954 continue;
955 }
Cary Clark9174bda2017-09-19 17:39:32 -0400956 if ("SK_TO_STRING_NONVIRT" == name) {
957 name = "toString";
958 }
Cary Clark8032b982017-07-28 11:04:54 -0400959 size_t paren = name.find('(');
960 size_t funcLen = string::npos == paren ? name.length() : paren;
961 maxLen = SkTMax(maxLen, funcLen);
962 sortedNames.emplace_back(name);
963 }
Cary Clark9174bda2017-09-19 17:39:32 -0400964 if (constructorMax) {
965 std::sort(constructorNames.begin(), constructorNames.end());
966 this->writeTag("Subtopic", "Constructors");
967 this->writeTableHeader("description", constructorMax, "function");
968 for (auto& name : constructorNames) {
969 this->writeTableRow(constructorMax, name);
970 }
971 this->writeTableTrailer();
972 this->writeEndTag("Subtopic");
973 this->lf(2);
974 }
975 if (operatorMax) {
976 std::sort(operatorNames.begin(), operatorNames.end());
977 this->writeTag("Subtopic", "Operators");
978 this->writeTableHeader("description", operatorMax, "function");
979 for (auto& name : operatorNames) {
980 this->writeTableRow(operatorMax, name);
981 }
982 this->writeTableTrailer();
983 this->writeEndTag("Subtopic");
984 this->lf(2);
985 }
Cary Clark8032b982017-07-28 11:04:54 -0400986 std::sort(sortedNames.begin(), sortedNames.end());
Cary Clark9174bda2017-09-19 17:39:32 -0400987 this->writeTag("Subtopic", "Member_Functions");
988 this->writeTableHeader("description", maxLen, "function");
Cary Clark8032b982017-07-28 11:04:54 -0400989 for (auto& name : sortedNames) {
990 size_t paren = name.find('(');
991 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark9174bda2017-09-19 17:39:32 -0400992 this->writeTableRow(maxLen, name.substr(0, funcLen));
Cary Clark8032b982017-07-28 11:04:54 -0400993 }
Cary Clark9174bda2017-09-19 17:39:32 -0400994 this->writeTableTrailer();
995 this->writeEndTag("Subtopic");
996 this->lf(2);
997 this->writeEndTag("Topic");
998 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400999
1000 for (auto& oneClass : fIClassMap) {
1001 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1002 continue;
1003 }
1004 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -04001005 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -04001006 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -04001007 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -05001008 KeyWord keyword = oneClass.second.fKeyWord;
1009 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1010 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001011 this->writeTag(containerType, innerName);
1012 this->lf(2);
1013 this->writeTag("Code");
1014 this->writeEndTag("ToDo", "fill this in manually");
1015 this->writeEndTag();
1016 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001017 for (auto& token : oneClass.second.fTokens) {
1018 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1019 continue;
1020 }
Cary Clark9174bda2017-09-19 17:39:32 -04001021 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001022 }
1023 this->lf(2);
1024 this->dumpClassTokens(oneClass.second);
1025 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001026 this->writeEndTag(containerType, innerName);
1027 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001028 }
1029 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001030 this->writeEndTag(containerType, skClassName);
1031 this->lf(2);
1032 this->writeEndTag("Topic", topicName);
1033 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001034 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001035 SkDebugf("wrote %s\n", fileName.c_str());
1036 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001037}
1038
1039bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1040 // add comment preceding class, if any
1041 const Definition* parent = includeDef.fParent;
1042 int index = includeDef.fParentIndex;
1043 auto wordIter = parent->fTokens.begin();
1044 std::advance(wordIter, index);
1045 SkASSERT(&*wordIter == &includeDef);
1046 while (parent->fTokens.begin() != wordIter) {
1047 auto testIter = std::prev(wordIter);
1048 if (Definition::Type::kWord != testIter->fType
1049 && Definition::Type::kKeyWord != testIter->fType
1050 && (Definition::Type::kBracket != testIter->fType
1051 || Bracket::kAngle != testIter->fBracket)
1052 && (Definition::Type::kPunctuation != testIter->fType
1053 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1054 break;
1055 }
1056 wordIter = testIter;
1057 }
1058 auto commentIter = wordIter;
1059 while (parent->fTokens.begin() != commentIter) {
1060 auto testIter = std::prev(commentIter);
1061 bool isComment = Definition::Type::kBracket == testIter->fType
1062 && (Bracket::kSlashSlash == testIter->fBracket
1063 || Bracket::kSlashStar == testIter->fBracket);
1064 if (!isComment) {
1065 break;
1066 }
1067 commentIter = testIter;
1068 }
1069 while (commentIter != wordIter) {
1070 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1071 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1072 return false;
1073 }
1074 commentIter = std::next(commentIter);
1075 }
1076 return true;
1077}
1078
Cary Clarkbad5ad72017-08-03 17:14:08 -04001079bool IncludeParser::internalName(const Definition& token) const {
1080 return 0 == token.fName.find("internal_")
1081 || 0 == token.fName.find("Internal_")
1082 || 0 == token.fName.find("legacy_")
1083 || 0 == token.fName.find("temporary_")
1084 || 0 == token.fName.find("private_");
1085}
1086
Cary Clark8032b982017-07-28 11:04:54 -04001087// caller calls reportError, so just return false here
1088bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1089 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001090 // parse class header
1091 auto iter = includeDef->fTokens.begin();
1092 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1093 // todo : documentation is ignoring this for now
1094 iter = std::next(iter);
1095 }
1096 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1097 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001098 iter = std::next(iter);
1099 if (iter == includeDef->fTokens.end()) {
1100 return true; // forward declaration only
1101 }
Cary Clark8032b982017-07-28 11:04:54 -04001102 do {
1103 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001104 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001105 }
1106 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1107 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001108 }
Cary Clark8032b982017-07-28 11:04:54 -04001109 } while (static_cast<void>(iter = std::next(iter)), true);
1110 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001111 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001112 }
1113 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1114 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001115 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001116 }
1117 markupDef->fStart = iter->fStart;
1118 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001119 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001120 }
1121// if (1 != includeDef->fChildren.size()) {
1122// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1123// }
1124 includeDef = includeDef->fChildren.front();
1125 iter = includeDef->fTokens.begin();
1126 // skip until public
1127 int publicIndex = 0;
1128 if (IsStruct::kNo == isStruct) {
1129 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1130 size_t publicLen = strlen(publicName);
1131 while (iter != includeDef->fTokens.end()
1132 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1133 || strncmp(iter->fStart, publicName, publicLen))) {
1134 iter = std::next(iter);
1135 ++publicIndex;
1136 }
1137 }
1138 auto childIter = includeDef->fChildren.begin();
1139 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1140 (*childIter)->fPrivate = true;
1141 childIter = std::next(childIter);
1142 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001143 int keyIndex = publicIndex;
1144 KeyWord currentKey = KeyWord::kPublic;
1145 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1146 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001147 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1148 size_t protectedLen = strlen(protectedName);
1149 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1150 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001151 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001152 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001153 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1154 const char* testStart = iter->fStart;
1155 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1156 iter = std::next(iter);
1157 ++keyIndex;
1158 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1159 currentKey = KeyWord::kPublic;
1160 break;
1161 }
1162 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1163 currentKey = KeyWord::kProtected;
1164 break;
1165 }
1166 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1167 currentKey = KeyWord::kPrivate;
1168 break;
1169 }
1170 }
1171 fLastObject = nullptr;
1172 if (KeyWord::kPublic == currentKey) {
1173 if (!this->parseObject(child, markupDef)) {
1174 return false;
1175 }
1176 } else {
1177 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001178 }
Cary Clark73fa9722017-08-29 17:36:51 -04001179 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001180 childIter = std::next(childIter);
1181 }
Cary Clark8032b982017-07-28 11:04:54 -04001182 SkASSERT(fParent->fParent);
1183 fParent = fParent->fParent;
1184 return true;
1185}
1186
1187bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1188 int lineCount, Definition* markupDef) {
1189 TextParser parser(filename, start, end, lineCount);
1190 // parse doxygen if present
1191 if (parser.startsWith("**")) {
1192 parser.next();
1193 parser.next();
1194 parser.skipWhiteSpace();
1195 if ('\\' == parser.peek()) {
1196 parser.next();
1197 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1198 return reportError<bool>("missing object type");
1199 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001200 if (!parser.skipWord(markupDef->fName.c_str()) &&
1201 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001202 return reportError<bool>("missing object name");
1203 }
1204
1205 }
1206 }
1207 // remove leading '*' if present
1208 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1209 while (!parser.eof() && parser.skipWhiteSpace()) {
1210 while ('*' == parser.peek()) {
1211 parser.next();
1212 if (parser.eof()) {
1213 break;
1214 }
1215 parser.skipWhiteSpace();
1216 }
1217 if (parser.eof()) {
1218 break;
1219 }
1220 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001221 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark8032b982017-07-28 11:04:54 -04001222 parser.fLineCount, parent);
1223 parser.skipToEndBracket('\n');
1224 }
1225 return true;
1226}
1227
1228bool IncludeParser::parseDefine() {
1229
1230 return true;
1231}
1232
1233bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
1234 string nameStr;
1235 if (child->fTokens.size() > 0) {
1236 auto token = child->fTokens.begin();
1237 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1238 token = token->fTokens.begin();
1239 }
1240 if (Definition::Type::kWord == token->fType) {
1241 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1242 }
1243 }
1244 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1245 child->fLineCount, markupDef);
1246 Definition* markupChild = &markupDef->fTokens.back();
1247 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1248 markupChild->fKeyWord = KeyWord::kEnum;
1249 TextParser enumName(child);
1250 enumName.skipExact("enum ");
Cary Clarkbad5ad72017-08-03 17:14:08 -04001251 if (enumName.skipExact("class ")) {
1252 markupChild->fMarkType = MarkType::kEnumClass;
1253 }
Cary Clark8032b982017-07-28 11:04:54 -04001254 const char* nameStart = enumName.fChar;
1255 enumName.skipToSpace();
Ben Wagner63fd7602017-10-09 15:45:33 -04001256 markupChild->fName = markupDef->fName + "::" +
Cary Clark8032b982017-07-28 11:04:54 -04001257 string(nameStart, (size_t) (enumName.fChar - nameStart));
1258 if (!this->findComments(*child, markupChild)) {
1259 return false;
1260 }
1261 TextParser parser(child);
1262 parser.skipToEndBracket('{');
Cary Clark9174bda2017-09-19 17:39:32 -04001263 parser.next();
Cary Clark8032b982017-07-28 11:04:54 -04001264 const char* dataEnd;
1265 do {
Cary Clark8032b982017-07-28 11:04:54 -04001266 parser.skipWhiteSpace();
1267 if ('}' == parser.peek()) {
1268 break;
1269 }
1270 Definition* comment = nullptr;
1271 // note that comment, if any, can be before or after (on the same line, though) as member
1272 if ('#' == parser.peek()) {
1273 // fixme: handle preprecessor, but just skip it for now
1274 parser.skipToLineStart();
1275 }
1276 while (parser.startsWith("/*") || parser.startsWith("//")) {
1277 parser.next();
1278 const char* start = parser.fChar;
1279 const char* end;
1280 if ('*' == parser.peek()) {
1281 end = parser.strnstr("*/", parser.fEnd);
1282 parser.fChar = end;
1283 parser.next();
1284 parser.next();
1285 } else {
1286 end = parser.trimmedLineEnd();
1287 parser.skipToLineStart();
1288 }
1289 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1290 markupChild);
1291 comment = &markupChild->fTokens.back();
1292 comment->fTerminator = end;
1293 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1294 return false;
1295 }
1296 parser.skipWhiteSpace();
1297 }
1298 parser.skipWhiteSpace();
1299 const char* memberStart = parser.fChar;
1300 if ('}' == memberStart[0]) {
1301 break;
1302 }
Cary Clark9174bda2017-09-19 17:39:32 -04001303 // if there's comment on same the line as member def, output first as if it was before
1304
Cary Clark8032b982017-07-28 11:04:54 -04001305 parser.skipToNonAlphaNum();
1306 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001307 if (parser.eof() || !parser.skipWhiteSpace()) {
1308 return this->reportError<bool>("enum member must end with comma 1");
1309 }
Cary Clark8032b982017-07-28 11:04:54 -04001310 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001311 if ('=' == parser.peek()) {
1312 parser.skipToEndBracket(',');
1313 }
1314 if (parser.eof() || ',' != parser.peek()) {
1315 return this->reportError<bool>("enum member must end with comma 2");
1316 }
1317 dataEnd = parser.fChar;
1318 const char* start = parser.anyOf("/\n");
1319 SkASSERT(start);
1320 parser.skipTo(start);
1321 if ('/' == parser.next()) {
1322 char slashStar = parser.next();
1323 if ('/' == slashStar || '*' == slashStar) {
1324 TextParser::Save save(&parser);
1325 char doxCheck = parser.next();
1326 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1327 save.restore();
1328 }
1329 }
1330 parser.skipWhiteSpace();
1331 const char* commentStart = parser.fChar;
1332 if ('/' == slashStar) {
1333 parser.skipToEndBracket('\n');
1334 } else {
1335 parser.skipToEndBracket("*/");
1336 }
1337 SkASSERT(!parser.eof());
1338 const char* commentEnd = parser.fChar;
1339 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
1340 parser.fLineCount, markupChild);
1341 comment = &markupChild->fTokens.back();
1342 comment->fTerminator = commentEnd;
1343 }
Cary Clark8032b982017-07-28 11:04:54 -04001344 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1345 markupChild);
1346 Definition* member = &markupChild->fTokens.back();
1347 member->fName = memberName;
1348 if (comment) {
1349 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001350 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001351 }
1352 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001353 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001354 for (auto outsideMember : child->fChildren) {
1355 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001356 continue;
1357 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001358 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1359 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001360 continue;
1361 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001362 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1363 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
1364 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild);
Cary Clark884dd7d2017-10-11 10:37:52 -04001365 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001366 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001367 // FIXME: ? add comment as well ?
1368 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001369 }
Cary Clark8032b982017-07-28 11:04:54 -04001370 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1371 SkASSERT(classDef.fStart);
1372 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1373 markupChild->fName = uniqueName;
1374 classDef.fEnums[uniqueName] = markupChild;
1375 return true;
1376}
1377
1378bool IncludeParser::parseInclude(const string& name) {
1379 fParent = &fIncludeMap[name];
1380 fParent->fName = name;
1381 fParent->fFileName = fFileName;
1382 fParent->fType = Definition::Type::kFileType;
1383 fParent->fContentStart = fChar;
1384 fParent->fContentEnd = fEnd;
1385 // parse include file into tree
1386 while (fChar < fEnd) {
1387 if (!this->parseChar()) {
1388 return false;
1389 }
1390 }
1391 // parse tree and add named objects to maps
1392 fParent = &fIncludeMap[name];
1393 if (!this->parseObjects(fParent, nullptr)) {
1394 return false;
1395 }
1396 return true;
1397}
1398
1399bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1400 const char* typeStart = child->fChildren[0]->fContentStart;
1401 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1402 child->fLineCount, markupDef);
1403 Definition* markupChild = &markupDef->fTokens.back();
1404 TextParser nameParser(child);
1405 nameParser.skipToNonAlphaNum();
1406 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1407 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1408 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1409 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001410 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001411 classDef.fMembers[uniqueName] = markupChild;
1412 if (child->fParentIndex >= 2) {
1413 auto comment = child->fParent->fTokens.begin();
1414 std::advance(comment, child->fParentIndex - 2);
1415 if (Definition::Type::kBracket == comment->fType
1416 && (Bracket::kSlashStar == comment->fBracket
1417 || Bracket::kSlashSlash == comment->fBracket)) {
1418 TextParser parser(&*comment);
1419 do {
1420 parser.skipToAlpha();
1421 if (parser.eof()) {
1422 break;
1423 }
1424 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001425 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001426 if (Bracket::kSlashStar == comment->fBracket) {
1427 const char* commentEnd = parser.strnstr("*/", end);
1428 if (commentEnd) {
1429 end = commentEnd;
1430 }
1431 }
1432 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1433 markupDef);
1434 Definition* commentChild = &markupDef->fTokens.back();
1435 markupChild->fChildren.emplace_back(commentChild);
1436 parser.skipTo(end);
1437 } while (!parser.eof());
1438 }
1439 }
1440 return true;
1441}
1442
1443bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1444 auto tokenIter = child->fParent->fTokens.begin();
1445 std::advance(tokenIter, child->fParentIndex);
1446 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001447 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001448 bool addConst = false;
1449 auto operatorCheck = tokenIter;
1450 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1451 operatorCheck = std::prev(tokenIter);
1452 }
1453 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001454 auto closeParen = std::next(tokenIter);
1455 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1456 '(' == closeParen->fContentStart[0]);
1457 nameEnd = closeParen->fContentEnd + 1;
1458 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001459 if (Definition::Type::kKeyWord == closeParen->fType &&
1460 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001461 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001462 }
Cary Clarka560c472017-11-27 10:44:06 -05001463 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001464 }
1465 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001466 if (addConst) {
1467 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001468 }
Cary Clark8032b982017-07-28 11:04:54 -04001469 while (tokenIter != child->fParent->fTokens.begin()) {
1470 auto testIter = std::prev(tokenIter);
1471 switch (testIter->fType) {
1472 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001473 if (testIter == child->fParent->fTokens.begin() &&
1474 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1475 KeyWord::kIfndef == child->fParent->fKeyWord ||
1476 KeyWord::kIf == child->fParent->fKeyWord)) {
1477 std::next(tokenIter);
1478 break;
1479 }
Cary Clark8032b982017-07-28 11:04:54 -04001480 goto keepGoing;
1481 case Definition::Type::kKeyWord: {
1482 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1483 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1484 goto keepGoing;
1485 }
1486 } break;
1487 case Definition::Type::kBracket:
1488 if (Bracket::kAngle == testIter->fBracket) {
1489 goto keepGoing;
1490 }
1491 break;
1492 case Definition::Type::kPunctuation:
1493 if (Punctuation::kSemicolon == testIter->fPunctuation
1494 || Punctuation::kLeftBrace == testIter->fPunctuation
1495 || Punctuation::kColon == testIter->fPunctuation) {
1496 break;
1497 }
1498 keepGoing:
1499 tokenIter = testIter;
1500 continue;
1501 default:
1502 break;
1503 }
1504 break;
1505 }
1506 tokenIter->fName = nameStr;
1507 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001508 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001509 auto testIter = child->fParent->fTokens.begin();
1510 SkASSERT(child->fParentIndex > 0);
1511 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001512 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1513 0 == tokenIter->fParentIndex) {
1514 tokenIter = std::next(tokenIter);
1515 }
Cary Clark8032b982017-07-28 11:04:54 -04001516 const char* start = tokenIter->fContentStart;
1517 const char* end = tokenIter->fContentEnd;
1518 const char kDebugCodeStr[] = "SkDEBUGCODE";
1519 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1520 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1521 std::advance(testIter, 1);
1522 start = testIter->fContentStart + 1;
1523 end = testIter->fContentEnd - 1;
1524 } else {
1525 end = testIter->fContentEnd;
1526 while (testIter != child->fParent->fTokens.end()) {
1527 testIter = std::next(testIter);
1528 switch (testIter->fType) {
1529 case Definition::Type::kPunctuation:
1530 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1531 || Punctuation::kLeftBrace == testIter->fPunctuation
1532 || Punctuation::kColon == testIter->fPunctuation);
1533 end = testIter->fStart;
1534 break;
1535 case Definition::Type::kKeyWord: {
1536 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1537 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1538 continue;
1539 }
1540 } break;
1541 default:
1542 continue;
1543 }
1544 break;
1545 }
1546 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001547 while (end > start && ' ' >= end[-1]) {
1548 --end;
1549 }
Cary Clark9174bda2017-09-19 17:39:32 -04001550 if (!markupDef) {
1551 auto parentIter = child->fParent->fTokens.begin();
1552 SkASSERT(child->fParentIndex > 0);
1553 std::advance(parentIter, child->fParentIndex - 1);
1554 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001555 TextParser nameParser(methodName);
1556 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001557 return true; // expect this is inline class definition outside of class
1558 }
Cary Clarka560c472017-11-27 10:44:06 -05001559 string name(nameParser.fLine, nameParser.lineLength());
1560 auto finder = fIFunctionMap.find(name);
1561 if (fIFunctionMap.end() != finder) {
1562 // create unique name
1563 SkASSERT(0); // incomplete
1564 }
1565 auto globalFunction = &fIFunctionMap[name];
1566 globalFunction->fContentStart = start;
1567 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001568 globalFunction->fFiddle = name;
1569 globalFunction->fContentEnd = end;
1570 globalFunction->fMarkType = MarkType::kMethod;
1571 globalFunction->fLineCount = tokenIter->fLineCount;
1572 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001573 }
Cary Clark8032b982017-07-28 11:04:54 -04001574 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1575 markupDef);
1576 Definition* markupChild = &markupDef->fTokens.back();
1577 // do find instead -- I wonder if there is a way to prevent this in c++
1578 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1579 SkASSERT(classDef.fStart);
1580 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1581 markupChild->fName = uniqueName;
1582 if (!this->findComments(*child, markupChild)) {
1583 return false;
1584 }
1585 classDef.fMethods[uniqueName] = markupChild;
1586 return true;
1587}
1588
Cary Clark8032b982017-07-28 11:04:54 -04001589bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1590 for (auto& child : parent->fChildren) {
1591 if (!this->parseObject(child, markupDef)) {
1592 return false;
1593 }
1594 }
1595 return true;
1596}
1597
1598bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1599 // set up for error reporting
1600 fLine = fChar = child->fStart;
1601 fEnd = child->fContentEnd;
1602 // todo: put original line number in child as well
1603 switch (child->fType) {
1604 case Definition::Type::kKeyWord:
1605 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001606 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001607 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001608 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001609 }
1610 break;
1611 case KeyWord::kEnum:
1612 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001613 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001614 }
1615 break;
1616 case KeyWord::kStruct:
1617 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001618 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001619 }
1620 break;
1621 case KeyWord::kTemplate:
1622 if (!this->parseTemplate()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001623 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001624 }
1625 break;
1626 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001627 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001628 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001629 }
1630 break;
1631 case KeyWord::kUnion:
1632 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001633 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001634 }
1635 break;
1636 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001637 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001638 }
1639 break;
1640 case Definition::Type::kBracket:
1641 switch (child->fBracket) {
1642 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001643 if (fLastObject) {
1644 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1645 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001646 if (!checkDeprecated.eof()) {
1647 checkDeprecated.skipWhiteSpace();
1648 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1649 break;
1650 }
1651 }
1652 }
1653 {
1654 auto tokenIter = child->fParent->fTokens.begin();
1655 std::advance(tokenIter, child->fParentIndex);
1656 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05001657 TextParser previousToken(&*tokenIter);
1658 if (previousToken.startsWith("SK_ATTR_DEPRECATED")) {
Cary Clark73fa9722017-08-29 17:36:51 -04001659 break;
1660 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05001661 if (Bracket::kPound == child->fParent->fBracket &&
1662 KeyWord::kIf == child->fParent->fKeyWord) {
1663 // TODO: this will skip methods named defined() -- for the
1664 // moment there aren't any
1665 if (previousToken.startsWith("defined")) {
1666 break;
1667 }
1668 }
Cary Clark73fa9722017-08-29 17:36:51 -04001669 }
Cary Clark8032b982017-07-28 11:04:54 -04001670 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001671 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001672 }
Cary Clark73fa9722017-08-29 17:36:51 -04001673 break;
Cary Clark8032b982017-07-28 11:04:54 -04001674 case Bracket::kSlashSlash:
1675 case Bracket::kSlashStar:
1676 // comments are picked up by parsing objects first
1677 break;
1678 case Bracket::kPound:
1679 // special-case the #xxx xxx_DEFINED entries
1680 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001681 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001682 case KeyWord::kIfndef:
1683 case KeyWord::kIfdef:
1684 if (child->boilerplateIfDef(fParent)) {
1685 if (!this->parseObjects(child, markupDef)) {
1686 return false;
1687 }
1688 break;
1689 }
1690 goto preproError;
1691 case KeyWord::kDefine:
1692 if (child->boilerplateDef(fParent)) {
1693 break;
1694 }
1695 goto preproError;
1696 case KeyWord::kEndif:
1697 if (child->boilerplateEndIf()) {
1698 break;
1699 }
1700 case KeyWord::kInclude:
1701 // ignored for now
1702 break;
1703 case KeyWord::kElse:
1704 case KeyWord::kElif:
1705 // todo: handle these
1706 break;
1707 default:
1708 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001709 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001710 }
1711 break;
1712 case Bracket::kAngle:
1713 // pick up templated function pieces when method is found
1714 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001715 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001716 if (!this->parseObjects(child, markupDef)) {
1717 return false;
1718 }
Cary Clark73fa9722017-08-29 17:36:51 -04001719 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001720 case Bracket::kSquare: {
1721 // check to see if parent is operator, the only case we handle so far
1722 auto prev = child->fParent->fTokens.begin();
1723 std::advance(prev, child->fParentIndex - 1);
1724 if (KeyWord::kOperator != prev->fKeyWord) {
1725 return child->reportError<bool>("expected operator overload");
1726 }
1727 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001728 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001729 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001730 }
1731 break;
1732 case Definition::Type::kWord:
1733 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001734 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001735 }
1736 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001737 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001738 }
1739 break;
1740 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001741 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001742 break;
1743 }
1744 return true;
1745}
1746
1747bool IncludeParser::parseTemplate() {
1748
1749 return true;
1750}
1751
Cary Clark2f466242017-12-11 16:03:17 -05001752bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1753 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05001754 typedefParser.skipExact("typedef");
1755 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05001756 string nameStr = typedefParser.typedefName();
1757 if (!markupDef) {
1758 Definition& typedefDef = fITypedefMap[nameStr];
1759 SkASSERT(!typedefDef.fStart);
1760 typedefDef.fStart = child->fContentStart;
1761 typedefDef.fContentStart = child->fContentStart;
1762 typedefDef.fName = nameStr;
1763 typedefDef.fFiddle = nameStr;
1764 typedefDef.fContentEnd = child->fContentEnd;
1765 typedefDef.fTerminator = child->fContentEnd;
1766 typedefDef.fMarkType = MarkType::kTypedef;
1767 typedefDef.fLineCount = child->fLineCount;
1768 return true;
1769 }
1770 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
1771 child->fLineCount, markupDef);
1772 Definition* markupChild = &markupDef->fTokens.back();
1773 markupChild->fName = nameStr;
1774 markupChild->fTerminator = markupChild->fContentEnd;
1775 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1776 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001777 return true;
1778}
1779
1780bool IncludeParser::parseUnion() {
1781
1782 return true;
1783}
1784
1785bool IncludeParser::parseChar() {
1786 char test = *fChar;
1787 if ('\\' == fPrev) {
1788 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001789// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001790 fLine = fChar + 1;
1791 }
1792 goto done;
1793 }
1794 switch (test) {
1795 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001796// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001797 fLine = fChar + 1;
1798 if (fInChar) {
1799 return reportError<bool>("malformed char");
1800 }
1801 if (fInString) {
1802 return reportError<bool>("malformed string");
1803 }
1804 if (!this->checkForWord()) {
1805 return false;
1806 }
1807 if (Bracket::kPound == this->topBracket()) {
1808 KeyWord keyWord = fParent->fKeyWord;
1809 if (KeyWord::kNone == keyWord) {
1810 return this->reportError<bool>("unhandled preprocessor directive");
1811 }
1812 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) {
1813 this->popBracket();
1814 }
1815 } else if (Bracket::kSlashSlash == this->topBracket()) {
1816 this->popBracket();
1817 }
1818 break;
1819 case '*':
1820 if (!fInCharCommentString && '/' == fPrev) {
1821 this->pushBracket(Bracket::kSlashStar);
1822 }
1823 if (!this->checkForWord()) {
1824 return false;
1825 }
1826 if (!fInCharCommentString) {
1827 this->addPunctuation(Punctuation::kAsterisk);
1828 }
1829 break;
1830 case '/':
1831 if ('*' == fPrev) {
1832 if (!fInCharCommentString) {
1833 return reportError<bool>("malformed closing comment");
1834 }
1835 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001836 TextParser::Save save(this);
1837 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001838 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001839 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001840 }
1841 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001842 }
Cary Clark8032b982017-07-28 11:04:54 -04001843 if (!fInCharCommentString && '/' == fPrev) {
1844 this->pushBracket(Bracket::kSlashSlash);
1845 break;
1846 }
1847 if (!this->checkForWord()) {
1848 return false;
1849 }
1850 break;
1851 case '\'':
1852 if (Bracket::kChar == this->topBracket()) {
1853 this->popBracket();
1854 } else if (!fInComment && !fInString) {
1855 if (fIncludeWord) {
1856 return this->reportError<bool>("word then single-quote");
1857 }
1858 this->pushBracket(Bracket::kChar);
1859 }
1860 break;
1861 case '\"':
1862 if (Bracket::kString == this->topBracket()) {
1863 this->popBracket();
1864 } else if (!fInComment && !fInChar) {
1865 if (fIncludeWord) {
1866 return this->reportError<bool>("word then double-quote");
1867 }
1868 this->pushBracket(Bracket::kString);
1869 }
1870 break;
1871 case ':':
1872 case '(':
1873 case '[':
1874 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001875 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1876 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1877 this->pushBracket(Bracket::kDebugCode);
1878 break;
1879 }
Cary Clark8032b982017-07-28 11:04:54 -04001880 if (fInCharCommentString) {
1881 break;
1882 }
1883 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1884 break;
1885 }
1886 if (!fInBrace) {
1887 if (!this->checkForWord()) {
1888 return false;
1889 }
1890 if (':' == test && !fInFunction) {
1891 break;
1892 }
1893 if ('{' == test) {
1894 this->addPunctuation(Punctuation::kLeftBrace);
1895 } else if (':' == test) {
1896 this->addPunctuation(Punctuation::kColon);
1897 }
1898 }
1899 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1900 && Bracket::kColon == fInBrace->fBracket) {
1901 Definition* braceParent = fParent->fParent;
1902 braceParent->fChildren.pop_back();
1903 braceParent->fTokens.pop_back();
1904 fParent = braceParent;
1905 fInBrace = nullptr;
1906 }
1907 this->pushBracket(
1908 '(' == test ? Bracket::kParen :
1909 '[' == test ? Bracket::kSquare :
1910 '{' == test ? Bracket::kBrace :
1911 Bracket::kColon);
1912 if (!fInBrace
1913 && ('{' == test || (':' == test && ' ' >= fChar[1]))
1914 && fInFunction) {
1915 fInBrace = fParent;
1916 }
1917 } break;
1918 case '<':
1919 if (fInCharCommentString || fInBrace) {
1920 break;
1921 }
1922 if (!this->checkForWord()) {
1923 return false;
1924 }
1925 if (fInEnum) {
1926 break;
1927 }
1928 this->pushBracket(Bracket::kAngle);
1929 break;
1930 case ')':
1931 case ']':
1932 case '}': {
1933 if (fInCharCommentString) {
1934 break;
1935 }
1936 if (!fInBrace) {
1937 if (!this->checkForWord()) {
1938 return false;
1939 }
1940 }
1941 bool popBraceParent = fInBrace == fParent;
1942 if ((')' == test ? Bracket::kParen :
1943 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
1944 this->popBracket();
1945 if (!fInFunction) {
1946 bool deprecatedMacro = false;
1947 if (')' == test) {
1948 auto iter = fParent->fTokens.end();
1949 bool lookForWord = false;
1950 while (fParent->fTokens.begin() != iter) {
1951 --iter;
1952 if (lookForWord) {
1953 if (Definition::Type::kWord != iter->fType) {
1954 break;
1955 }
1956 string word(iter->fContentStart, iter->length());
1957 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
1958 deprecatedMacro = true;
1959 // remove macro paren (would confuse method parsing later)
Ben Wagner63fd7602017-10-09 15:45:33 -04001960 fParent->fTokens.pop_back();
Cary Clark8032b982017-07-28 11:04:54 -04001961 fParent->fChildren.pop_back();
1962 }
1963 break;
1964 }
1965 if (Definition::Type::kBracket != iter->fType) {
1966 break;
1967 }
1968 if (Bracket::kParen != iter->fBracket) {
1969 break;
1970 }
1971 lookForWord = true;
1972 }
1973 }
1974 fInFunction = ')' == test && !deprecatedMacro;
1975 } else {
1976 fInFunction = '}' != test;
1977 }
Cary Clark73fa9722017-08-29 17:36:51 -04001978 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
1979 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04001980 } else {
1981 return reportError<bool>("malformed close bracket");
1982 }
1983 if (popBraceParent) {
1984 Definition* braceParent = fInBrace->fParent;
1985 braceParent->fChildren.pop_back();
1986 braceParent->fTokens.pop_back();
1987 fInBrace = nullptr;
1988 }
1989 } break;
1990 case '>':
1991 if (fInCharCommentString || fInBrace) {
1992 break;
1993 }
1994 if (!this->checkForWord()) {
1995 return false;
1996 }
1997 if (fInEnum) {
1998 break;
1999 }
Cary Clarka560c472017-11-27 10:44:06 -05002000 if (Bracket::kPound == this->topBracket()) {
2001 break;
2002 }
Cary Clark8032b982017-07-28 11:04:54 -04002003 if (Bracket::kAngle == this->topBracket()) {
2004 this->popBracket();
2005 } else {
2006 return reportError<bool>("malformed close angle bracket");
2007 }
2008 break;
2009 case '#': {
2010 if (fInCharCommentString || fInBrace) {
2011 break;
2012 }
2013 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2014 this->pushBracket(Bracket::kPound);
2015 break;
2016 }
2017 case '&':
2018 case ',':
2019 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05002020 case '+':
2021 case '=':
2022 case '-':
2023 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002024 if (fInCharCommentString || fInBrace) {
2025 break;
2026 }
2027 if (!this->checkForWord()) {
2028 return false;
2029 }
2030 break;
2031 case ';':
2032 if (fInCharCommentString || fInBrace) {
2033 break;
2034 }
2035 if (!this->checkForWord()) {
2036 return false;
2037 }
2038 if (Definition::Type::kKeyWord == fParent->fType
2039 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002040 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
2041 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2042 this->popObject();
2043 }
Cary Clark8032b982017-07-28 11:04:54 -04002044 if (KeyWord::kEnum == fParent->fKeyWord) {
2045 fInEnum = false;
2046 }
2047 this->popObject();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002048 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002049 } else if (Definition::Type::kBracket == fParent->fType
2050 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2051 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2052 list<Definition>::iterator baseIter = fParent->fTokens.end();
2053 list<Definition>::iterator namedIter = fParent->fTokens.end();
2054 for (auto tokenIter = fParent->fTokens.end();
2055 fParent->fTokens.begin() != tokenIter--; ) {
2056 if (tokenIter->fLineCount == fLineCount) {
2057 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2058 if (namedIter != fParent->fTokens.end()) {
2059 return reportError<bool>("found two named member tokens");
2060 }
2061 namedIter = tokenIter;
2062 }
2063 baseIter = tokenIter;
2064 } else {
2065 break;
2066 }
2067 }
2068 // FIXME: if a member definition spans multiple lines, this won't work
2069 if (namedIter != fParent->fTokens.end()) {
2070 if (baseIter == namedIter) {
2071 return this->reportError<bool>("expected type before named token");
2072 }
2073 Definition* member = &*namedIter;
2074 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002075 if (!member->fTerminator) {
2076 member->fTerminator = member->fContentEnd;
2077 }
Cary Clark8032b982017-07-28 11:04:54 -04002078 fParent->fChildren.push_back(member);
2079 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2080 member->fChildren.push_back(&*nameType);
2081 }
Cary Clark8032b982017-07-28 11:04:54 -04002082 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002083 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002084 } else if (fParent->fChildren.size() > 0) {
2085 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002086 Definition* priorEnum = fPriorEnum;
2087 fPriorEnum = nullptr;
2088 if (!priorEnum) {
2089 while (fParent->fChildren.begin() != lastIter) {
2090 std::advance(lastIter, -1);
2091 priorEnum = *lastIter;
2092 if (Definition::Type::kBracket != priorEnum->fType ||
2093 (Bracket::kSlashSlash != priorEnum->fBracket
2094 && Bracket::kSlashStar != priorEnum->fBracket)) {
2095 break;
2096 }
Cary Clark8032b982017-07-28 11:04:54 -04002097 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002098 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002099 }
2100 if (Definition::Type::kKeyWord == priorEnum->fType
2101 && KeyWord::kEnum == priorEnum->fKeyWord) {
2102 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002103 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002104 while (tokenWalker != fParent->fTokens.end()) {
2105 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002106 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002107 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2108 break;
2109 }
2110 }
2111 while (tokenWalker != fParent->fTokens.end()) {
2112 std::advance(tokenWalker, 1);
2113 const Definition* test = &*tokenWalker;
2114 if (Definition::Type::kBracket != test->fType ||
2115 (Bracket::kSlashSlash != test->fBracket
2116 && Bracket::kSlashStar != test->fBracket)) {
2117 break;
2118 }
2119 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002120 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002121 Definition* start = &*tokenWalker;
2122 bool foundExpected = true;
2123 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2124 const Definition* test = &*tokenWalker;
2125 if (expected != test->fKeyWord) {
2126 foundExpected = false;
2127 break;
2128 }
2129 if (tokenWalker == fParent->fTokens.end()) {
2130 break;
2131 }
2132 std::advance(tokenWalker, 1);
2133 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002134 if (!foundExpected) {
2135 foundExpected = true;
2136 tokenWalker = saveTokenWalker;
2137 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2138 const Definition* test = &*tokenWalker;
2139 if (expected != test->fKeyWord) {
2140 foundExpected = false;
2141 break;
2142 }
2143 if (tokenWalker == fParent->fTokens.end()) {
2144 break;
2145 }
2146 if (KeyWord::kNone != expected) {
2147 std::advance(tokenWalker, 1);
2148 }
2149 }
2150 if (foundExpected) {
2151 auto nameToken = priorEnum->fTokens.begin();
2152 string enumName = string(nameToken->fContentStart,
2153 nameToken->fContentEnd - nameToken->fContentStart);
2154 const Definition* test = &*tokenWalker;
2155 string constType = string(test->fContentStart,
2156 test->fContentEnd - test->fContentStart);
2157 if (enumName != constType) {
2158 foundExpected = false;
2159 } else {
2160 std::advance(tokenWalker, 1);
2161 }
2162 }
2163 }
Cary Clark8032b982017-07-28 11:04:54 -04002164 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2165 const char* nameStart = tokenWalker->fStart;
2166 std::advance(tokenWalker, 1);
2167 if (tokenWalker != fParent->fTokens.end()) {
2168 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2169 tp.skipToNonAlphaNum();
2170 start->fName = string(nameStart, tp.fChar - nameStart);
2171 start->fContentEnd = fChar;
2172 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002173 fPriorEnum = priorEnum;
2174 }
Cary Clark8032b982017-07-28 11:04:54 -04002175 }
2176 }
2177 }
2178 this->addPunctuation(Punctuation::kSemicolon);
2179 fInFunction = false;
2180 break;
2181 case '~':
2182 if (fInEnum) {
2183 break;
2184 }
2185 case '0': case '1': case '2': case '3': case '4':
2186 case '5': case '6': case '7': case '8': case '9':
2187 // TODO: don't want to parse numbers, but do need to track for enum defs
2188 // break;
2189 case 'A': case 'B': case 'C': case 'D': case 'E':
2190 case 'F': case 'G': case 'H': case 'I': case 'J':
2191 case 'K': case 'L': case 'M': case 'N': case 'O':
2192 case 'P': case 'Q': case 'R': case 'S': case 'T':
2193 case 'U': case 'V': case 'W': case 'X': case 'Y':
2194 case 'Z': case '_':
2195 case 'a': case 'b': case 'c': case 'd': case 'e':
2196 case 'f': case 'g': case 'h': case 'i': case 'j':
2197 case 'k': case 'l': case 'm': case 'n': case 'o':
2198 case 'p': case 'q': case 'r': case 's': case 't':
2199 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002200 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002201 if (fInCharCommentString || fInBrace) {
2202 break;
2203 }
2204 if (!fIncludeWord) {
2205 fIncludeWord = fChar;
2206 }
2207 break;
2208 }
2209done:
2210 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002211 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002212 return true;
2213}
2214
2215void IncludeParser::validate() const {
2216 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2217 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2218 }
2219 IncludeParser::ValidateKeyWords();
2220}