blob: da2ee54c2ee0aa0a6a28540a3ac8ff3590b13784 [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 Clark884dd7d2017-10-11 10:37:52 -0400452 for (auto& classMapper : fIClassMap) {
453 string className = classMapper.first;
454 auto finder = bmhParser.fClassMap.find(className);
455 if (bmhParser.fClassMap.end() == finder) {
456 continue;
457 }
458 RootDefinition* root = &finder->second;
Cary Clarka560c472017-11-27 10:44:06 -0500459 if (!root->dumpUnVisited(bmhParser.fSkip)) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400460 SkDebugf("some struct elements not found; struct finding in includeParser is missing\n");
Cary Clarkf059e7c2017-12-20 14:53:21 -0500461 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400462 }
463 SkDebugf("cross-checked %s\n", className.c_str());
Cary Clark8032b982017-07-28 11:04:54 -0400464 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400465 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500466 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400467}
468
469IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
470 const string& name) {
471 string className;
472 const Definition* test = fParent;
473 while (Definition::Type::kFileType != test->fType) {
474 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
475 className = test->fName + "::";
476 break;
477 }
478 test = test->fParent;
479 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400480 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400481 unordered_map<string, IClassDefinition>& map = fIClassMap;
482 IClassDefinition& markupDef = map[className];
483 if (markupDef.fStart) {
484 typedef IClassDefinition* IClassDefPtr;
485 return INHERITED::reportError<IClassDefPtr>("class already defined");
486 }
487 markupDef.fFileName = fFileName;
488 markupDef.fStart = includeDef.fStart;
489 markupDef.fContentStart = includeDef.fStart;
490 markupDef.fName = className;
491 markupDef.fContentEnd = includeDef.fContentEnd;
492 markupDef.fTerminator = includeDef.fTerminator;
493 markupDef.fParent = fParent;
494 markupDef.fLineCount = fLineCount;
495 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
496 MarkType::kStruct : MarkType::kClass;
497 markupDef.fKeyWord = includeDef.fKeyWord;
498 markupDef.fType = Definition::Type::kMark;
499 fParent = &markupDef;
500 return &markupDef;
501}
502
503void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
504 auto& tokens = classDef.fTokens;
505 for (auto& token : tokens) {
506 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
507 continue;
508 }
509 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400510 this->writeString(
511 "# ------------------------------------------------------------------------------");
512 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400513 }
514 switch (token.fMarkType) {
515 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500516 case MarkType::kEnumClass:
Cary Clark9174bda2017-09-19 17:39:32 -0400517 this->dumpEnum(token);
Cary Clark8032b982017-07-28 11:04:54 -0400518 break;
519 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400520 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400521 break;
522 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400523 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400524 continue;
525 break;
526 default:
527 SkASSERT(0);
528 }
529 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400530 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400531 this->lf(1);
532 this->writeString("// incomplete");
533 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400534 this->writeEndTag();
535 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400536 this->writeTag("SeeAlso");
537 this->writeSpace();
538 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400539 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500540 switch (token.fMarkType) {
541 case MarkType::kEnum:
542 case MarkType::kEnumClass:
543 this->writeEndTag("Enum");
544 break;
545 case MarkType::kMethod:
546 this->writeEndTag("Method");
547 break;
548 case MarkType::kMember:
549 this->writeEndTag("Member");
550 continue;
551 break;
552 default:
553 SkASSERT(0);
554 }
Cary Clark9174bda2017-09-19 17:39:32 -0400555 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400556 }
557}
Cary Clark9174bda2017-09-19 17:39:32 -0400558void IncludeParser::dumpComment(const Definition& token) {
559 fLineCount = token.fLineCount;
560 fChar = fLine = token.fContentStart;
561 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400562 bool sawParam = false;
563 bool multiline = false;
564 bool sawReturn = false;
565 bool sawComment = false;
566 bool methodHasReturn = false;
567 vector<string> methodParams;
568 vector<string> foundParams;
569 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400570 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
571 token.fLineCount);
572 if (MarkType::kMethod == token.fMarkType) {
573 methodName.fName = string(token.fContentStart,
574 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400575 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500576 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400577 && !methodParser.strnchr('~', methodParser.fEnd);
578 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
579 const char* nextEnd = paren;
580 do {
581 string paramName;
582 methodParser.fChar = nextEnd + 1;
583 methodParser.skipSpace();
584 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
585 continue;
586 }
587 methodParams.push_back(paramName);
588 } while (')' != nextEnd[0]);
589 }
Cary Clark9174bda2017-09-19 17:39:32 -0400590 for (const auto& child : token.fTokens) {
591 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
592 break;
593 }
Cary Clark8032b982017-07-28 11:04:54 -0400594 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400595 if (child.fPrivate) {
596 break;
597 }
Cary Clark8032b982017-07-28 11:04:54 -0400598 if ('@' == child.fContentStart[0]) {
599 TextParser parser(&child);
600 do {
601 parser.next();
602 if (parser.startsWith("param ")) {
603 parser.skipWord("param");
604 const char* parmStart = parser.fChar;
605 parser.skipToSpace();
606 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
607 parser.skipWhiteSpace();
608 do {
609 size_t nextComma = parmName.find(',');
610 string piece;
611 if (string::npos == nextComma) {
612 piece = parmName;
613 parmName = "";
614 } else {
615 piece = parmName.substr(0, nextComma);
616 parmName = parmName.substr(nextComma + 1);
617 }
618 if (sawParam) {
619 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400620 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400621 }
Cary Clark9174bda2017-09-19 17:39:32 -0400622 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400623 } else {
624 if (sawComment) {
625 this->nl();
626 }
627 this->lf(2);
628 }
629 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400630 this->writeTag("Param", piece);
631 this->writeSpace(2);
632 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
633 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400634 sawParam = true;
635 sawComment = false;
636 } while (parmName.length());
637 parser.skipTo(parser.fEnd);
638 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
639 parser.skipWord("return");
640 if ('s' == parser.peek()) {
641 parser.next();
642 }
643 if (sawParam) {
644 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400645 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400646 }
Cary Clark9174bda2017-09-19 17:39:32 -0400647 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400648 }
649 this->checkForMissingParams(methodParams, foundParams);
650 sawParam = false;
651 sawComment = false;
652 multiline = false;
653 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400654 this->writeTag("Return");
655 this->writeSpace(2);
656 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
657 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400658 sawReturn = true;
659 parser.skipTo(parser.fEnd);
660 } else {
661 this->reportError("unexpected doxygen directive");
662 }
663 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400664 } else if (child.length() > 1) {
665 const char* start = child.fContentStart;
666 ptrdiff_t length = child.fContentEnd - start;
667 SkASSERT(length >= 0);
668 while (length && '/' == start[0]) {
669 start += 1;
670 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400671 }
Cary Clark9174bda2017-09-19 17:39:32 -0400672 while (length && '/' == start[length - 1]) {
673 length -= 1;
674 if (length && '*' == start[length - 1]) {
675 length -= 1;
676 }
677 }
678 if (length) {
679 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
680 if (sawParam || sawReturn) {
681 this->indentToColumn(8);
682 }
683 this->writeBlock(length, start);
684 this->writeSpace();
685 sawComment = true;
686 if (sawParam || sawReturn) {
687 multiline = true;
688 }
Cary Clark8032b982017-07-28 11:04:54 -0400689 }
690 }
691 }
692 }
693 if (sawParam || sawReturn) {
694 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400695 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400696 }
Cary Clark9174bda2017-09-19 17:39:32 -0400697 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400698 }
699 if (!sawReturn) {
700 if (!sawParam) {
701 if (sawComment) {
702 this->nl();
703 }
704 this->lf(2);
705 }
706 this->checkForMissingParams(methodParams, foundParams);
707 }
708 if (methodHasReturn != sawReturn) {
709 if (!methodHasReturn) {
710 this->reportError("unexpected doxygen return");
711 } else {
712 if (sawComment) {
713 this->nl();
714 }
715 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400716 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400717 }
718 }
719}
720
Cary Clark9174bda2017-09-19 17:39:32 -0400721void IncludeParser::dumpEnum(const Definition& token) {
722 this->writeTag("Enum", token.fName);
723 this->lf(2);
724 this->writeString("#Code");
725 this->lfAlways(1);
726 this->indentToColumn(4);
727 this->writeString("enum");
728 this->writeSpace();
729 if ("_anonymous" != token.fName.substr(0, 10)) {
730 this->writeString(token.fName);
731 this->writeSpace();
732 }
733 this->writeString("{");
734 this->lfAlways(1);
735 for (auto& child : token.fChildren) {
736 this->indentToColumn(8);
737 this->writeString(child->fName);
738 if (child->length()) {
739 this->writeSpace();
740 this->writeBlock(child->length(), child->fContentStart);
741 }
742 if (',' != fLastChar) {
743 this->writeString(",");
744 }
745 this->lfAlways(1);
746 }
747 this->indentToColumn(4);
748 this->writeString("};");
749 this->lf(1);
750 this->writeString("##");
751 this->lf(2);
752 this->dumpComment(token);
753 for (auto& child : token.fChildren) {
754 // start here;
755 // get comments before
756 // or after const values
757 this->writeString("#Const");
758 this->writeSpace();
759 this->writeString(child->fName);
760 TextParser val(child);
761 if (!val.eof()) {
762 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
763 val.next();
764 val.skipSpace();
765 const char* valEnd = val.anyOf(",\n");
766 if (!valEnd) {
767 valEnd = val.fEnd;
768 }
769 this->writeSpace();
770 this->writeBlock(valEnd - val.fStart, val.fStart);
771 } else {
772 this->writeSpace();
773 this->writeDefinition(*child);
774 }
775 }
776 this->lf(1);
777 for (auto comment : child->fChildren) {
778 if (MarkType::kComment == comment->fMarkType) {
779 TextParser parser(comment);
780 parser.skipExact("*");
781 parser.skipExact("*");
782 while (!parser.eof() && parser.skipWhiteSpace()) {
783 parser.skipExact("*");
784 parser.skipWhiteSpace();
785 const char* start = parser.fChar;
786 parser.skipToEndBracket('\n');
787 this->lf(1);
788 this->writeBlock(parser.fChar - start, start);
789 }
790 }
791 }
792 this->writeEndTag();
793 }
794 this->lf(2);
795}
796
797void IncludeParser::dumpMethod(const Definition& token) {
798 this->writeString("#Method");
799 this->writeSpace();
800 if ("SK_TO_STRING_NONVIRT" == token.fName) {
801 this->writeString("void toString(SkString* str) const;");
802 this->lf(2);
803 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
804 this->lf(2);
805 this->writeTag("Private");
806 this->lf(1);
807 this->writeString("macro expands to: void toString(SkString* str) const;");
808 this->writeEndTag();
809 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400810 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400811 "Creates string representation. The representation is read by\n"
812 "internal debugging tools. The interface and implementation may be\n"
813 "suppressed by defining SK_IGNORE_TO_STRING.";
814 this->writeBlock(sizeof(desc) - 1, desc);
815 this->lf(2);
816 this->writeTag("Param", "str");
817 this->writeSpace(2);
818 this->writeString("storage for string representation");
819 this->writeSpace();
820 this->writeString("##");
821 this->lf(2);
822 return;
823 }
824 this->writeBlock(token.length(), token.fStart);
825 this->lf(1);
826 this->dumpComment(token);
827}
828
829void IncludeParser::dumpMember(const Definition& token) {
830 this->writeTag("Member");
831 this->writeSpace();
832 this->writeDefinition(token, token.fName, 2);
833 lf(1);
834 for (auto child : token.fChildren) {
835 this->writeDefinition(*child);
836 }
837 this->writeEndTag();
838 lf(2);
839}
840
Cary Clarkd0530ba2017-09-14 11:25:39 -0400841bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400842 for (const auto& member : fIClassMap) {
843 if (string::npos != member.first.find("::")) {
844 continue;
845 }
846 if (!this->dumpTokens(dir, member.first)) {
847 return false;
848 }
849 }
850 return true;
851}
852
Ben Wagner63fd7602017-10-09 15:45:33 -0400853 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400854bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400855 string fileName = dir;
856 if (dir.length() && '/' != dir[dir.length() - 1]) {
857 fileName += '/';
858 }
859 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400860 fOut = fopen(fileName.c_str(), "wb");
861 if (!fOut) {
862 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400863 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400864 }
865 string prefixName = skClassName.substr(0, 2);
866 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
867 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400868 this->writeTagNoLF("Topic", topicName);
869 this->writeTag("Alias", topicName + "_Reference");
870 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400871 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500872 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
873 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400874 this->writeTag(containerType, skClassName);
875 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400876 auto& tokens = classMap.fTokens;
877 for (auto& token : tokens) {
878 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
879 continue;
880 }
Cary Clark9174bda2017-09-19 17:39:32 -0400881 this->writeDefinition(token);
882 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400883 }
884 this->lf(2);
885 string className(skClassName.substr(2));
886 vector<string> sortedClasses;
887 size_t maxLen = 0;
888 for (const auto& oneClass : fIClassMap) {
889 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
890 continue;
891 }
892 string structName = oneClass.first.substr(skClassName.length() + 2);
893 maxLen = SkTMax(maxLen, structName.length());
894 sortedClasses.emplace_back(structName);
895 }
Cary Clark9174bda2017-09-19 17:39:32 -0400896 this->writeTag("Topic", "Overview");
897 this->lf(2);
898 this->writeTag("Subtopic", "Subtopics");
899 this->writeEndTag("ToDo", "manually add subtopics");
900 this->writeTableHeader("topics", 0, "description");
901 this->writeTableTrailer();
902 this->writeEndTag();
903 this->lf(2);
904 if (maxLen) {
905 this->writeTag("Subtopic", "Structs");
906 this->writeTableHeader("description", maxLen, "struct");
907 for (auto& name : sortedClasses) {
908 this->writeTableRow(maxLen, name);
909 }
910 this->writeTableTrailer();
911 this->writeEndTag("Subtopic");
912 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400913 }
Cary Clark8032b982017-07-28 11:04:54 -0400914 maxLen = 0;
Cary Clark9174bda2017-09-19 17:39:32 -0400915 size_t constructorMax = 0;
916 size_t operatorMax = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400917 vector<string> sortedNames;
Cary Clark9174bda2017-09-19 17:39:32 -0400918 vector<string> constructorNames;
919 vector<string> operatorNames;
Cary Clark8032b982017-07-28 11:04:54 -0400920 for (const auto& token : classMap.fTokens) {
921 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
922 continue;
923 }
Cary Clark9174bda2017-09-19 17:39:32 -0400924 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400925 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
926 continue;
927 }
Cary Clark9174bda2017-09-19 17:39:32 -0400928 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
929 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
930 constructorMax = SkTMax(constructorMax, name.length());
931 constructorNames.emplace_back(name);
932 continue;
933 }
934 if (name.substr(0, 8) == "operator") {
935 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
936 operatorMax = SkTMax(operatorMax, name.length());
937 operatorNames.emplace_back(name);
938 continue;
939 }
Cary Clark8032b982017-07-28 11:04:54 -0400940 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
941 continue;
942 }
Cary Clark9174bda2017-09-19 17:39:32 -0400943 if ("SK_TO_STRING_NONVIRT" == name) {
944 name = "toString";
945 }
Cary Clark8032b982017-07-28 11:04:54 -0400946 size_t paren = name.find('(');
947 size_t funcLen = string::npos == paren ? name.length() : paren;
948 maxLen = SkTMax(maxLen, funcLen);
949 sortedNames.emplace_back(name);
950 }
Cary Clark9174bda2017-09-19 17:39:32 -0400951 if (constructorMax) {
952 std::sort(constructorNames.begin(), constructorNames.end());
953 this->writeTag("Subtopic", "Constructors");
954 this->writeTableHeader("description", constructorMax, "function");
955 for (auto& name : constructorNames) {
956 this->writeTableRow(constructorMax, name);
957 }
958 this->writeTableTrailer();
959 this->writeEndTag("Subtopic");
960 this->lf(2);
961 }
962 if (operatorMax) {
963 std::sort(operatorNames.begin(), operatorNames.end());
964 this->writeTag("Subtopic", "Operators");
965 this->writeTableHeader("description", operatorMax, "function");
966 for (auto& name : operatorNames) {
967 this->writeTableRow(operatorMax, name);
968 }
969 this->writeTableTrailer();
970 this->writeEndTag("Subtopic");
971 this->lf(2);
972 }
Cary Clark8032b982017-07-28 11:04:54 -0400973 std::sort(sortedNames.begin(), sortedNames.end());
Cary Clark9174bda2017-09-19 17:39:32 -0400974 this->writeTag("Subtopic", "Member_Functions");
975 this->writeTableHeader("description", maxLen, "function");
Cary Clark8032b982017-07-28 11:04:54 -0400976 for (auto& name : sortedNames) {
977 size_t paren = name.find('(');
978 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark9174bda2017-09-19 17:39:32 -0400979 this->writeTableRow(maxLen, name.substr(0, funcLen));
Cary Clark8032b982017-07-28 11:04:54 -0400980 }
Cary Clark9174bda2017-09-19 17:39:32 -0400981 this->writeTableTrailer();
982 this->writeEndTag("Subtopic");
983 this->lf(2);
984 this->writeEndTag("Topic");
985 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400986
987 for (auto& oneClass : fIClassMap) {
988 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
989 continue;
990 }
991 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -0400992 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -0400993 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -0400994 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500995 KeyWord keyword = oneClass.second.fKeyWord;
996 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
997 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400998 this->writeTag(containerType, innerName);
999 this->lf(2);
1000 this->writeTag("Code");
1001 this->writeEndTag("ToDo", "fill this in manually");
1002 this->writeEndTag();
1003 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001004 for (auto& token : oneClass.second.fTokens) {
1005 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1006 continue;
1007 }
Cary Clark9174bda2017-09-19 17:39:32 -04001008 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001009 }
1010 this->lf(2);
1011 this->dumpClassTokens(oneClass.second);
1012 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001013 this->writeEndTag(containerType, innerName);
1014 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001015 }
1016 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001017 this->writeEndTag(containerType, skClassName);
1018 this->lf(2);
1019 this->writeEndTag("Topic", topicName);
1020 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001021 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001022 SkDebugf("wrote %s\n", fileName.c_str());
1023 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001024}
1025
1026bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1027 // add comment preceding class, if any
1028 const Definition* parent = includeDef.fParent;
1029 int index = includeDef.fParentIndex;
1030 auto wordIter = parent->fTokens.begin();
1031 std::advance(wordIter, index);
1032 SkASSERT(&*wordIter == &includeDef);
1033 while (parent->fTokens.begin() != wordIter) {
1034 auto testIter = std::prev(wordIter);
1035 if (Definition::Type::kWord != testIter->fType
1036 && Definition::Type::kKeyWord != testIter->fType
1037 && (Definition::Type::kBracket != testIter->fType
1038 || Bracket::kAngle != testIter->fBracket)
1039 && (Definition::Type::kPunctuation != testIter->fType
1040 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1041 break;
1042 }
1043 wordIter = testIter;
1044 }
1045 auto commentIter = wordIter;
1046 while (parent->fTokens.begin() != commentIter) {
1047 auto testIter = std::prev(commentIter);
1048 bool isComment = Definition::Type::kBracket == testIter->fType
1049 && (Bracket::kSlashSlash == testIter->fBracket
1050 || Bracket::kSlashStar == testIter->fBracket);
1051 if (!isComment) {
1052 break;
1053 }
1054 commentIter = testIter;
1055 }
1056 while (commentIter != wordIter) {
1057 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1058 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1059 return false;
1060 }
1061 commentIter = std::next(commentIter);
1062 }
1063 return true;
1064}
1065
Cary Clarkbad5ad72017-08-03 17:14:08 -04001066bool IncludeParser::internalName(const Definition& token) const {
1067 return 0 == token.fName.find("internal_")
1068 || 0 == token.fName.find("Internal_")
1069 || 0 == token.fName.find("legacy_")
1070 || 0 == token.fName.find("temporary_")
1071 || 0 == token.fName.find("private_");
1072}
1073
Cary Clark8032b982017-07-28 11:04:54 -04001074// caller calls reportError, so just return false here
1075bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1076 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001077 // parse class header
1078 auto iter = includeDef->fTokens.begin();
1079 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1080 // todo : documentation is ignoring this for now
1081 iter = std::next(iter);
1082 }
1083 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1084 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001085 iter = std::next(iter);
1086 if (iter == includeDef->fTokens.end()) {
1087 return true; // forward declaration only
1088 }
Cary Clark8032b982017-07-28 11:04:54 -04001089 do {
1090 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001091 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001092 }
1093 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1094 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001095 }
Cary Clark8032b982017-07-28 11:04:54 -04001096 } while (static_cast<void>(iter = std::next(iter)), true);
1097 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001098 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001099 }
1100 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1101 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001102 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001103 }
1104 markupDef->fStart = iter->fStart;
1105 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001106 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001107 }
1108// if (1 != includeDef->fChildren.size()) {
1109// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1110// }
1111 includeDef = includeDef->fChildren.front();
1112 iter = includeDef->fTokens.begin();
1113 // skip until public
1114 int publicIndex = 0;
1115 if (IsStruct::kNo == isStruct) {
1116 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1117 size_t publicLen = strlen(publicName);
1118 while (iter != includeDef->fTokens.end()
1119 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1120 || strncmp(iter->fStart, publicName, publicLen))) {
1121 iter = std::next(iter);
1122 ++publicIndex;
1123 }
1124 }
1125 auto childIter = includeDef->fChildren.begin();
1126 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1127 (*childIter)->fPrivate = true;
1128 childIter = std::next(childIter);
1129 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001130 int keyIndex = publicIndex;
1131 KeyWord currentKey = KeyWord::kPublic;
1132 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1133 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001134 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1135 size_t protectedLen = strlen(protectedName);
1136 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1137 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001138 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001139 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001140 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1141 const char* testStart = iter->fStart;
1142 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1143 iter = std::next(iter);
1144 ++keyIndex;
1145 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1146 currentKey = KeyWord::kPublic;
1147 break;
1148 }
1149 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1150 currentKey = KeyWord::kProtected;
1151 break;
1152 }
1153 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1154 currentKey = KeyWord::kPrivate;
1155 break;
1156 }
1157 }
1158 fLastObject = nullptr;
1159 if (KeyWord::kPublic == currentKey) {
1160 if (!this->parseObject(child, markupDef)) {
1161 return false;
1162 }
1163 } else {
1164 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001165 }
Cary Clark73fa9722017-08-29 17:36:51 -04001166 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001167 childIter = std::next(childIter);
1168 }
Cary Clark8032b982017-07-28 11:04:54 -04001169 SkASSERT(fParent->fParent);
1170 fParent = fParent->fParent;
1171 return true;
1172}
1173
1174bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1175 int lineCount, Definition* markupDef) {
1176 TextParser parser(filename, start, end, lineCount);
1177 // parse doxygen if present
1178 if (parser.startsWith("**")) {
1179 parser.next();
1180 parser.next();
1181 parser.skipWhiteSpace();
1182 if ('\\' == parser.peek()) {
1183 parser.next();
1184 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1185 return reportError<bool>("missing object type");
1186 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001187 if (!parser.skipWord(markupDef->fName.c_str()) &&
1188 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001189 return reportError<bool>("missing object name");
1190 }
1191
1192 }
1193 }
1194 // remove leading '*' if present
1195 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1196 while (!parser.eof() && parser.skipWhiteSpace()) {
1197 while ('*' == parser.peek()) {
1198 parser.next();
1199 if (parser.eof()) {
1200 break;
1201 }
1202 parser.skipWhiteSpace();
1203 }
1204 if (parser.eof()) {
1205 break;
1206 }
1207 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001208 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark8032b982017-07-28 11:04:54 -04001209 parser.fLineCount, parent);
1210 parser.skipToEndBracket('\n');
1211 }
1212 return true;
1213}
1214
1215bool IncludeParser::parseDefine() {
1216
1217 return true;
1218}
1219
1220bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
1221 string nameStr;
1222 if (child->fTokens.size() > 0) {
1223 auto token = child->fTokens.begin();
1224 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1225 token = token->fTokens.begin();
1226 }
1227 if (Definition::Type::kWord == token->fType) {
1228 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1229 }
1230 }
1231 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1232 child->fLineCount, markupDef);
1233 Definition* markupChild = &markupDef->fTokens.back();
1234 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1235 markupChild->fKeyWord = KeyWord::kEnum;
1236 TextParser enumName(child);
1237 enumName.skipExact("enum ");
Cary Clarkbad5ad72017-08-03 17:14:08 -04001238 if (enumName.skipExact("class ")) {
1239 markupChild->fMarkType = MarkType::kEnumClass;
1240 }
Cary Clark8032b982017-07-28 11:04:54 -04001241 const char* nameStart = enumName.fChar;
1242 enumName.skipToSpace();
Ben Wagner63fd7602017-10-09 15:45:33 -04001243 markupChild->fName = markupDef->fName + "::" +
Cary Clark8032b982017-07-28 11:04:54 -04001244 string(nameStart, (size_t) (enumName.fChar - nameStart));
1245 if (!this->findComments(*child, markupChild)) {
1246 return false;
1247 }
1248 TextParser parser(child);
1249 parser.skipToEndBracket('{');
Cary Clark9174bda2017-09-19 17:39:32 -04001250 parser.next();
Cary Clark8032b982017-07-28 11:04:54 -04001251 const char* dataEnd;
1252 do {
Cary Clark8032b982017-07-28 11:04:54 -04001253 parser.skipWhiteSpace();
1254 if ('}' == parser.peek()) {
1255 break;
1256 }
1257 Definition* comment = nullptr;
1258 // note that comment, if any, can be before or after (on the same line, though) as member
1259 if ('#' == parser.peek()) {
1260 // fixme: handle preprecessor, but just skip it for now
1261 parser.skipToLineStart();
1262 }
1263 while (parser.startsWith("/*") || parser.startsWith("//")) {
1264 parser.next();
1265 const char* start = parser.fChar;
1266 const char* end;
1267 if ('*' == parser.peek()) {
1268 end = parser.strnstr("*/", parser.fEnd);
1269 parser.fChar = end;
1270 parser.next();
1271 parser.next();
1272 } else {
1273 end = parser.trimmedLineEnd();
1274 parser.skipToLineStart();
1275 }
1276 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1277 markupChild);
1278 comment = &markupChild->fTokens.back();
1279 comment->fTerminator = end;
1280 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1281 return false;
1282 }
1283 parser.skipWhiteSpace();
1284 }
1285 parser.skipWhiteSpace();
1286 const char* memberStart = parser.fChar;
1287 if ('}' == memberStart[0]) {
1288 break;
1289 }
Cary Clark9174bda2017-09-19 17:39:32 -04001290 // if there's comment on same the line as member def, output first as if it was before
1291
Cary Clark8032b982017-07-28 11:04:54 -04001292 parser.skipToNonAlphaNum();
1293 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001294 if (parser.eof() || !parser.skipWhiteSpace()) {
1295 return this->reportError<bool>("enum member must end with comma 1");
1296 }
Cary Clark8032b982017-07-28 11:04:54 -04001297 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001298 if ('=' == parser.peek()) {
1299 parser.skipToEndBracket(',');
1300 }
1301 if (parser.eof() || ',' != parser.peek()) {
1302 return this->reportError<bool>("enum member must end with comma 2");
1303 }
1304 dataEnd = parser.fChar;
1305 const char* start = parser.anyOf("/\n");
1306 SkASSERT(start);
1307 parser.skipTo(start);
1308 if ('/' == parser.next()) {
1309 char slashStar = parser.next();
1310 if ('/' == slashStar || '*' == slashStar) {
1311 TextParser::Save save(&parser);
1312 char doxCheck = parser.next();
1313 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1314 save.restore();
1315 }
1316 }
1317 parser.skipWhiteSpace();
1318 const char* commentStart = parser.fChar;
1319 if ('/' == slashStar) {
1320 parser.skipToEndBracket('\n');
1321 } else {
1322 parser.skipToEndBracket("*/");
1323 }
1324 SkASSERT(!parser.eof());
1325 const char* commentEnd = parser.fChar;
1326 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
1327 parser.fLineCount, markupChild);
1328 comment = &markupChild->fTokens.back();
1329 comment->fTerminator = commentEnd;
1330 }
Cary Clark8032b982017-07-28 11:04:54 -04001331 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1332 markupChild);
1333 Definition* member = &markupChild->fTokens.back();
1334 member->fName = memberName;
1335 if (comment) {
1336 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001337 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001338 }
1339 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001340 } while (true);
Cary Clark884dd7d2017-10-11 10:37:52 -04001341 for (auto count : child->fChildren) {
1342 if (Definition::Type::kBracket == count->fType) {
1343 continue;
1344 }
1345 SkASSERT(Definition::Type::kKeyWord == count->fType);
1346 if (KeyWord::kClass == count->fKeyWord) {
1347 continue;
1348 }
1349 SkASSERT(KeyWord::kStatic == count->fKeyWord);
1350 markupChild->fTokens.emplace_back(MarkType::kMember, count->fContentStart,
1351 count->fContentEnd, count->fLineCount, markupChild);
1352 Definition* member = &markupChild->fTokens.back();
1353 member->fName = count->fName;
1354 // FIXME: ? add comment as well ?
1355 markupChild->fChildren.push_back(member);
1356 break;
1357 }
Cary Clark8032b982017-07-28 11:04:54 -04001358 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1359 SkASSERT(classDef.fStart);
1360 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1361 markupChild->fName = uniqueName;
1362 classDef.fEnums[uniqueName] = markupChild;
1363 return true;
1364}
1365
1366bool IncludeParser::parseInclude(const string& name) {
1367 fParent = &fIncludeMap[name];
1368 fParent->fName = name;
1369 fParent->fFileName = fFileName;
1370 fParent->fType = Definition::Type::kFileType;
1371 fParent->fContentStart = fChar;
1372 fParent->fContentEnd = fEnd;
1373 // parse include file into tree
1374 while (fChar < fEnd) {
1375 if (!this->parseChar()) {
1376 return false;
1377 }
1378 }
1379 // parse tree and add named objects to maps
1380 fParent = &fIncludeMap[name];
1381 if (!this->parseObjects(fParent, nullptr)) {
1382 return false;
1383 }
1384 return true;
1385}
1386
1387bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1388 const char* typeStart = child->fChildren[0]->fContentStart;
1389 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1390 child->fLineCount, markupDef);
1391 Definition* markupChild = &markupDef->fTokens.back();
1392 TextParser nameParser(child);
1393 nameParser.skipToNonAlphaNum();
1394 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1395 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1396 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1397 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001398 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001399 classDef.fMembers[uniqueName] = markupChild;
1400 if (child->fParentIndex >= 2) {
1401 auto comment = child->fParent->fTokens.begin();
1402 std::advance(comment, child->fParentIndex - 2);
1403 if (Definition::Type::kBracket == comment->fType
1404 && (Bracket::kSlashStar == comment->fBracket
1405 || Bracket::kSlashSlash == comment->fBracket)) {
1406 TextParser parser(&*comment);
1407 do {
1408 parser.skipToAlpha();
1409 if (parser.eof()) {
1410 break;
1411 }
1412 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001413 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001414 if (Bracket::kSlashStar == comment->fBracket) {
1415 const char* commentEnd = parser.strnstr("*/", end);
1416 if (commentEnd) {
1417 end = commentEnd;
1418 }
1419 }
1420 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1421 markupDef);
1422 Definition* commentChild = &markupDef->fTokens.back();
1423 markupChild->fChildren.emplace_back(commentChild);
1424 parser.skipTo(end);
1425 } while (!parser.eof());
1426 }
1427 }
1428 return true;
1429}
1430
1431bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1432 auto tokenIter = child->fParent->fTokens.begin();
1433 std::advance(tokenIter, child->fParentIndex);
1434 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001435 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001436 bool addConst = false;
1437 auto operatorCheck = tokenIter;
1438 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1439 operatorCheck = std::prev(tokenIter);
1440 }
1441 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001442 auto closeParen = std::next(tokenIter);
1443 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1444 '(' == closeParen->fContentStart[0]);
1445 nameEnd = closeParen->fContentEnd + 1;
1446 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001447 if (Definition::Type::kKeyWord == closeParen->fType &&
1448 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001449 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001450 }
Cary Clarka560c472017-11-27 10:44:06 -05001451 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001452 }
1453 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001454 if (addConst) {
1455 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001456 }
Cary Clark8032b982017-07-28 11:04:54 -04001457 while (tokenIter != child->fParent->fTokens.begin()) {
1458 auto testIter = std::prev(tokenIter);
1459 switch (testIter->fType) {
1460 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001461 if (testIter == child->fParent->fTokens.begin() &&
1462 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1463 KeyWord::kIfndef == child->fParent->fKeyWord ||
1464 KeyWord::kIf == child->fParent->fKeyWord)) {
1465 std::next(tokenIter);
1466 break;
1467 }
Cary Clark8032b982017-07-28 11:04:54 -04001468 goto keepGoing;
1469 case Definition::Type::kKeyWord: {
1470 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1471 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1472 goto keepGoing;
1473 }
1474 } break;
1475 case Definition::Type::kBracket:
1476 if (Bracket::kAngle == testIter->fBracket) {
1477 goto keepGoing;
1478 }
1479 break;
1480 case Definition::Type::kPunctuation:
1481 if (Punctuation::kSemicolon == testIter->fPunctuation
1482 || Punctuation::kLeftBrace == testIter->fPunctuation
1483 || Punctuation::kColon == testIter->fPunctuation) {
1484 break;
1485 }
1486 keepGoing:
1487 tokenIter = testIter;
1488 continue;
1489 default:
1490 break;
1491 }
1492 break;
1493 }
1494 tokenIter->fName = nameStr;
1495 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001496 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001497 auto testIter = child->fParent->fTokens.begin();
1498 SkASSERT(child->fParentIndex > 0);
1499 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001500 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1501 0 == tokenIter->fParentIndex) {
1502 tokenIter = std::next(tokenIter);
1503 }
Cary Clark8032b982017-07-28 11:04:54 -04001504 const char* start = tokenIter->fContentStart;
1505 const char* end = tokenIter->fContentEnd;
1506 const char kDebugCodeStr[] = "SkDEBUGCODE";
1507 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1508 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1509 std::advance(testIter, 1);
1510 start = testIter->fContentStart + 1;
1511 end = testIter->fContentEnd - 1;
1512 } else {
1513 end = testIter->fContentEnd;
1514 while (testIter != child->fParent->fTokens.end()) {
1515 testIter = std::next(testIter);
1516 switch (testIter->fType) {
1517 case Definition::Type::kPunctuation:
1518 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1519 || Punctuation::kLeftBrace == testIter->fPunctuation
1520 || Punctuation::kColon == testIter->fPunctuation);
1521 end = testIter->fStart;
1522 break;
1523 case Definition::Type::kKeyWord: {
1524 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1525 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1526 continue;
1527 }
1528 } break;
1529 default:
1530 continue;
1531 }
1532 break;
1533 }
1534 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001535 while (end > start && ' ' >= end[-1]) {
1536 --end;
1537 }
Cary Clark9174bda2017-09-19 17:39:32 -04001538 if (!markupDef) {
1539 auto parentIter = child->fParent->fTokens.begin();
1540 SkASSERT(child->fParentIndex > 0);
1541 std::advance(parentIter, child->fParentIndex - 1);
1542 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001543 TextParser nameParser(methodName);
1544 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001545 return true; // expect this is inline class definition outside of class
1546 }
Cary Clarka560c472017-11-27 10:44:06 -05001547 string name(nameParser.fLine, nameParser.lineLength());
1548 auto finder = fIFunctionMap.find(name);
1549 if (fIFunctionMap.end() != finder) {
1550 // create unique name
1551 SkASSERT(0); // incomplete
1552 }
1553 auto globalFunction = &fIFunctionMap[name];
1554 globalFunction->fContentStart = start;
1555 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001556 globalFunction->fFiddle = name;
1557 globalFunction->fContentEnd = end;
1558 globalFunction->fMarkType = MarkType::kMethod;
1559 globalFunction->fLineCount = tokenIter->fLineCount;
1560 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001561 }
Cary Clark8032b982017-07-28 11:04:54 -04001562 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1563 markupDef);
1564 Definition* markupChild = &markupDef->fTokens.back();
1565 // do find instead -- I wonder if there is a way to prevent this in c++
1566 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1567 SkASSERT(classDef.fStart);
1568 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1569 markupChild->fName = uniqueName;
1570 if (!this->findComments(*child, markupChild)) {
1571 return false;
1572 }
1573 classDef.fMethods[uniqueName] = markupChild;
1574 return true;
1575}
1576
Cary Clark8032b982017-07-28 11:04:54 -04001577bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1578 for (auto& child : parent->fChildren) {
1579 if (!this->parseObject(child, markupDef)) {
1580 return false;
1581 }
1582 }
1583 return true;
1584}
1585
1586bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1587 // set up for error reporting
1588 fLine = fChar = child->fStart;
1589 fEnd = child->fContentEnd;
1590 // todo: put original line number in child as well
1591 switch (child->fType) {
1592 case Definition::Type::kKeyWord:
1593 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001594 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001595 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001596 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001597 }
1598 break;
1599 case KeyWord::kEnum:
1600 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001601 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001602 }
1603 break;
1604 case KeyWord::kStruct:
1605 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001606 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001607 }
1608 break;
1609 case KeyWord::kTemplate:
1610 if (!this->parseTemplate()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001611 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001612 }
1613 break;
1614 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001615 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001616 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001617 }
1618 break;
1619 case KeyWord::kUnion:
1620 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001621 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001622 }
1623 break;
1624 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001625 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001626 }
1627 break;
1628 case Definition::Type::kBracket:
1629 switch (child->fBracket) {
1630 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001631 if (fLastObject) {
1632 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1633 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001634 if (!checkDeprecated.eof()) {
1635 checkDeprecated.skipWhiteSpace();
1636 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1637 break;
1638 }
1639 }
1640 }
1641 {
1642 auto tokenIter = child->fParent->fTokens.begin();
1643 std::advance(tokenIter, child->fParentIndex);
1644 tokenIter = std::prev(tokenIter);
1645 TextParser checkDeprecated(&*tokenIter);
Cary Clark73fa9722017-08-29 17:36:51 -04001646 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1647 break;
1648 }
1649 }
Cary Clark8032b982017-07-28 11:04:54 -04001650 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001651 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001652 }
Cary Clark73fa9722017-08-29 17:36:51 -04001653 break;
Cary Clark8032b982017-07-28 11:04:54 -04001654 case Bracket::kSlashSlash:
1655 case Bracket::kSlashStar:
1656 // comments are picked up by parsing objects first
1657 break;
1658 case Bracket::kPound:
1659 // special-case the #xxx xxx_DEFINED entries
1660 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001661 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001662 case KeyWord::kIfndef:
1663 case KeyWord::kIfdef:
1664 if (child->boilerplateIfDef(fParent)) {
1665 if (!this->parseObjects(child, markupDef)) {
1666 return false;
1667 }
1668 break;
1669 }
1670 goto preproError;
1671 case KeyWord::kDefine:
1672 if (child->boilerplateDef(fParent)) {
1673 break;
1674 }
1675 goto preproError;
1676 case KeyWord::kEndif:
1677 if (child->boilerplateEndIf()) {
1678 break;
1679 }
1680 case KeyWord::kInclude:
1681 // ignored for now
1682 break;
1683 case KeyWord::kElse:
1684 case KeyWord::kElif:
1685 // todo: handle these
1686 break;
1687 default:
1688 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001689 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001690 }
1691 break;
1692 case Bracket::kAngle:
1693 // pick up templated function pieces when method is found
1694 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001695 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001696 if (!this->parseObjects(child, markupDef)) {
1697 return false;
1698 }
Cary Clark73fa9722017-08-29 17:36:51 -04001699 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001700 case Bracket::kSquare: {
1701 // check to see if parent is operator, the only case we handle so far
1702 auto prev = child->fParent->fTokens.begin();
1703 std::advance(prev, child->fParentIndex - 1);
1704 if (KeyWord::kOperator != prev->fKeyWord) {
1705 return child->reportError<bool>("expected operator overload");
1706 }
1707 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001708 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001709 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001710 }
1711 break;
1712 case Definition::Type::kWord:
1713 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001714 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001715 }
1716 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001717 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001718 }
1719 break;
1720 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001721 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001722 break;
1723 }
1724 return true;
1725}
1726
1727bool IncludeParser::parseTemplate() {
1728
1729 return true;
1730}
1731
Cary Clark2f466242017-12-11 16:03:17 -05001732bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1733 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05001734 typedefParser.skipExact("typedef");
1735 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05001736 string nameStr = typedefParser.typedefName();
1737 if (!markupDef) {
1738 Definition& typedefDef = fITypedefMap[nameStr];
1739 SkASSERT(!typedefDef.fStart);
1740 typedefDef.fStart = child->fContentStart;
1741 typedefDef.fContentStart = child->fContentStart;
1742 typedefDef.fName = nameStr;
1743 typedefDef.fFiddle = nameStr;
1744 typedefDef.fContentEnd = child->fContentEnd;
1745 typedefDef.fTerminator = child->fContentEnd;
1746 typedefDef.fMarkType = MarkType::kTypedef;
1747 typedefDef.fLineCount = child->fLineCount;
1748 return true;
1749 }
1750 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
1751 child->fLineCount, markupDef);
1752 Definition* markupChild = &markupDef->fTokens.back();
1753 markupChild->fName = nameStr;
1754 markupChild->fTerminator = markupChild->fContentEnd;
1755 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1756 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001757 return true;
1758}
1759
1760bool IncludeParser::parseUnion() {
1761
1762 return true;
1763}
1764
1765bool IncludeParser::parseChar() {
1766 char test = *fChar;
1767 if ('\\' == fPrev) {
1768 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001769// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001770 fLine = fChar + 1;
1771 }
1772 goto done;
1773 }
1774 switch (test) {
1775 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001776// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001777 fLine = fChar + 1;
1778 if (fInChar) {
1779 return reportError<bool>("malformed char");
1780 }
1781 if (fInString) {
1782 return reportError<bool>("malformed string");
1783 }
1784 if (!this->checkForWord()) {
1785 return false;
1786 }
1787 if (Bracket::kPound == this->topBracket()) {
1788 KeyWord keyWord = fParent->fKeyWord;
1789 if (KeyWord::kNone == keyWord) {
1790 return this->reportError<bool>("unhandled preprocessor directive");
1791 }
1792 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) {
1793 this->popBracket();
1794 }
1795 } else if (Bracket::kSlashSlash == this->topBracket()) {
1796 this->popBracket();
1797 }
1798 break;
1799 case '*':
1800 if (!fInCharCommentString && '/' == fPrev) {
1801 this->pushBracket(Bracket::kSlashStar);
1802 }
1803 if (!this->checkForWord()) {
1804 return false;
1805 }
1806 if (!fInCharCommentString) {
1807 this->addPunctuation(Punctuation::kAsterisk);
1808 }
1809 break;
1810 case '/':
1811 if ('*' == fPrev) {
1812 if (!fInCharCommentString) {
1813 return reportError<bool>("malformed closing comment");
1814 }
1815 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001816 TextParser::Save save(this);
1817 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001818 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001819 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001820 }
1821 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001822 }
Cary Clark8032b982017-07-28 11:04:54 -04001823 if (!fInCharCommentString && '/' == fPrev) {
1824 this->pushBracket(Bracket::kSlashSlash);
1825 break;
1826 }
1827 if (!this->checkForWord()) {
1828 return false;
1829 }
1830 break;
1831 case '\'':
1832 if (Bracket::kChar == this->topBracket()) {
1833 this->popBracket();
1834 } else if (!fInComment && !fInString) {
1835 if (fIncludeWord) {
1836 return this->reportError<bool>("word then single-quote");
1837 }
1838 this->pushBracket(Bracket::kChar);
1839 }
1840 break;
1841 case '\"':
1842 if (Bracket::kString == this->topBracket()) {
1843 this->popBracket();
1844 } else if (!fInComment && !fInChar) {
1845 if (fIncludeWord) {
1846 return this->reportError<bool>("word then double-quote");
1847 }
1848 this->pushBracket(Bracket::kString);
1849 }
1850 break;
1851 case ':':
1852 case '(':
1853 case '[':
1854 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001855 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1856 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1857 this->pushBracket(Bracket::kDebugCode);
1858 break;
1859 }
Cary Clark8032b982017-07-28 11:04:54 -04001860 if (fInCharCommentString) {
1861 break;
1862 }
1863 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1864 break;
1865 }
1866 if (!fInBrace) {
1867 if (!this->checkForWord()) {
1868 return false;
1869 }
1870 if (':' == test && !fInFunction) {
1871 break;
1872 }
1873 if ('{' == test) {
1874 this->addPunctuation(Punctuation::kLeftBrace);
1875 } else if (':' == test) {
1876 this->addPunctuation(Punctuation::kColon);
1877 }
1878 }
1879 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1880 && Bracket::kColon == fInBrace->fBracket) {
1881 Definition* braceParent = fParent->fParent;
1882 braceParent->fChildren.pop_back();
1883 braceParent->fTokens.pop_back();
1884 fParent = braceParent;
1885 fInBrace = nullptr;
1886 }
1887 this->pushBracket(
1888 '(' == test ? Bracket::kParen :
1889 '[' == test ? Bracket::kSquare :
1890 '{' == test ? Bracket::kBrace :
1891 Bracket::kColon);
1892 if (!fInBrace
1893 && ('{' == test || (':' == test && ' ' >= fChar[1]))
1894 && fInFunction) {
1895 fInBrace = fParent;
1896 }
1897 } break;
1898 case '<':
1899 if (fInCharCommentString || fInBrace) {
1900 break;
1901 }
1902 if (!this->checkForWord()) {
1903 return false;
1904 }
1905 if (fInEnum) {
1906 break;
1907 }
1908 this->pushBracket(Bracket::kAngle);
1909 break;
1910 case ')':
1911 case ']':
1912 case '}': {
1913 if (fInCharCommentString) {
1914 break;
1915 }
1916 if (!fInBrace) {
1917 if (!this->checkForWord()) {
1918 return false;
1919 }
1920 }
1921 bool popBraceParent = fInBrace == fParent;
1922 if ((')' == test ? Bracket::kParen :
1923 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
1924 this->popBracket();
1925 if (!fInFunction) {
1926 bool deprecatedMacro = false;
1927 if (')' == test) {
1928 auto iter = fParent->fTokens.end();
1929 bool lookForWord = false;
1930 while (fParent->fTokens.begin() != iter) {
1931 --iter;
1932 if (lookForWord) {
1933 if (Definition::Type::kWord != iter->fType) {
1934 break;
1935 }
1936 string word(iter->fContentStart, iter->length());
1937 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
1938 deprecatedMacro = true;
1939 // remove macro paren (would confuse method parsing later)
Ben Wagner63fd7602017-10-09 15:45:33 -04001940 fParent->fTokens.pop_back();
Cary Clark8032b982017-07-28 11:04:54 -04001941 fParent->fChildren.pop_back();
1942 }
1943 break;
1944 }
1945 if (Definition::Type::kBracket != iter->fType) {
1946 break;
1947 }
1948 if (Bracket::kParen != iter->fBracket) {
1949 break;
1950 }
1951 lookForWord = true;
1952 }
1953 }
1954 fInFunction = ')' == test && !deprecatedMacro;
1955 } else {
1956 fInFunction = '}' != test;
1957 }
Cary Clark73fa9722017-08-29 17:36:51 -04001958 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
1959 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04001960 } else {
1961 return reportError<bool>("malformed close bracket");
1962 }
1963 if (popBraceParent) {
1964 Definition* braceParent = fInBrace->fParent;
1965 braceParent->fChildren.pop_back();
1966 braceParent->fTokens.pop_back();
1967 fInBrace = nullptr;
1968 }
1969 } break;
1970 case '>':
1971 if (fInCharCommentString || fInBrace) {
1972 break;
1973 }
1974 if (!this->checkForWord()) {
1975 return false;
1976 }
1977 if (fInEnum) {
1978 break;
1979 }
Cary Clarka560c472017-11-27 10:44:06 -05001980 if (Bracket::kPound == this->topBracket()) {
1981 break;
1982 }
Cary Clark8032b982017-07-28 11:04:54 -04001983 if (Bracket::kAngle == this->topBracket()) {
1984 this->popBracket();
1985 } else {
1986 return reportError<bool>("malformed close angle bracket");
1987 }
1988 break;
1989 case '#': {
1990 if (fInCharCommentString || fInBrace) {
1991 break;
1992 }
1993 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
1994 this->pushBracket(Bracket::kPound);
1995 break;
1996 }
1997 case '&':
1998 case ',':
1999 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05002000 case '+':
2001 case '=':
2002 case '-':
2003 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002004 if (fInCharCommentString || fInBrace) {
2005 break;
2006 }
2007 if (!this->checkForWord()) {
2008 return false;
2009 }
2010 break;
2011 case ';':
2012 if (fInCharCommentString || fInBrace) {
2013 break;
2014 }
2015 if (!this->checkForWord()) {
2016 return false;
2017 }
2018 if (Definition::Type::kKeyWord == fParent->fType
2019 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002020 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
2021 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2022 this->popObject();
2023 }
Cary Clark8032b982017-07-28 11:04:54 -04002024 if (KeyWord::kEnum == fParent->fKeyWord) {
2025 fInEnum = false;
2026 }
2027 this->popObject();
2028 } else if (Definition::Type::kBracket == fParent->fType
2029 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2030 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2031 list<Definition>::iterator baseIter = fParent->fTokens.end();
2032 list<Definition>::iterator namedIter = fParent->fTokens.end();
2033 for (auto tokenIter = fParent->fTokens.end();
2034 fParent->fTokens.begin() != tokenIter--; ) {
2035 if (tokenIter->fLineCount == fLineCount) {
2036 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2037 if (namedIter != fParent->fTokens.end()) {
2038 return reportError<bool>("found two named member tokens");
2039 }
2040 namedIter = tokenIter;
2041 }
2042 baseIter = tokenIter;
2043 } else {
2044 break;
2045 }
2046 }
2047 // FIXME: if a member definition spans multiple lines, this won't work
2048 if (namedIter != fParent->fTokens.end()) {
2049 if (baseIter == namedIter) {
2050 return this->reportError<bool>("expected type before named token");
2051 }
2052 Definition* member = &*namedIter;
2053 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002054 if (!member->fTerminator) {
2055 member->fTerminator = member->fContentEnd;
2056 }
Cary Clark8032b982017-07-28 11:04:54 -04002057 fParent->fChildren.push_back(member);
2058 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2059 member->fChildren.push_back(&*nameType);
2060 }
2061
2062 }
2063 } else if (fParent->fChildren.size() > 0) {
2064 auto lastIter = fParent->fChildren.end();
2065 Definition* priorEnum;
2066 while (fParent->fChildren.begin() != lastIter) {
2067 std::advance(lastIter, -1);
2068 priorEnum = *lastIter;
2069 if (Definition::Type::kBracket != priorEnum->fType ||
2070 (Bracket::kSlashSlash != priorEnum->fBracket
2071 && Bracket::kSlashStar != priorEnum->fBracket)) {
2072 break;
2073 }
2074 }
2075 if (Definition::Type::kKeyWord == priorEnum->fType
2076 && KeyWord::kEnum == priorEnum->fKeyWord) {
2077 auto tokenWalker = fParent->fTokens.begin();
2078 std::advance(tokenWalker, priorEnum->fParentIndex);
2079 SkASSERT(KeyWord::kEnum == tokenWalker->fKeyWord);
2080 while (tokenWalker != fParent->fTokens.end()) {
2081 std::advance(tokenWalker, 1);
2082 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2083 break;
2084 }
2085 }
2086 while (tokenWalker != fParent->fTokens.end()) {
2087 std::advance(tokenWalker, 1);
2088 const Definition* test = &*tokenWalker;
2089 if (Definition::Type::kBracket != test->fType ||
2090 (Bracket::kSlashSlash != test->fBracket
2091 && Bracket::kSlashStar != test->fBracket)) {
2092 break;
2093 }
2094 }
2095 Definition* start = &*tokenWalker;
2096 bool foundExpected = true;
2097 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2098 const Definition* test = &*tokenWalker;
2099 if (expected != test->fKeyWord) {
2100 foundExpected = false;
2101 break;
2102 }
2103 if (tokenWalker == fParent->fTokens.end()) {
2104 break;
2105 }
2106 std::advance(tokenWalker, 1);
2107 }
2108 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2109 const char* nameStart = tokenWalker->fStart;
2110 std::advance(tokenWalker, 1);
2111 if (tokenWalker != fParent->fTokens.end()) {
2112 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2113 tp.skipToNonAlphaNum();
2114 start->fName = string(nameStart, tp.fChar - nameStart);
2115 start->fContentEnd = fChar;
2116 priorEnum->fChildren.emplace_back(start);
2117 }
2118 }
2119 }
2120 }
2121 this->addPunctuation(Punctuation::kSemicolon);
2122 fInFunction = false;
2123 break;
2124 case '~':
2125 if (fInEnum) {
2126 break;
2127 }
2128 case '0': case '1': case '2': case '3': case '4':
2129 case '5': case '6': case '7': case '8': case '9':
2130 // TODO: don't want to parse numbers, but do need to track for enum defs
2131 // break;
2132 case 'A': case 'B': case 'C': case 'D': case 'E':
2133 case 'F': case 'G': case 'H': case 'I': case 'J':
2134 case 'K': case 'L': case 'M': case 'N': case 'O':
2135 case 'P': case 'Q': case 'R': case 'S': case 'T':
2136 case 'U': case 'V': case 'W': case 'X': case 'Y':
2137 case 'Z': case '_':
2138 case 'a': case 'b': case 'c': case 'd': case 'e':
2139 case 'f': case 'g': case 'h': case 'i': case 'j':
2140 case 'k': case 'l': case 'm': case 'n': case 'o':
2141 case 'p': case 'q': case 'r': case 's': case 't':
2142 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002143 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002144 if (fInCharCommentString || fInBrace) {
2145 break;
2146 }
2147 if (!fIncludeWord) {
2148 fIncludeWord = fChar;
2149 }
2150 break;
2151 }
2152done:
2153 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002154 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002155 return true;
2156}
2157
2158void IncludeParser::validate() const {
2159 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2160 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2161 }
2162 IncludeParser::ValidateKeyWords();
2163}