blob: fa5ad325fd004f5ce35ea2d9a67b9a2bcb731842 [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 Clarka560c472017-11-27 10:44:06 -0500461 if (!root->dumpUnVisited(bmhParser.fSkip)) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400462 SkDebugf("some struct elements not found; struct finding in includeParser is missing\n");
Cary Clarkf059e7c2017-12-20 14:53:21 -0500463 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400464 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500465 if (crossChecks) {
466 SkDebugf(".");
467 } else {
468 SkDebugf("cross-check");
469 firstCheck = className;
470 }
471 ++crossChecks;
472 }
473 if (crossChecks) {
474 if (1 == crossChecks) {
475 SkDebugf("%s", firstCheck.c_str());
476 }
477 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400478 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400479 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500480 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400481}
482
483IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
484 const string& name) {
485 string className;
486 const Definition* test = fParent;
487 while (Definition::Type::kFileType != test->fType) {
488 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
489 className = test->fName + "::";
490 break;
491 }
492 test = test->fParent;
493 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400494 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400495 unordered_map<string, IClassDefinition>& map = fIClassMap;
496 IClassDefinition& markupDef = map[className];
497 if (markupDef.fStart) {
498 typedef IClassDefinition* IClassDefPtr;
499 return INHERITED::reportError<IClassDefPtr>("class already defined");
500 }
501 markupDef.fFileName = fFileName;
502 markupDef.fStart = includeDef.fStart;
503 markupDef.fContentStart = includeDef.fStart;
504 markupDef.fName = className;
505 markupDef.fContentEnd = includeDef.fContentEnd;
506 markupDef.fTerminator = includeDef.fTerminator;
507 markupDef.fParent = fParent;
508 markupDef.fLineCount = fLineCount;
509 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
510 MarkType::kStruct : MarkType::kClass;
511 markupDef.fKeyWord = includeDef.fKeyWord;
512 markupDef.fType = Definition::Type::kMark;
513 fParent = &markupDef;
514 return &markupDef;
515}
516
517void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
518 auto& tokens = classDef.fTokens;
519 for (auto& token : tokens) {
520 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
521 continue;
522 }
523 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400524 this->writeString(
525 "# ------------------------------------------------------------------------------");
526 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400527 }
528 switch (token.fMarkType) {
529 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500530 case MarkType::kEnumClass:
Cary Clark9174bda2017-09-19 17:39:32 -0400531 this->dumpEnum(token);
Cary Clark8032b982017-07-28 11:04:54 -0400532 break;
533 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400534 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400535 break;
536 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400537 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400538 continue;
539 break;
540 default:
541 SkASSERT(0);
542 }
543 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400544 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400545 this->lf(1);
546 this->writeString("// incomplete");
547 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400548 this->writeEndTag();
549 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400550 this->writeTag("SeeAlso");
551 this->writeSpace();
552 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400553 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500554 switch (token.fMarkType) {
555 case MarkType::kEnum:
556 case MarkType::kEnumClass:
557 this->writeEndTag("Enum");
558 break;
559 case MarkType::kMethod:
560 this->writeEndTag("Method");
561 break;
562 case MarkType::kMember:
563 this->writeEndTag("Member");
564 continue;
565 break;
566 default:
567 SkASSERT(0);
568 }
Cary Clark9174bda2017-09-19 17:39:32 -0400569 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400570 }
571}
Cary Clark9174bda2017-09-19 17:39:32 -0400572void IncludeParser::dumpComment(const Definition& token) {
573 fLineCount = token.fLineCount;
574 fChar = fLine = token.fContentStart;
575 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400576 bool sawParam = false;
577 bool multiline = false;
578 bool sawReturn = false;
579 bool sawComment = false;
580 bool methodHasReturn = false;
581 vector<string> methodParams;
582 vector<string> foundParams;
583 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400584 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
585 token.fLineCount);
586 if (MarkType::kMethod == token.fMarkType) {
587 methodName.fName = string(token.fContentStart,
588 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400589 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500590 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400591 && !methodParser.strnchr('~', methodParser.fEnd);
592 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
593 const char* nextEnd = paren;
594 do {
595 string paramName;
596 methodParser.fChar = nextEnd + 1;
597 methodParser.skipSpace();
598 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
599 continue;
600 }
601 methodParams.push_back(paramName);
602 } while (')' != nextEnd[0]);
603 }
Cary Clark9174bda2017-09-19 17:39:32 -0400604 for (const auto& child : token.fTokens) {
605 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
606 break;
607 }
Cary Clark8032b982017-07-28 11:04:54 -0400608 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400609 if (child.fPrivate) {
610 break;
611 }
Cary Clark8032b982017-07-28 11:04:54 -0400612 if ('@' == child.fContentStart[0]) {
613 TextParser parser(&child);
614 do {
615 parser.next();
616 if (parser.startsWith("param ")) {
617 parser.skipWord("param");
618 const char* parmStart = parser.fChar;
619 parser.skipToSpace();
620 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
621 parser.skipWhiteSpace();
622 do {
623 size_t nextComma = parmName.find(',');
624 string piece;
625 if (string::npos == nextComma) {
626 piece = parmName;
627 parmName = "";
628 } else {
629 piece = parmName.substr(0, nextComma);
630 parmName = parmName.substr(nextComma + 1);
631 }
632 if (sawParam) {
633 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400634 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400635 }
Cary Clark9174bda2017-09-19 17:39:32 -0400636 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400637 } else {
638 if (sawComment) {
639 this->nl();
640 }
641 this->lf(2);
642 }
643 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400644 this->writeTag("Param", piece);
645 this->writeSpace(2);
646 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
647 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400648 sawParam = true;
649 sawComment = false;
650 } while (parmName.length());
651 parser.skipTo(parser.fEnd);
652 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
653 parser.skipWord("return");
654 if ('s' == parser.peek()) {
655 parser.next();
656 }
657 if (sawParam) {
658 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400659 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400660 }
Cary Clark9174bda2017-09-19 17:39:32 -0400661 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400662 }
663 this->checkForMissingParams(methodParams, foundParams);
664 sawParam = false;
665 sawComment = false;
666 multiline = false;
667 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400668 this->writeTag("Return");
669 this->writeSpace(2);
670 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
671 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400672 sawReturn = true;
673 parser.skipTo(parser.fEnd);
674 } else {
675 this->reportError("unexpected doxygen directive");
676 }
677 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400678 } else if (child.length() > 1) {
679 const char* start = child.fContentStart;
680 ptrdiff_t length = child.fContentEnd - start;
681 SkASSERT(length >= 0);
682 while (length && '/' == start[0]) {
683 start += 1;
684 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400685 }
Cary Clark9174bda2017-09-19 17:39:32 -0400686 while (length && '/' == start[length - 1]) {
687 length -= 1;
688 if (length && '*' == start[length - 1]) {
689 length -= 1;
690 }
691 }
692 if (length) {
693 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
694 if (sawParam || sawReturn) {
695 this->indentToColumn(8);
696 }
697 this->writeBlock(length, start);
698 this->writeSpace();
699 sawComment = true;
700 if (sawParam || sawReturn) {
701 multiline = true;
702 }
Cary Clark8032b982017-07-28 11:04:54 -0400703 }
704 }
705 }
706 }
707 if (sawParam || sawReturn) {
708 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400709 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400710 }
Cary Clark9174bda2017-09-19 17:39:32 -0400711 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400712 }
713 if (!sawReturn) {
714 if (!sawParam) {
715 if (sawComment) {
716 this->nl();
717 }
718 this->lf(2);
719 }
720 this->checkForMissingParams(methodParams, foundParams);
721 }
722 if (methodHasReturn != sawReturn) {
723 if (!methodHasReturn) {
724 this->reportError("unexpected doxygen return");
725 } else {
726 if (sawComment) {
727 this->nl();
728 }
729 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400730 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400731 }
732 }
733}
734
Cary Clark9174bda2017-09-19 17:39:32 -0400735void IncludeParser::dumpEnum(const Definition& token) {
736 this->writeTag("Enum", token.fName);
737 this->lf(2);
738 this->writeString("#Code");
739 this->lfAlways(1);
740 this->indentToColumn(4);
741 this->writeString("enum");
742 this->writeSpace();
743 if ("_anonymous" != token.fName.substr(0, 10)) {
744 this->writeString(token.fName);
745 this->writeSpace();
746 }
747 this->writeString("{");
748 this->lfAlways(1);
749 for (auto& child : token.fChildren) {
750 this->indentToColumn(8);
751 this->writeString(child->fName);
752 if (child->length()) {
753 this->writeSpace();
754 this->writeBlock(child->length(), child->fContentStart);
755 }
756 if (',' != fLastChar) {
757 this->writeString(",");
758 }
759 this->lfAlways(1);
760 }
761 this->indentToColumn(4);
762 this->writeString("};");
763 this->lf(1);
764 this->writeString("##");
765 this->lf(2);
766 this->dumpComment(token);
767 for (auto& child : token.fChildren) {
768 // start here;
769 // get comments before
770 // or after const values
771 this->writeString("#Const");
772 this->writeSpace();
773 this->writeString(child->fName);
774 TextParser val(child);
775 if (!val.eof()) {
776 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
777 val.next();
778 val.skipSpace();
779 const char* valEnd = val.anyOf(",\n");
780 if (!valEnd) {
781 valEnd = val.fEnd;
782 }
783 this->writeSpace();
784 this->writeBlock(valEnd - val.fStart, val.fStart);
785 } else {
786 this->writeSpace();
787 this->writeDefinition(*child);
788 }
789 }
790 this->lf(1);
791 for (auto comment : child->fChildren) {
792 if (MarkType::kComment == comment->fMarkType) {
793 TextParser parser(comment);
794 parser.skipExact("*");
795 parser.skipExact("*");
796 while (!parser.eof() && parser.skipWhiteSpace()) {
797 parser.skipExact("*");
798 parser.skipWhiteSpace();
799 const char* start = parser.fChar;
800 parser.skipToEndBracket('\n');
801 this->lf(1);
802 this->writeBlock(parser.fChar - start, start);
803 }
804 }
805 }
806 this->writeEndTag();
807 }
808 this->lf(2);
809}
810
811void IncludeParser::dumpMethod(const Definition& token) {
812 this->writeString("#Method");
813 this->writeSpace();
814 if ("SK_TO_STRING_NONVIRT" == token.fName) {
815 this->writeString("void toString(SkString* str) const;");
816 this->lf(2);
817 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
818 this->lf(2);
819 this->writeTag("Private");
820 this->lf(1);
821 this->writeString("macro expands to: void toString(SkString* str) const;");
822 this->writeEndTag();
823 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400824 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400825 "Creates string representation. The representation is read by\n"
826 "internal debugging tools. The interface and implementation may be\n"
827 "suppressed by defining SK_IGNORE_TO_STRING.";
828 this->writeBlock(sizeof(desc) - 1, desc);
829 this->lf(2);
830 this->writeTag("Param", "str");
831 this->writeSpace(2);
832 this->writeString("storage for string representation");
833 this->writeSpace();
834 this->writeString("##");
835 this->lf(2);
836 return;
837 }
838 this->writeBlock(token.length(), token.fStart);
839 this->lf(1);
840 this->dumpComment(token);
841}
842
843void IncludeParser::dumpMember(const Definition& token) {
844 this->writeTag("Member");
845 this->writeSpace();
846 this->writeDefinition(token, token.fName, 2);
847 lf(1);
848 for (auto child : token.fChildren) {
849 this->writeDefinition(*child);
850 }
851 this->writeEndTag();
852 lf(2);
853}
854
Cary Clarkd0530ba2017-09-14 11:25:39 -0400855bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400856 for (const auto& member : fIClassMap) {
857 if (string::npos != member.first.find("::")) {
858 continue;
859 }
860 if (!this->dumpTokens(dir, member.first)) {
861 return false;
862 }
863 }
864 return true;
865}
866
Ben Wagner63fd7602017-10-09 15:45:33 -0400867 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400868bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400869 string fileName = dir;
870 if (dir.length() && '/' != dir[dir.length() - 1]) {
871 fileName += '/';
872 }
873 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400874 fOut = fopen(fileName.c_str(), "wb");
875 if (!fOut) {
876 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400877 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400878 }
879 string prefixName = skClassName.substr(0, 2);
880 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
881 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400882 this->writeTagNoLF("Topic", topicName);
883 this->writeTag("Alias", topicName + "_Reference");
884 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400885 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500886 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
887 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400888 this->writeTag(containerType, skClassName);
889 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400890 auto& tokens = classMap.fTokens;
891 for (auto& token : tokens) {
892 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
893 continue;
894 }
Cary Clark9174bda2017-09-19 17:39:32 -0400895 this->writeDefinition(token);
896 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400897 }
898 this->lf(2);
899 string className(skClassName.substr(2));
900 vector<string> sortedClasses;
901 size_t maxLen = 0;
902 for (const auto& oneClass : fIClassMap) {
903 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
904 continue;
905 }
906 string structName = oneClass.first.substr(skClassName.length() + 2);
907 maxLen = SkTMax(maxLen, structName.length());
908 sortedClasses.emplace_back(structName);
909 }
Cary Clark9174bda2017-09-19 17:39:32 -0400910 this->writeTag("Topic", "Overview");
911 this->lf(2);
912 this->writeTag("Subtopic", "Subtopics");
913 this->writeEndTag("ToDo", "manually add subtopics");
914 this->writeTableHeader("topics", 0, "description");
915 this->writeTableTrailer();
916 this->writeEndTag();
917 this->lf(2);
918 if (maxLen) {
919 this->writeTag("Subtopic", "Structs");
920 this->writeTableHeader("description", maxLen, "struct");
921 for (auto& name : sortedClasses) {
922 this->writeTableRow(maxLen, name);
923 }
924 this->writeTableTrailer();
925 this->writeEndTag("Subtopic");
926 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400927 }
Cary Clark8032b982017-07-28 11:04:54 -0400928 maxLen = 0;
Cary Clark9174bda2017-09-19 17:39:32 -0400929 size_t constructorMax = 0;
930 size_t operatorMax = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400931 vector<string> sortedNames;
Cary Clark9174bda2017-09-19 17:39:32 -0400932 vector<string> constructorNames;
933 vector<string> operatorNames;
Cary Clark8032b982017-07-28 11:04:54 -0400934 for (const auto& token : classMap.fTokens) {
935 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
936 continue;
937 }
Cary Clark9174bda2017-09-19 17:39:32 -0400938 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400939 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
940 continue;
941 }
Cary Clark9174bda2017-09-19 17:39:32 -0400942 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
943 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
944 constructorMax = SkTMax(constructorMax, name.length());
945 constructorNames.emplace_back(name);
946 continue;
947 }
948 if (name.substr(0, 8) == "operator") {
949 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
950 operatorMax = SkTMax(operatorMax, name.length());
951 operatorNames.emplace_back(name);
952 continue;
953 }
Cary Clark8032b982017-07-28 11:04:54 -0400954 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
955 continue;
956 }
Cary Clark9174bda2017-09-19 17:39:32 -0400957 if ("SK_TO_STRING_NONVIRT" == name) {
958 name = "toString";
959 }
Cary Clark8032b982017-07-28 11:04:54 -0400960 size_t paren = name.find('(');
961 size_t funcLen = string::npos == paren ? name.length() : paren;
962 maxLen = SkTMax(maxLen, funcLen);
963 sortedNames.emplace_back(name);
964 }
Cary Clark9174bda2017-09-19 17:39:32 -0400965 if (constructorMax) {
966 std::sort(constructorNames.begin(), constructorNames.end());
967 this->writeTag("Subtopic", "Constructors");
968 this->writeTableHeader("description", constructorMax, "function");
969 for (auto& name : constructorNames) {
970 this->writeTableRow(constructorMax, name);
971 }
972 this->writeTableTrailer();
973 this->writeEndTag("Subtopic");
974 this->lf(2);
975 }
976 if (operatorMax) {
977 std::sort(operatorNames.begin(), operatorNames.end());
978 this->writeTag("Subtopic", "Operators");
979 this->writeTableHeader("description", operatorMax, "function");
980 for (auto& name : operatorNames) {
981 this->writeTableRow(operatorMax, name);
982 }
983 this->writeTableTrailer();
984 this->writeEndTag("Subtopic");
985 this->lf(2);
986 }
Cary Clark8032b982017-07-28 11:04:54 -0400987 std::sort(sortedNames.begin(), sortedNames.end());
Cary Clark9174bda2017-09-19 17:39:32 -0400988 this->writeTag("Subtopic", "Member_Functions");
989 this->writeTableHeader("description", maxLen, "function");
Cary Clark8032b982017-07-28 11:04:54 -0400990 for (auto& name : sortedNames) {
991 size_t paren = name.find('(');
992 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark9174bda2017-09-19 17:39:32 -0400993 this->writeTableRow(maxLen, name.substr(0, funcLen));
Cary Clark8032b982017-07-28 11:04:54 -0400994 }
Cary Clark9174bda2017-09-19 17:39:32 -0400995 this->writeTableTrailer();
996 this->writeEndTag("Subtopic");
997 this->lf(2);
998 this->writeEndTag("Topic");
999 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001000
1001 for (auto& oneClass : fIClassMap) {
1002 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1003 continue;
1004 }
1005 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -04001006 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -04001007 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -04001008 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -05001009 KeyWord keyword = oneClass.second.fKeyWord;
1010 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1011 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001012 this->writeTag(containerType, innerName);
1013 this->lf(2);
1014 this->writeTag("Code");
1015 this->writeEndTag("ToDo", "fill this in manually");
1016 this->writeEndTag();
1017 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001018 for (auto& token : oneClass.second.fTokens) {
1019 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1020 continue;
1021 }
Cary Clark9174bda2017-09-19 17:39:32 -04001022 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001023 }
1024 this->lf(2);
1025 this->dumpClassTokens(oneClass.second);
1026 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001027 this->writeEndTag(containerType, innerName);
1028 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001029 }
1030 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001031 this->writeEndTag(containerType, skClassName);
1032 this->lf(2);
1033 this->writeEndTag("Topic", topicName);
1034 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001035 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001036 SkDebugf("wrote %s\n", fileName.c_str());
1037 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001038}
1039
1040bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1041 // add comment preceding class, if any
1042 const Definition* parent = includeDef.fParent;
1043 int index = includeDef.fParentIndex;
1044 auto wordIter = parent->fTokens.begin();
1045 std::advance(wordIter, index);
1046 SkASSERT(&*wordIter == &includeDef);
1047 while (parent->fTokens.begin() != wordIter) {
1048 auto testIter = std::prev(wordIter);
1049 if (Definition::Type::kWord != testIter->fType
1050 && Definition::Type::kKeyWord != testIter->fType
1051 && (Definition::Type::kBracket != testIter->fType
1052 || Bracket::kAngle != testIter->fBracket)
1053 && (Definition::Type::kPunctuation != testIter->fType
1054 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1055 break;
1056 }
1057 wordIter = testIter;
1058 }
1059 auto commentIter = wordIter;
1060 while (parent->fTokens.begin() != commentIter) {
1061 auto testIter = std::prev(commentIter);
1062 bool isComment = Definition::Type::kBracket == testIter->fType
1063 && (Bracket::kSlashSlash == testIter->fBracket
1064 || Bracket::kSlashStar == testIter->fBracket);
1065 if (!isComment) {
1066 break;
1067 }
1068 commentIter = testIter;
1069 }
1070 while (commentIter != wordIter) {
1071 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1072 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1073 return false;
1074 }
1075 commentIter = std::next(commentIter);
1076 }
1077 return true;
1078}
1079
Cary Clarkbad5ad72017-08-03 17:14:08 -04001080bool IncludeParser::internalName(const Definition& token) const {
1081 return 0 == token.fName.find("internal_")
1082 || 0 == token.fName.find("Internal_")
1083 || 0 == token.fName.find("legacy_")
1084 || 0 == token.fName.find("temporary_")
1085 || 0 == token.fName.find("private_");
1086}
1087
Cary Clark8032b982017-07-28 11:04:54 -04001088// caller calls reportError, so just return false here
1089bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1090 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001091 // parse class header
1092 auto iter = includeDef->fTokens.begin();
1093 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1094 // todo : documentation is ignoring this for now
1095 iter = std::next(iter);
1096 }
1097 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1098 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001099 iter = std::next(iter);
1100 if (iter == includeDef->fTokens.end()) {
1101 return true; // forward declaration only
1102 }
Cary Clark8032b982017-07-28 11:04:54 -04001103 do {
1104 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001105 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001106 }
1107 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1108 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001109 }
Cary Clark8032b982017-07-28 11:04:54 -04001110 } while (static_cast<void>(iter = std::next(iter)), true);
1111 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001112 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001113 }
1114 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1115 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001116 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001117 }
1118 markupDef->fStart = iter->fStart;
1119 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001120 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001121 }
1122// if (1 != includeDef->fChildren.size()) {
1123// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1124// }
1125 includeDef = includeDef->fChildren.front();
1126 iter = includeDef->fTokens.begin();
1127 // skip until public
1128 int publicIndex = 0;
1129 if (IsStruct::kNo == isStruct) {
1130 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1131 size_t publicLen = strlen(publicName);
1132 while (iter != includeDef->fTokens.end()
1133 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1134 || strncmp(iter->fStart, publicName, publicLen))) {
1135 iter = std::next(iter);
1136 ++publicIndex;
1137 }
1138 }
1139 auto childIter = includeDef->fChildren.begin();
1140 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1141 (*childIter)->fPrivate = true;
1142 childIter = std::next(childIter);
1143 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001144 int keyIndex = publicIndex;
1145 KeyWord currentKey = KeyWord::kPublic;
1146 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1147 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001148 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1149 size_t protectedLen = strlen(protectedName);
1150 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1151 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001152 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001153 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001154 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1155 const char* testStart = iter->fStart;
1156 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1157 iter = std::next(iter);
1158 ++keyIndex;
1159 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1160 currentKey = KeyWord::kPublic;
1161 break;
1162 }
1163 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1164 currentKey = KeyWord::kProtected;
1165 break;
1166 }
1167 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1168 currentKey = KeyWord::kPrivate;
1169 break;
1170 }
1171 }
1172 fLastObject = nullptr;
1173 if (KeyWord::kPublic == currentKey) {
1174 if (!this->parseObject(child, markupDef)) {
1175 return false;
1176 }
1177 } else {
1178 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001179 }
Cary Clark73fa9722017-08-29 17:36:51 -04001180 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001181 childIter = std::next(childIter);
1182 }
Cary Clark8032b982017-07-28 11:04:54 -04001183 SkASSERT(fParent->fParent);
1184 fParent = fParent->fParent;
1185 return true;
1186}
1187
1188bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1189 int lineCount, Definition* markupDef) {
1190 TextParser parser(filename, start, end, lineCount);
1191 // parse doxygen if present
1192 if (parser.startsWith("**")) {
1193 parser.next();
1194 parser.next();
1195 parser.skipWhiteSpace();
1196 if ('\\' == parser.peek()) {
1197 parser.next();
1198 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1199 return reportError<bool>("missing object type");
1200 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001201 if (!parser.skipWord(markupDef->fName.c_str()) &&
1202 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001203 return reportError<bool>("missing object name");
1204 }
1205
1206 }
1207 }
1208 // remove leading '*' if present
1209 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1210 while (!parser.eof() && parser.skipWhiteSpace()) {
1211 while ('*' == parser.peek()) {
1212 parser.next();
1213 if (parser.eof()) {
1214 break;
1215 }
1216 parser.skipWhiteSpace();
1217 }
1218 if (parser.eof()) {
1219 break;
1220 }
1221 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001222 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark8032b982017-07-28 11:04:54 -04001223 parser.fLineCount, parent);
1224 parser.skipToEndBracket('\n');
1225 }
1226 return true;
1227}
1228
1229bool IncludeParser::parseDefine() {
1230
1231 return true;
1232}
1233
1234bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
1235 string nameStr;
1236 if (child->fTokens.size() > 0) {
1237 auto token = child->fTokens.begin();
1238 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1239 token = token->fTokens.begin();
1240 }
1241 if (Definition::Type::kWord == token->fType) {
1242 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1243 }
1244 }
1245 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1246 child->fLineCount, markupDef);
1247 Definition* markupChild = &markupDef->fTokens.back();
1248 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1249 markupChild->fKeyWord = KeyWord::kEnum;
1250 TextParser enumName(child);
1251 enumName.skipExact("enum ");
Cary Clarkbad5ad72017-08-03 17:14:08 -04001252 if (enumName.skipExact("class ")) {
1253 markupChild->fMarkType = MarkType::kEnumClass;
1254 }
Cary Clark8032b982017-07-28 11:04:54 -04001255 const char* nameStart = enumName.fChar;
1256 enumName.skipToSpace();
Ben Wagner63fd7602017-10-09 15:45:33 -04001257 markupChild->fName = markupDef->fName + "::" +
Cary Clark8032b982017-07-28 11:04:54 -04001258 string(nameStart, (size_t) (enumName.fChar - nameStart));
1259 if (!this->findComments(*child, markupChild)) {
1260 return false;
1261 }
1262 TextParser parser(child);
1263 parser.skipToEndBracket('{');
Cary Clark9174bda2017-09-19 17:39:32 -04001264 parser.next();
Cary Clark8032b982017-07-28 11:04:54 -04001265 const char* dataEnd;
1266 do {
Cary Clark8032b982017-07-28 11:04:54 -04001267 parser.skipWhiteSpace();
1268 if ('}' == parser.peek()) {
1269 break;
1270 }
1271 Definition* comment = nullptr;
1272 // note that comment, if any, can be before or after (on the same line, though) as member
1273 if ('#' == parser.peek()) {
1274 // fixme: handle preprecessor, but just skip it for now
1275 parser.skipToLineStart();
1276 }
1277 while (parser.startsWith("/*") || parser.startsWith("//")) {
1278 parser.next();
1279 const char* start = parser.fChar;
1280 const char* end;
1281 if ('*' == parser.peek()) {
1282 end = parser.strnstr("*/", parser.fEnd);
1283 parser.fChar = end;
1284 parser.next();
1285 parser.next();
1286 } else {
1287 end = parser.trimmedLineEnd();
1288 parser.skipToLineStart();
1289 }
1290 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1291 markupChild);
1292 comment = &markupChild->fTokens.back();
1293 comment->fTerminator = end;
1294 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1295 return false;
1296 }
1297 parser.skipWhiteSpace();
1298 }
1299 parser.skipWhiteSpace();
1300 const char* memberStart = parser.fChar;
1301 if ('}' == memberStart[0]) {
1302 break;
1303 }
Cary Clark9174bda2017-09-19 17:39:32 -04001304 // if there's comment on same the line as member def, output first as if it was before
1305
Cary Clark8032b982017-07-28 11:04:54 -04001306 parser.skipToNonAlphaNum();
1307 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001308 if (parser.eof() || !parser.skipWhiteSpace()) {
1309 return this->reportError<bool>("enum member must end with comma 1");
1310 }
Cary Clark8032b982017-07-28 11:04:54 -04001311 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001312 if ('=' == parser.peek()) {
1313 parser.skipToEndBracket(',');
1314 }
1315 if (parser.eof() || ',' != parser.peek()) {
1316 return this->reportError<bool>("enum member must end with comma 2");
1317 }
1318 dataEnd = parser.fChar;
1319 const char* start = parser.anyOf("/\n");
1320 SkASSERT(start);
1321 parser.skipTo(start);
1322 if ('/' == parser.next()) {
1323 char slashStar = parser.next();
1324 if ('/' == slashStar || '*' == slashStar) {
1325 TextParser::Save save(&parser);
1326 char doxCheck = parser.next();
1327 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1328 save.restore();
1329 }
1330 }
1331 parser.skipWhiteSpace();
1332 const char* commentStart = parser.fChar;
1333 if ('/' == slashStar) {
1334 parser.skipToEndBracket('\n');
1335 } else {
1336 parser.skipToEndBracket("*/");
1337 }
1338 SkASSERT(!parser.eof());
1339 const char* commentEnd = parser.fChar;
1340 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
1341 parser.fLineCount, markupChild);
1342 comment = &markupChild->fTokens.back();
1343 comment->fTerminator = commentEnd;
1344 }
Cary Clark8032b982017-07-28 11:04:54 -04001345 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1346 markupChild);
1347 Definition* member = &markupChild->fTokens.back();
1348 member->fName = memberName;
1349 if (comment) {
1350 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001351 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001352 }
1353 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001354 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001355 for (auto outsideMember : child->fChildren) {
1356 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001357 continue;
1358 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001359 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1360 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001361 continue;
1362 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001363 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1364 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
1365 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild);
Cary Clark884dd7d2017-10-11 10:37:52 -04001366 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001367 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001368 // FIXME: ? add comment as well ?
1369 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001370 }
Cary Clark8032b982017-07-28 11:04:54 -04001371 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1372 SkASSERT(classDef.fStart);
1373 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1374 markupChild->fName = uniqueName;
1375 classDef.fEnums[uniqueName] = markupChild;
1376 return true;
1377}
1378
1379bool IncludeParser::parseInclude(const string& name) {
1380 fParent = &fIncludeMap[name];
1381 fParent->fName = name;
1382 fParent->fFileName = fFileName;
1383 fParent->fType = Definition::Type::kFileType;
1384 fParent->fContentStart = fChar;
1385 fParent->fContentEnd = fEnd;
1386 // parse include file into tree
1387 while (fChar < fEnd) {
1388 if (!this->parseChar()) {
1389 return false;
1390 }
1391 }
1392 // parse tree and add named objects to maps
1393 fParent = &fIncludeMap[name];
1394 if (!this->parseObjects(fParent, nullptr)) {
1395 return false;
1396 }
1397 return true;
1398}
1399
1400bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1401 const char* typeStart = child->fChildren[0]->fContentStart;
1402 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1403 child->fLineCount, markupDef);
1404 Definition* markupChild = &markupDef->fTokens.back();
1405 TextParser nameParser(child);
1406 nameParser.skipToNonAlphaNum();
1407 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1408 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1409 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1410 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001411 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001412 classDef.fMembers[uniqueName] = markupChild;
1413 if (child->fParentIndex >= 2) {
1414 auto comment = child->fParent->fTokens.begin();
1415 std::advance(comment, child->fParentIndex - 2);
1416 if (Definition::Type::kBracket == comment->fType
1417 && (Bracket::kSlashStar == comment->fBracket
1418 || Bracket::kSlashSlash == comment->fBracket)) {
1419 TextParser parser(&*comment);
1420 do {
1421 parser.skipToAlpha();
1422 if (parser.eof()) {
1423 break;
1424 }
1425 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001426 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001427 if (Bracket::kSlashStar == comment->fBracket) {
1428 const char* commentEnd = parser.strnstr("*/", end);
1429 if (commentEnd) {
1430 end = commentEnd;
1431 }
1432 }
1433 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1434 markupDef);
1435 Definition* commentChild = &markupDef->fTokens.back();
1436 markupChild->fChildren.emplace_back(commentChild);
1437 parser.skipTo(end);
1438 } while (!parser.eof());
1439 }
1440 }
1441 return true;
1442}
1443
1444bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1445 auto tokenIter = child->fParent->fTokens.begin();
1446 std::advance(tokenIter, child->fParentIndex);
1447 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001448 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001449 bool addConst = false;
1450 auto operatorCheck = tokenIter;
1451 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1452 operatorCheck = std::prev(tokenIter);
1453 }
1454 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001455 auto closeParen = std::next(tokenIter);
1456 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1457 '(' == closeParen->fContentStart[0]);
1458 nameEnd = closeParen->fContentEnd + 1;
1459 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001460 if (Definition::Type::kKeyWord == closeParen->fType &&
1461 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001462 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001463 }
Cary Clarka560c472017-11-27 10:44:06 -05001464 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001465 }
1466 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001467 if (addConst) {
1468 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001469 }
Cary Clark8032b982017-07-28 11:04:54 -04001470 while (tokenIter != child->fParent->fTokens.begin()) {
1471 auto testIter = std::prev(tokenIter);
1472 switch (testIter->fType) {
1473 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001474 if (testIter == child->fParent->fTokens.begin() &&
1475 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1476 KeyWord::kIfndef == child->fParent->fKeyWord ||
1477 KeyWord::kIf == child->fParent->fKeyWord)) {
1478 std::next(tokenIter);
1479 break;
1480 }
Cary Clark8032b982017-07-28 11:04:54 -04001481 goto keepGoing;
1482 case Definition::Type::kKeyWord: {
1483 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1484 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1485 goto keepGoing;
1486 }
1487 } break;
1488 case Definition::Type::kBracket:
1489 if (Bracket::kAngle == testIter->fBracket) {
1490 goto keepGoing;
1491 }
1492 break;
1493 case Definition::Type::kPunctuation:
1494 if (Punctuation::kSemicolon == testIter->fPunctuation
1495 || Punctuation::kLeftBrace == testIter->fPunctuation
1496 || Punctuation::kColon == testIter->fPunctuation) {
1497 break;
1498 }
1499 keepGoing:
1500 tokenIter = testIter;
1501 continue;
1502 default:
1503 break;
1504 }
1505 break;
1506 }
1507 tokenIter->fName = nameStr;
1508 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001509 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001510 auto testIter = child->fParent->fTokens.begin();
1511 SkASSERT(child->fParentIndex > 0);
1512 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001513 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1514 0 == tokenIter->fParentIndex) {
1515 tokenIter = std::next(tokenIter);
1516 }
Cary Clark8032b982017-07-28 11:04:54 -04001517 const char* start = tokenIter->fContentStart;
1518 const char* end = tokenIter->fContentEnd;
1519 const char kDebugCodeStr[] = "SkDEBUGCODE";
1520 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1521 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1522 std::advance(testIter, 1);
1523 start = testIter->fContentStart + 1;
1524 end = testIter->fContentEnd - 1;
1525 } else {
1526 end = testIter->fContentEnd;
1527 while (testIter != child->fParent->fTokens.end()) {
1528 testIter = std::next(testIter);
1529 switch (testIter->fType) {
1530 case Definition::Type::kPunctuation:
1531 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1532 || Punctuation::kLeftBrace == testIter->fPunctuation
1533 || Punctuation::kColon == testIter->fPunctuation);
1534 end = testIter->fStart;
1535 break;
1536 case Definition::Type::kKeyWord: {
1537 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1538 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1539 continue;
1540 }
1541 } break;
1542 default:
1543 continue;
1544 }
1545 break;
1546 }
1547 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001548 while (end > start && ' ' >= end[-1]) {
1549 --end;
1550 }
Cary Clark9174bda2017-09-19 17:39:32 -04001551 if (!markupDef) {
1552 auto parentIter = child->fParent->fTokens.begin();
1553 SkASSERT(child->fParentIndex > 0);
1554 std::advance(parentIter, child->fParentIndex - 1);
1555 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001556 TextParser nameParser(methodName);
1557 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001558 return true; // expect this is inline class definition outside of class
1559 }
Cary Clarka560c472017-11-27 10:44:06 -05001560 string name(nameParser.fLine, nameParser.lineLength());
1561 auto finder = fIFunctionMap.find(name);
1562 if (fIFunctionMap.end() != finder) {
1563 // create unique name
1564 SkASSERT(0); // incomplete
1565 }
1566 auto globalFunction = &fIFunctionMap[name];
1567 globalFunction->fContentStart = start;
1568 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001569 globalFunction->fFiddle = name;
1570 globalFunction->fContentEnd = end;
1571 globalFunction->fMarkType = MarkType::kMethod;
1572 globalFunction->fLineCount = tokenIter->fLineCount;
1573 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001574 }
Cary Clark8032b982017-07-28 11:04:54 -04001575 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1576 markupDef);
1577 Definition* markupChild = &markupDef->fTokens.back();
1578 // do find instead -- I wonder if there is a way to prevent this in c++
1579 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1580 SkASSERT(classDef.fStart);
1581 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1582 markupChild->fName = uniqueName;
1583 if (!this->findComments(*child, markupChild)) {
1584 return false;
1585 }
1586 classDef.fMethods[uniqueName] = markupChild;
1587 return true;
1588}
1589
Cary Clark8032b982017-07-28 11:04:54 -04001590bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1591 for (auto& child : parent->fChildren) {
1592 if (!this->parseObject(child, markupDef)) {
1593 return false;
1594 }
1595 }
1596 return true;
1597}
1598
1599bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1600 // set up for error reporting
1601 fLine = fChar = child->fStart;
1602 fEnd = child->fContentEnd;
1603 // todo: put original line number in child as well
1604 switch (child->fType) {
1605 case Definition::Type::kKeyWord:
1606 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001607 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001608 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001609 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001610 }
1611 break;
1612 case KeyWord::kEnum:
1613 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001614 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001615 }
1616 break;
1617 case KeyWord::kStruct:
1618 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001619 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001620 }
1621 break;
1622 case KeyWord::kTemplate:
1623 if (!this->parseTemplate()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001624 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001625 }
1626 break;
1627 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001628 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001629 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001630 }
1631 break;
1632 case KeyWord::kUnion:
1633 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001634 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001635 }
1636 break;
1637 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001638 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001639 }
1640 break;
1641 case Definition::Type::kBracket:
1642 switch (child->fBracket) {
1643 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001644 if (fLastObject) {
1645 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1646 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001647 if (!checkDeprecated.eof()) {
1648 checkDeprecated.skipWhiteSpace();
1649 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1650 break;
1651 }
1652 }
1653 }
1654 {
1655 auto tokenIter = child->fParent->fTokens.begin();
1656 std::advance(tokenIter, child->fParentIndex);
1657 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05001658 TextParser previousToken(&*tokenIter);
1659 if (previousToken.startsWith("SK_ATTR_DEPRECATED")) {
Cary Clark73fa9722017-08-29 17:36:51 -04001660 break;
1661 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05001662 if (Bracket::kPound == child->fParent->fBracket &&
1663 KeyWord::kIf == child->fParent->fKeyWord) {
1664 // TODO: this will skip methods named defined() -- for the
1665 // moment there aren't any
1666 if (previousToken.startsWith("defined")) {
1667 break;
1668 }
1669 }
Cary Clark73fa9722017-08-29 17:36:51 -04001670 }
Cary Clark8032b982017-07-28 11:04:54 -04001671 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001672 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001673 }
Cary Clark73fa9722017-08-29 17:36:51 -04001674 break;
Cary Clark8032b982017-07-28 11:04:54 -04001675 case Bracket::kSlashSlash:
1676 case Bracket::kSlashStar:
1677 // comments are picked up by parsing objects first
1678 break;
1679 case Bracket::kPound:
1680 // special-case the #xxx xxx_DEFINED entries
1681 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001682 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001683 case KeyWord::kIfndef:
1684 case KeyWord::kIfdef:
1685 if (child->boilerplateIfDef(fParent)) {
1686 if (!this->parseObjects(child, markupDef)) {
1687 return false;
1688 }
1689 break;
1690 }
1691 goto preproError;
1692 case KeyWord::kDefine:
1693 if (child->boilerplateDef(fParent)) {
1694 break;
1695 }
1696 goto preproError;
1697 case KeyWord::kEndif:
1698 if (child->boilerplateEndIf()) {
1699 break;
1700 }
1701 case KeyWord::kInclude:
1702 // ignored for now
1703 break;
1704 case KeyWord::kElse:
1705 case KeyWord::kElif:
1706 // todo: handle these
1707 break;
1708 default:
1709 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001710 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001711 }
1712 break;
1713 case Bracket::kAngle:
1714 // pick up templated function pieces when method is found
1715 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001716 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001717 if (!this->parseObjects(child, markupDef)) {
1718 return false;
1719 }
Cary Clark73fa9722017-08-29 17:36:51 -04001720 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001721 case Bracket::kSquare: {
1722 // check to see if parent is operator, the only case we handle so far
1723 auto prev = child->fParent->fTokens.begin();
1724 std::advance(prev, child->fParentIndex - 1);
1725 if (KeyWord::kOperator != prev->fKeyWord) {
1726 return child->reportError<bool>("expected operator overload");
1727 }
1728 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001729 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001730 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001731 }
1732 break;
1733 case Definition::Type::kWord:
1734 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001735 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001736 }
1737 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001738 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001739 }
1740 break;
1741 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001742 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001743 break;
1744 }
1745 return true;
1746}
1747
1748bool IncludeParser::parseTemplate() {
1749
1750 return true;
1751}
1752
Cary Clark2f466242017-12-11 16:03:17 -05001753bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1754 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05001755 typedefParser.skipExact("typedef");
1756 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05001757 string nameStr = typedefParser.typedefName();
1758 if (!markupDef) {
1759 Definition& typedefDef = fITypedefMap[nameStr];
1760 SkASSERT(!typedefDef.fStart);
1761 typedefDef.fStart = child->fContentStart;
1762 typedefDef.fContentStart = child->fContentStart;
1763 typedefDef.fName = nameStr;
1764 typedefDef.fFiddle = nameStr;
1765 typedefDef.fContentEnd = child->fContentEnd;
1766 typedefDef.fTerminator = child->fContentEnd;
1767 typedefDef.fMarkType = MarkType::kTypedef;
1768 typedefDef.fLineCount = child->fLineCount;
1769 return true;
1770 }
1771 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
1772 child->fLineCount, markupDef);
1773 Definition* markupChild = &markupDef->fTokens.back();
1774 markupChild->fName = nameStr;
1775 markupChild->fTerminator = markupChild->fContentEnd;
1776 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1777 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001778 return true;
1779}
1780
1781bool IncludeParser::parseUnion() {
1782
1783 return true;
1784}
1785
1786bool IncludeParser::parseChar() {
1787 char test = *fChar;
1788 if ('\\' == fPrev) {
1789 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001790// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001791 fLine = fChar + 1;
1792 }
1793 goto done;
1794 }
1795 switch (test) {
1796 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001797// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001798 fLine = fChar + 1;
1799 if (fInChar) {
1800 return reportError<bool>("malformed char");
1801 }
1802 if (fInString) {
1803 return reportError<bool>("malformed string");
1804 }
1805 if (!this->checkForWord()) {
1806 return false;
1807 }
1808 if (Bracket::kPound == this->topBracket()) {
1809 KeyWord keyWord = fParent->fKeyWord;
1810 if (KeyWord::kNone == keyWord) {
1811 return this->reportError<bool>("unhandled preprocessor directive");
1812 }
1813 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) {
1814 this->popBracket();
1815 }
1816 } else if (Bracket::kSlashSlash == this->topBracket()) {
1817 this->popBracket();
1818 }
1819 break;
1820 case '*':
1821 if (!fInCharCommentString && '/' == fPrev) {
1822 this->pushBracket(Bracket::kSlashStar);
1823 }
1824 if (!this->checkForWord()) {
1825 return false;
1826 }
1827 if (!fInCharCommentString) {
1828 this->addPunctuation(Punctuation::kAsterisk);
1829 }
1830 break;
1831 case '/':
1832 if ('*' == fPrev) {
1833 if (!fInCharCommentString) {
1834 return reportError<bool>("malformed closing comment");
1835 }
1836 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001837 TextParser::Save save(this);
1838 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001839 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001840 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001841 }
1842 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001843 }
Cary Clark8032b982017-07-28 11:04:54 -04001844 if (!fInCharCommentString && '/' == fPrev) {
1845 this->pushBracket(Bracket::kSlashSlash);
1846 break;
1847 }
1848 if (!this->checkForWord()) {
1849 return false;
1850 }
1851 break;
1852 case '\'':
1853 if (Bracket::kChar == this->topBracket()) {
1854 this->popBracket();
1855 } else if (!fInComment && !fInString) {
1856 if (fIncludeWord) {
1857 return this->reportError<bool>("word then single-quote");
1858 }
1859 this->pushBracket(Bracket::kChar);
1860 }
1861 break;
1862 case '\"':
1863 if (Bracket::kString == this->topBracket()) {
1864 this->popBracket();
1865 } else if (!fInComment && !fInChar) {
1866 if (fIncludeWord) {
1867 return this->reportError<bool>("word then double-quote");
1868 }
1869 this->pushBracket(Bracket::kString);
1870 }
1871 break;
1872 case ':':
1873 case '(':
1874 case '[':
1875 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001876 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1877 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1878 this->pushBracket(Bracket::kDebugCode);
1879 break;
1880 }
Cary Clark8032b982017-07-28 11:04:54 -04001881 if (fInCharCommentString) {
1882 break;
1883 }
1884 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1885 break;
1886 }
1887 if (!fInBrace) {
1888 if (!this->checkForWord()) {
1889 return false;
1890 }
1891 if (':' == test && !fInFunction) {
1892 break;
1893 }
1894 if ('{' == test) {
1895 this->addPunctuation(Punctuation::kLeftBrace);
1896 } else if (':' == test) {
1897 this->addPunctuation(Punctuation::kColon);
1898 }
1899 }
1900 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1901 && Bracket::kColon == fInBrace->fBracket) {
1902 Definition* braceParent = fParent->fParent;
1903 braceParent->fChildren.pop_back();
1904 braceParent->fTokens.pop_back();
1905 fParent = braceParent;
1906 fInBrace = nullptr;
1907 }
1908 this->pushBracket(
1909 '(' == test ? Bracket::kParen :
1910 '[' == test ? Bracket::kSquare :
1911 '{' == test ? Bracket::kBrace :
1912 Bracket::kColon);
1913 if (!fInBrace
1914 && ('{' == test || (':' == test && ' ' >= fChar[1]))
1915 && fInFunction) {
1916 fInBrace = fParent;
1917 }
1918 } break;
1919 case '<':
1920 if (fInCharCommentString || fInBrace) {
1921 break;
1922 }
1923 if (!this->checkForWord()) {
1924 return false;
1925 }
1926 if (fInEnum) {
1927 break;
1928 }
1929 this->pushBracket(Bracket::kAngle);
1930 break;
1931 case ')':
1932 case ']':
1933 case '}': {
1934 if (fInCharCommentString) {
1935 break;
1936 }
1937 if (!fInBrace) {
1938 if (!this->checkForWord()) {
1939 return false;
1940 }
1941 }
1942 bool popBraceParent = fInBrace == fParent;
1943 if ((')' == test ? Bracket::kParen :
1944 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
1945 this->popBracket();
1946 if (!fInFunction) {
1947 bool deprecatedMacro = false;
1948 if (')' == test) {
1949 auto iter = fParent->fTokens.end();
1950 bool lookForWord = false;
1951 while (fParent->fTokens.begin() != iter) {
1952 --iter;
1953 if (lookForWord) {
1954 if (Definition::Type::kWord != iter->fType) {
1955 break;
1956 }
1957 string word(iter->fContentStart, iter->length());
1958 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
1959 deprecatedMacro = true;
1960 // remove macro paren (would confuse method parsing later)
Ben Wagner63fd7602017-10-09 15:45:33 -04001961 fParent->fTokens.pop_back();
Cary Clark8032b982017-07-28 11:04:54 -04001962 fParent->fChildren.pop_back();
1963 }
1964 break;
1965 }
1966 if (Definition::Type::kBracket != iter->fType) {
1967 break;
1968 }
1969 if (Bracket::kParen != iter->fBracket) {
1970 break;
1971 }
1972 lookForWord = true;
1973 }
1974 }
1975 fInFunction = ')' == test && !deprecatedMacro;
1976 } else {
1977 fInFunction = '}' != test;
1978 }
Cary Clark73fa9722017-08-29 17:36:51 -04001979 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
1980 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04001981 } else {
1982 return reportError<bool>("malformed close bracket");
1983 }
1984 if (popBraceParent) {
1985 Definition* braceParent = fInBrace->fParent;
1986 braceParent->fChildren.pop_back();
1987 braceParent->fTokens.pop_back();
1988 fInBrace = nullptr;
1989 }
1990 } break;
1991 case '>':
1992 if (fInCharCommentString || fInBrace) {
1993 break;
1994 }
1995 if (!this->checkForWord()) {
1996 return false;
1997 }
1998 if (fInEnum) {
1999 break;
2000 }
Cary Clarka560c472017-11-27 10:44:06 -05002001 if (Bracket::kPound == this->topBracket()) {
2002 break;
2003 }
Cary Clark8032b982017-07-28 11:04:54 -04002004 if (Bracket::kAngle == this->topBracket()) {
2005 this->popBracket();
2006 } else {
2007 return reportError<bool>("malformed close angle bracket");
2008 }
2009 break;
2010 case '#': {
2011 if (fInCharCommentString || fInBrace) {
2012 break;
2013 }
2014 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2015 this->pushBracket(Bracket::kPound);
2016 break;
2017 }
2018 case '&':
2019 case ',':
2020 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05002021 case '+':
2022 case '=':
2023 case '-':
2024 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002025 if (fInCharCommentString || fInBrace) {
2026 break;
2027 }
2028 if (!this->checkForWord()) {
2029 return false;
2030 }
2031 break;
2032 case ';':
2033 if (fInCharCommentString || fInBrace) {
2034 break;
2035 }
2036 if (!this->checkForWord()) {
2037 return false;
2038 }
2039 if (Definition::Type::kKeyWord == fParent->fType
2040 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002041 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
2042 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2043 this->popObject();
2044 }
Cary Clark8032b982017-07-28 11:04:54 -04002045 if (KeyWord::kEnum == fParent->fKeyWord) {
2046 fInEnum = false;
2047 }
2048 this->popObject();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002049 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002050 } else if (Definition::Type::kBracket == fParent->fType
2051 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2052 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2053 list<Definition>::iterator baseIter = fParent->fTokens.end();
2054 list<Definition>::iterator namedIter = fParent->fTokens.end();
2055 for (auto tokenIter = fParent->fTokens.end();
2056 fParent->fTokens.begin() != tokenIter--; ) {
2057 if (tokenIter->fLineCount == fLineCount) {
2058 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2059 if (namedIter != fParent->fTokens.end()) {
2060 return reportError<bool>("found two named member tokens");
2061 }
2062 namedIter = tokenIter;
2063 }
2064 baseIter = tokenIter;
2065 } else {
2066 break;
2067 }
2068 }
2069 // FIXME: if a member definition spans multiple lines, this won't work
2070 if (namedIter != fParent->fTokens.end()) {
2071 if (baseIter == namedIter) {
2072 return this->reportError<bool>("expected type before named token");
2073 }
2074 Definition* member = &*namedIter;
2075 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002076 if (!member->fTerminator) {
2077 member->fTerminator = member->fContentEnd;
2078 }
Cary Clark8032b982017-07-28 11:04:54 -04002079 fParent->fChildren.push_back(member);
2080 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2081 member->fChildren.push_back(&*nameType);
2082 }
Cary Clark8032b982017-07-28 11:04:54 -04002083 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002084 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002085 } else if (fParent->fChildren.size() > 0) {
2086 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002087 Definition* priorEnum = fPriorEnum;
2088 fPriorEnum = nullptr;
2089 if (!priorEnum) {
2090 while (fParent->fChildren.begin() != lastIter) {
2091 std::advance(lastIter, -1);
2092 priorEnum = *lastIter;
2093 if (Definition::Type::kBracket != priorEnum->fType ||
2094 (Bracket::kSlashSlash != priorEnum->fBracket
2095 && Bracket::kSlashStar != priorEnum->fBracket)) {
2096 break;
2097 }
Cary Clark8032b982017-07-28 11:04:54 -04002098 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002099 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002100 }
2101 if (Definition::Type::kKeyWord == priorEnum->fType
2102 && KeyWord::kEnum == priorEnum->fKeyWord) {
2103 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002104 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002105 while (tokenWalker != fParent->fTokens.end()) {
2106 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002107 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002108 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2109 break;
2110 }
2111 }
2112 while (tokenWalker != fParent->fTokens.end()) {
2113 std::advance(tokenWalker, 1);
2114 const Definition* test = &*tokenWalker;
2115 if (Definition::Type::kBracket != test->fType ||
2116 (Bracket::kSlashSlash != test->fBracket
2117 && Bracket::kSlashStar != test->fBracket)) {
2118 break;
2119 }
2120 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002121 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002122 Definition* start = &*tokenWalker;
2123 bool foundExpected = true;
2124 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2125 const Definition* test = &*tokenWalker;
2126 if (expected != test->fKeyWord) {
2127 foundExpected = false;
2128 break;
2129 }
2130 if (tokenWalker == fParent->fTokens.end()) {
2131 break;
2132 }
2133 std::advance(tokenWalker, 1);
2134 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002135 if (!foundExpected) {
2136 foundExpected = true;
2137 tokenWalker = saveTokenWalker;
2138 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2139 const Definition* test = &*tokenWalker;
2140 if (expected != test->fKeyWord) {
2141 foundExpected = false;
2142 break;
2143 }
2144 if (tokenWalker == fParent->fTokens.end()) {
2145 break;
2146 }
2147 if (KeyWord::kNone != expected) {
2148 std::advance(tokenWalker, 1);
2149 }
2150 }
2151 if (foundExpected) {
2152 auto nameToken = priorEnum->fTokens.begin();
2153 string enumName = string(nameToken->fContentStart,
2154 nameToken->fContentEnd - nameToken->fContentStart);
2155 const Definition* test = &*tokenWalker;
2156 string constType = string(test->fContentStart,
2157 test->fContentEnd - test->fContentStart);
2158 if (enumName != constType) {
2159 foundExpected = false;
2160 } else {
2161 std::advance(tokenWalker, 1);
2162 }
2163 }
2164 }
Cary Clark8032b982017-07-28 11:04:54 -04002165 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2166 const char* nameStart = tokenWalker->fStart;
2167 std::advance(tokenWalker, 1);
2168 if (tokenWalker != fParent->fTokens.end()) {
2169 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2170 tp.skipToNonAlphaNum();
2171 start->fName = string(nameStart, tp.fChar - nameStart);
2172 start->fContentEnd = fChar;
2173 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002174 fPriorEnum = priorEnum;
2175 }
Cary Clark8032b982017-07-28 11:04:54 -04002176 }
2177 }
2178 }
2179 this->addPunctuation(Punctuation::kSemicolon);
2180 fInFunction = false;
2181 break;
2182 case '~':
2183 if (fInEnum) {
2184 break;
2185 }
2186 case '0': case '1': case '2': case '3': case '4':
2187 case '5': case '6': case '7': case '8': case '9':
2188 // TODO: don't want to parse numbers, but do need to track for enum defs
2189 // break;
2190 case 'A': case 'B': case 'C': case 'D': case 'E':
2191 case 'F': case 'G': case 'H': case 'I': case 'J':
2192 case 'K': case 'L': case 'M': case 'N': case 'O':
2193 case 'P': case 'Q': case 'R': case 'S': case 'T':
2194 case 'U': case 'V': case 'W': case 'X': case 'Y':
2195 case 'Z': case '_':
2196 case 'a': case 'b': case 'c': case 'd': case 'e':
2197 case 'f': case 'g': case 'h': case 'i': case 'j':
2198 case 'k': case 'l': case 'm': case 'n': case 'o':
2199 case 'p': case 'q': case 'r': case 's': case 't':
2200 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002201 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002202 if (fInCharCommentString || fInBrace) {
2203 break;
2204 }
2205 if (!fIncludeWord) {
2206 fIncludeWord = fChar;
2207 }
2208 break;
2209 }
2210done:
2211 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002212 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002213 return true;
2214}
2215
2216void IncludeParser::validate() const {
2217 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2218 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2219 }
2220 IncludeParser::ValidateKeyWords();
2221}