blob: c69af7a05aa37772920f1eb805b93a0d587e7c1a [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
Cary Clark2dc84ad2018-01-26 12:56:22 -05009#include "SkOSFile.h"
10#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040011
Cary Clark8032b982017-07-28 11:04:54 -040012const IncludeKey kKeyWords[] = {
13 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040014 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040015 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040016 { "bool", KeyWord::kBool, KeyProperty::kNumber },
17 { "char", KeyWord::kChar, KeyProperty::kNumber },
18 { "class", KeyWord::kClass, KeyProperty::kObject },
19 { "const", KeyWord::kConst, KeyProperty::kModifier },
20 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
21 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
22 { "double", KeyWord::kDouble, KeyProperty::kNumber },
23 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
24 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
25 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
26 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050027 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040028 { "float", KeyWord::kFloat, KeyProperty::kNumber },
29 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
30 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
31 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
32 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
33 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
34 { "inline", KeyWord::kInline, KeyProperty::kModifier },
35 { "int", KeyWord::kInt, KeyProperty::kNumber },
36 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
37 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
38 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
39 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
40 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
41 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
42 { "static", KeyWord::kStatic, KeyProperty::kModifier },
43 { "struct", KeyWord::kStruct, KeyProperty::kObject },
44 { "template", KeyWord::kTemplate, KeyProperty::kObject },
45 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040046 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040047 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040048 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
49 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040050 { "union", KeyWord::kUnion, KeyProperty::kObject },
51 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
52 { "void", KeyWord::kVoid, KeyProperty::kNumber },
53};
54
55const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
56
57KeyWord IncludeParser::FindKey(const char* start, const char* end) {
58 int ch = 0;
59 for (size_t index = 0; index < kKeyWordCount; ) {
60 if (start[ch] > kKeyWords[index].fName[ch]) {
61 ++index;
62 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
63 return KeyWord::kNone;
64 }
65 continue;
66 }
67 if (start[ch] < kKeyWords[index].fName[ch]) {
68 return KeyWord::kNone;
69 }
70 ++ch;
71 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040072 if (end - start < (int) strlen(kKeyWords[index].fName)) {
73 return KeyWord::kNone;
74 }
Cary Clark8032b982017-07-28 11:04:54 -040075 return kKeyWords[index].fKeyWord;
76 }
77 }
78 return KeyWord::kNone;
79}
80
81void IncludeParser::ValidateKeyWords() {
82 for (size_t index = 1; index < kKeyWordCount; ++index) {
83 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
84 == (int) kKeyWords[index].fKeyWord);
85 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
86 }
87}
88
89void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050090 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040091 fIncludeWord = nullptr;
92 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
93 Definition* def = &fParent->fTokens.back();
94 this->addDefinition(def);
95 if (KeyWord::kEnum == fParent->fKeyWord) {
96 fInEnum = true;
97 }
98 }
99}
100
Ben Wagner63fd7602017-10-09 15:45:33 -0400101void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400102 const vector<string>& foundParams) {
103 for (auto& methodParam : methodParams) {
104 bool found = false;
105 for (auto& foundParam : foundParams) {
106 if (methodParam == foundParam) {
107 found = true;
108 break;
109 }
110 }
111 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400112 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400113 }
114 }
115 for (auto& foundParam : foundParams) {
116 bool found = false;
117 for (auto& methodParam : methodParams) {
118 if (methodParam == foundParam) {
119 found = true;
120 break;
121 }
122 }
123 if (!found) {
124 this->reportError("doxygen param does not match method declaration");
125 }
126 }
127}
128
129bool IncludeParser::checkForWord() {
130 if (!fIncludeWord) {
131 return true;
132 }
133 KeyWord keyWord = FindKey(fIncludeWord, fChar);
134 if (KeyWord::kNone != keyWord) {
135 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
136 this->addKeyword(keyWord);
137 return true;
138 }
139 } else {
140 this->addWord();
141 return true;
142 }
143 Definition* poundDef = fParent;
144 if (!fParent) {
145 return reportError<bool>("expected parent");
146 }
147 if (Definition::Type::kBracket != poundDef->fType) {
148 return reportError<bool>("expected bracket");
149 }
150 if (Bracket::kPound != poundDef->fBracket) {
151 return reportError<bool>("expected preprocessor");
152 }
153 if (KeyWord::kNone != poundDef->fKeyWord) {
154 return reportError<bool>("already found keyword");
155 }
156 poundDef->fKeyWord = keyWord;
157 fIncludeWord = nullptr;
158 switch (keyWord) {
159 // these do not link to other # directives
160 case KeyWord::kDefine:
161 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500162 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400163 break;
164 // these start a # directive link
165 case KeyWord::kIf:
166 case KeyWord::kIfdef:
167 case KeyWord::kIfndef:
168 break;
169 // these continue a # directive link
170 case KeyWord::kElif:
171 case KeyWord::kElse: {
172 this->popObject(); // pop elif
173 if (Bracket::kPound != fParent->fBracket) {
174 return this->reportError<bool>("expected preprocessor directive");
175 }
176 this->popBracket(); // pop if
177 poundDef->fParent = fParent;
178 this->addDefinition(poundDef); // push elif back
179 } break;
180 // this ends a # directive link
181 case KeyWord::kEndif:
182 // FIXME : should this be calling popBracket() instead?
183 this->popObject(); // pop endif
184 if (Bracket::kPound != fParent->fBracket) {
185 return this->reportError<bool>("expected preprocessor directive");
186 }
187 this->popBracket(); // pop if/else
188 break;
189 default:
190 SkASSERT(0);
191 }
192 return true;
193}
194
195string IncludeParser::className() const {
196 string name(fParent->fName);
197 size_t slash = name.find_last_of("/");
198 if (string::npos == slash) {
199 slash = name.find_last_of("\\");
200 }
201 SkASSERT(string::npos != slash);
202 string result = name.substr(slash);
203 result = result.substr(1, result.size() - 3);
204 return result;
205}
206
Cary Clark884dd7d2017-10-11 10:37:52 -0400207#include <sstream>
208#include <iostream>
209
Cary Clark8032b982017-07-28 11:04:54 -0400210bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400211 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400212 string className = classMapper.first;
213 auto finder = bmhParser.fClassMap.find(className);
214 if (bmhParser.fClassMap.end() == finder) {
215 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400216 continue;
217 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400218 RootDefinition* root = &finder->second;
219 root->clearVisited();
220 }
221 for (auto& classMapper : fIClassMap) {
222 string className = classMapper.first;
223 std::istringstream iss(className);
224 string classStr;
225 string classBase;
226 RootDefinition* root = nullptr;
227 while (std::getline(iss, classStr, ':')) {
228 if (root) {
229 if (!classStr.length()) {
230 continue;
231 }
232 classBase += "::" + classStr;
233 auto finder = root->fBranches.find(classBase);
234 if (root->fBranches.end() != finder) {
235 root = finder->second;
236 } else {
237 SkASSERT(0);
238 }
239 } else {
240 classBase = classStr;
241 auto finder = bmhParser.fClassMap.find(classBase);
242 if (bmhParser.fClassMap.end() != finder) {
243 root = &finder->second;
244 } else {
245 SkASSERT(0);
246 }
247 }
248 }
Cary Clark8032b982017-07-28 11:04:54 -0400249 auto& classMap = classMapper.second;
250 auto& tokens = classMap.fTokens;
251 for (const auto& token : tokens) {
252 if (token.fPrivate) {
253 continue;
254 }
255 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400256 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400257 switch (token.fMarkType) {
258 case MarkType::kMethod: {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400259 if (this->internalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400260 continue;
261 }
Cary Clark8032b982017-07-28 11:04:54 -0400262 if (!def) {
263 string paramName = className + "::";
264 paramName += string(token.fContentStart,
265 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400266 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400267 if (!def && 0 == token.fName.find("operator")) {
268 string operatorName = className + "::";
269 TextParser oper("", token.fStart, token.fContentEnd, 0);
270 const char* start = oper.strnstr("operator", token.fContentEnd);
271 SkASSERT(start);
272 oper.skipTo(start);
273 oper.skipToEndBracket('(');
274 int parens = 0;
275 do {
276 if ('(' == oper.peek()) {
277 ++parens;
278 } else if (')' == oper.peek()) {
279 --parens;
280 }
281 } while (!oper.eof() && oper.next() && parens > 0);
282 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400283 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400284 }
285 }
286 if (!def) {
287 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
288 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
289 string constructorName = className + "::";
290 constructorName += string(token.fContentStart + skip,
291 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400292 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400293 }
294 if (!def && 0 == token.fName.find("SK_")) {
295 string incName = token.fName + "()";
296 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400297 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400298 if (def) {
299 if (def->fName == incName) {
300 def->fVisited = true;
301 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400302 def = root->find(className + "::toString",
303 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400304 if (def) {
305 def->fVisited = true;
306 } else {
307 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500308 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400309 }
310 }
311 break;
312 } else {
313 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500314 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400315 }
316 }
317 }
318 if (!def) {
319 bool allLower = true;
320 for (size_t index = 0; index < token.fName.length(); ++index) {
321 if (!islower(token.fName[index])) {
322 allLower = false;
323 break;
324 }
325 }
326 if (allLower) {
327 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400328 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400329 }
330 }
331 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400332 if ("SK_ATTR_DEPRECATED" == token.fName) {
333 break;
334 }
335 if (0 == token.fName.find("SkDEBUGCODE")) {
336 break;
337 }
338 }
339 if (!def) {
340 // simple method names inside nested classes have a bug and are missing trailing parens
341 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400342 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400343 }
344 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500345 if (!root->fDeprecated) {
346 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
347 fFailed = true;
348 }
Cary Clark8032b982017-07-28 11:04:54 -0400349 break;
350 }
Cary Clark73fa9722017-08-29 17:36:51 -0400351 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400352 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400353 if (MarkType::kDefinedBy == def->fMarkType) {
354 def->fParent->fVisited = true;
355 }
Cary Clark8032b982017-07-28 11:04:54 -0400356 } else {
357 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500358 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400359 }
360 } break;
361 case MarkType::kComment:
362 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400363 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400364 case MarkType::kEnum: {
365 if (!def) {
366 // work backwards from first word to deduce #Enum name
367 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
368 SkAssertResult(firstMember.skipName("enum"));
369 SkAssertResult(firstMember.skipToEndBracket('{'));
370 firstMember.next();
371 firstMember.skipWhiteSpace();
372 SkASSERT('k' == firstMember.peek());
373 const char* savePos = firstMember.fChar;
374 firstMember.skipToNonAlphaNum();
375 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400376 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400377 const char* lastUnderscore = nullptr;
378 do {
379 if (!firstMember.skipToEndBracket('_')) {
380 break;
381 }
382 if (firstMember.fChar > wordEnd) {
383 break;
384 }
385 lastUnderscore = firstMember.fChar;
386 } while (firstMember.next());
387 if (lastUnderscore) {
388 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400389 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400390 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400391 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400392 }
393 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500394 if (!root->fDeprecated) {
395 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
396 fFailed = true;
397 }
Cary Clark8032b982017-07-28 11:04:54 -0400398 break;
399 }
400 }
401 def->fVisited = true;
402 for (auto& child : def->fChildren) {
403 if (MarkType::kCode == child->fMarkType) {
404 def = child;
405 break;
406 }
407 }
408 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500409 if (!root->fDeprecated) {
410 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
411 fFailed = true;
412 }
Cary Clark8032b982017-07-28 11:04:54 -0400413 break;
414 }
415 if (def->crossCheck(token)) {
416 def->fVisited = true;
417 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500418 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
419 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400420 }
421 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400422 string constName = MarkType::kEnumClass == token.fMarkType ?
423 fullName : className;
424 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400425 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400426 if (!def) {
427 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400428 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400429 }
430 if (!def) {
431 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500432 if (!root->fDeprecated) {
433 SkDebugf("const missing from bmh: %s\n", constName.c_str());
434 fFailed = true;
435 }
Cary Clark8032b982017-07-28 11:04:54 -0400436 }
437 } else {
438 def->fVisited = true;
439 }
440 }
441 } break;
442 case MarkType::kMember:
443 if (def) {
444 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500445 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400446 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500447 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400448 }
449 break;
Cary Clark2f466242017-12-11 16:03:17 -0500450 case MarkType::kTypedef:
451 if (def) {
452 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500453 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500454 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500455 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500456 }
457 break;
Cary Clark8032b982017-07-28 11:04:54 -0400458 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400459 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400460 break;
461 }
462 }
463 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500464 int crossChecks = 0;
465 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400466 for (auto& classMapper : fIClassMap) {
467 string className = classMapper.first;
468 auto finder = bmhParser.fClassMap.find(className);
469 if (bmhParser.fClassMap.end() == finder) {
470 continue;
471 }
472 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500473 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500474 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400475 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500476 if (crossChecks) {
477 SkDebugf(".");
478 } else {
479 SkDebugf("cross-check");
480 firstCheck = className;
481 }
482 ++crossChecks;
483 }
484 if (crossChecks) {
485 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500486 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500487 }
488 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400489 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400490 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500491 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400492}
493
494IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
495 const string& name) {
496 string className;
497 const Definition* test = fParent;
498 while (Definition::Type::kFileType != test->fType) {
499 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
500 className = test->fName + "::";
501 break;
502 }
503 test = test->fParent;
504 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400505 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400506 unordered_map<string, IClassDefinition>& map = fIClassMap;
507 IClassDefinition& markupDef = map[className];
508 if (markupDef.fStart) {
509 typedef IClassDefinition* IClassDefPtr;
510 return INHERITED::reportError<IClassDefPtr>("class already defined");
511 }
512 markupDef.fFileName = fFileName;
513 markupDef.fStart = includeDef.fStart;
514 markupDef.fContentStart = includeDef.fStart;
515 markupDef.fName = className;
516 markupDef.fContentEnd = includeDef.fContentEnd;
517 markupDef.fTerminator = includeDef.fTerminator;
518 markupDef.fParent = fParent;
519 markupDef.fLineCount = fLineCount;
520 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
521 MarkType::kStruct : MarkType::kClass;
522 markupDef.fKeyWord = includeDef.fKeyWord;
523 markupDef.fType = Definition::Type::kMark;
524 fParent = &markupDef;
525 return &markupDef;
526}
527
528void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
529 auto& tokens = classDef.fTokens;
530 for (auto& token : tokens) {
531 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
532 continue;
533 }
534 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400535 this->writeString(
536 "# ------------------------------------------------------------------------------");
537 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400538 }
539 switch (token.fMarkType) {
540 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500541 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500542 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400543 break;
544 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400545 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400546 break;
547 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400548 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400549 continue;
550 break;
551 default:
552 SkASSERT(0);
553 }
554 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400555 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400556 this->lf(1);
557 this->writeString("// incomplete");
558 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400559 this->writeEndTag();
560 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400561 this->writeTag("SeeAlso");
562 this->writeSpace();
563 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400564 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500565 switch (token.fMarkType) {
566 case MarkType::kEnum:
567 case MarkType::kEnumClass:
568 this->writeEndTag("Enum");
569 break;
570 case MarkType::kMethod:
571 this->writeEndTag("Method");
572 break;
573 case MarkType::kMember:
574 this->writeEndTag("Member");
575 continue;
576 break;
577 default:
578 SkASSERT(0);
579 }
Cary Clark9174bda2017-09-19 17:39:32 -0400580 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400581 }
582}
Cary Clark9174bda2017-09-19 17:39:32 -0400583void IncludeParser::dumpComment(const Definition& token) {
584 fLineCount = token.fLineCount;
585 fChar = fLine = token.fContentStart;
586 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400587 bool sawParam = false;
588 bool multiline = false;
589 bool sawReturn = false;
590 bool sawComment = false;
591 bool methodHasReturn = false;
592 vector<string> methodParams;
593 vector<string> foundParams;
594 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400595 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
596 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500597 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400598 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500599 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400600 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400601 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500602 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400603 && !methodParser.strnchr('~', methodParser.fEnd);
604 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
605 const char* nextEnd = paren;
606 do {
607 string paramName;
608 methodParser.fChar = nextEnd + 1;
609 methodParser.skipSpace();
610 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
611 continue;
612 }
613 methodParams.push_back(paramName);
614 } while (')' != nextEnd[0]);
615 }
Cary Clark9174bda2017-09-19 17:39:32 -0400616 for (const auto& child : token.fTokens) {
617 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
618 break;
619 }
Cary Clark8032b982017-07-28 11:04:54 -0400620 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400621 if (child.fPrivate) {
622 break;
623 }
Cary Clark8032b982017-07-28 11:04:54 -0400624 if ('@' == child.fContentStart[0]) {
625 TextParser parser(&child);
626 do {
627 parser.next();
628 if (parser.startsWith("param ")) {
629 parser.skipWord("param");
630 const char* parmStart = parser.fChar;
631 parser.skipToSpace();
632 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
633 parser.skipWhiteSpace();
634 do {
635 size_t nextComma = parmName.find(',');
636 string piece;
637 if (string::npos == nextComma) {
638 piece = parmName;
639 parmName = "";
640 } else {
641 piece = parmName.substr(0, nextComma);
642 parmName = parmName.substr(nextComma + 1);
643 }
644 if (sawParam) {
645 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400646 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400647 }
Cary Clark9174bda2017-09-19 17:39:32 -0400648 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400649 } else {
650 if (sawComment) {
651 this->nl();
652 }
653 this->lf(2);
654 }
655 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400656 this->writeTag("Param", piece);
657 this->writeSpace(2);
658 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
659 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400660 sawParam = true;
661 sawComment = false;
662 } while (parmName.length());
663 parser.skipTo(parser.fEnd);
664 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
665 parser.skipWord("return");
666 if ('s' == parser.peek()) {
667 parser.next();
668 }
669 if (sawParam) {
670 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400671 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400672 }
Cary Clark9174bda2017-09-19 17:39:32 -0400673 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400674 }
675 this->checkForMissingParams(methodParams, foundParams);
676 sawParam = false;
677 sawComment = false;
678 multiline = false;
679 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400680 this->writeTag("Return");
681 this->writeSpace(2);
682 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
683 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400684 sawReturn = true;
685 parser.skipTo(parser.fEnd);
686 } else {
687 this->reportError("unexpected doxygen directive");
688 }
689 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400690 } else if (child.length() > 1) {
691 const char* start = child.fContentStart;
692 ptrdiff_t length = child.fContentEnd - start;
693 SkASSERT(length >= 0);
694 while (length && '/' == start[0]) {
695 start += 1;
696 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400697 }
Cary Clark9174bda2017-09-19 17:39:32 -0400698 while (length && '/' == start[length - 1]) {
699 length -= 1;
700 if (length && '*' == start[length - 1]) {
701 length -= 1;
702 }
703 }
704 if (length) {
705 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
706 if (sawParam || sawReturn) {
707 this->indentToColumn(8);
708 }
709 this->writeBlock(length, start);
710 this->writeSpace();
711 sawComment = true;
712 if (sawParam || sawReturn) {
713 multiline = true;
714 }
Cary Clark8032b982017-07-28 11:04:54 -0400715 }
716 }
717 }
718 }
719 if (sawParam || sawReturn) {
720 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400721 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400722 }
Cary Clark9174bda2017-09-19 17:39:32 -0400723 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400724 }
725 if (!sawReturn) {
726 if (!sawParam) {
727 if (sawComment) {
728 this->nl();
729 }
730 this->lf(2);
731 }
732 this->checkForMissingParams(methodParams, foundParams);
733 }
734 if (methodHasReturn != sawReturn) {
735 if (!methodHasReturn) {
736 this->reportError("unexpected doxygen return");
737 } else {
738 if (sawComment) {
739 this->nl();
740 }
741 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400742 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400743 }
744 }
745}
746
Cary Clark2dc84ad2018-01-26 12:56:22 -0500747void IncludeParser::dumpEnum(const Definition& token, const string& name) {
748 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400749 this->lf(2);
750 this->writeString("#Code");
751 this->lfAlways(1);
752 this->indentToColumn(4);
753 this->writeString("enum");
754 this->writeSpace();
755 if ("_anonymous" != token.fName.substr(0, 10)) {
756 this->writeString(token.fName);
757 this->writeSpace();
758 }
759 this->writeString("{");
760 this->lfAlways(1);
761 for (auto& child : token.fChildren) {
762 this->indentToColumn(8);
763 this->writeString(child->fName);
764 if (child->length()) {
765 this->writeSpace();
766 this->writeBlock(child->length(), child->fContentStart);
767 }
768 if (',' != fLastChar) {
769 this->writeString(",");
770 }
771 this->lfAlways(1);
772 }
773 this->indentToColumn(4);
774 this->writeString("};");
775 this->lf(1);
776 this->writeString("##");
777 this->lf(2);
778 this->dumpComment(token);
779 for (auto& child : token.fChildren) {
780 // start here;
781 // get comments before
782 // or after const values
783 this->writeString("#Const");
784 this->writeSpace();
785 this->writeString(child->fName);
786 TextParser val(child);
787 if (!val.eof()) {
788 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
789 val.next();
790 val.skipSpace();
791 const char* valEnd = val.anyOf(",\n");
792 if (!valEnd) {
793 valEnd = val.fEnd;
794 }
795 this->writeSpace();
796 this->writeBlock(valEnd - val.fStart, val.fStart);
797 } else {
798 this->writeSpace();
799 this->writeDefinition(*child);
800 }
801 }
802 this->lf(1);
803 for (auto comment : child->fChildren) {
804 if (MarkType::kComment == comment->fMarkType) {
805 TextParser parser(comment);
806 parser.skipExact("*");
807 parser.skipExact("*");
808 while (!parser.eof() && parser.skipWhiteSpace()) {
809 parser.skipExact("*");
810 parser.skipWhiteSpace();
811 const char* start = parser.fChar;
812 parser.skipToEndBracket('\n');
813 this->lf(1);
814 this->writeBlock(parser.fChar - start, start);
815 }
816 }
817 }
818 this->writeEndTag();
819 }
820 this->lf(2);
821}
822
823void IncludeParser::dumpMethod(const Definition& token) {
824 this->writeString("#Method");
825 this->writeSpace();
826 if ("SK_TO_STRING_NONVIRT" == token.fName) {
827 this->writeString("void toString(SkString* str) const;");
828 this->lf(2);
829 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
830 this->lf(2);
831 this->writeTag("Private");
832 this->lf(1);
833 this->writeString("macro expands to: void toString(SkString* str) const;");
834 this->writeEndTag();
835 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400836 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400837 "Creates string representation. The representation is read by\n"
838 "internal debugging tools. The interface and implementation may be\n"
839 "suppressed by defining SK_IGNORE_TO_STRING.";
840 this->writeBlock(sizeof(desc) - 1, desc);
841 this->lf(2);
842 this->writeTag("Param", "str");
843 this->writeSpace(2);
844 this->writeString("storage for string representation");
845 this->writeSpace();
846 this->writeString("##");
847 this->lf(2);
848 return;
849 }
850 this->writeBlock(token.length(), token.fStart);
851 this->lf(1);
852 this->dumpComment(token);
853}
854
855void IncludeParser::dumpMember(const Definition& token) {
856 this->writeTag("Member");
857 this->writeSpace();
858 this->writeDefinition(token, token.fName, 2);
859 lf(1);
860 for (auto child : token.fChildren) {
861 this->writeDefinition(*child);
862 }
863 this->writeEndTag();
864 lf(2);
865}
866
Cary Clarkd0530ba2017-09-14 11:25:39 -0400867bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400868 for (const auto& member : fIClassMap) {
869 if (string::npos != member.first.find("::")) {
870 continue;
871 }
872 if (!this->dumpTokens(dir, member.first)) {
873 return false;
874 }
875 }
876 return true;
877}
878
Ben Wagner63fd7602017-10-09 15:45:33 -0400879 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400880bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400881 string fileName = dir;
882 if (dir.length() && '/' != dir[dir.length() - 1]) {
883 fileName += '/';
884 }
885 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400886 fOut = fopen(fileName.c_str(), "wb");
887 if (!fOut) {
888 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400889 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400890 }
891 string prefixName = skClassName.substr(0, 2);
892 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
893 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400894 this->writeTagNoLF("Topic", topicName);
895 this->writeTag("Alias", topicName + "_Reference");
896 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400897 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500898 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
899 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400900 this->writeTag(containerType, skClassName);
901 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400902 auto& tokens = classMap.fTokens;
903 for (auto& token : tokens) {
904 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
905 continue;
906 }
Cary Clark9174bda2017-09-19 17:39:32 -0400907 this->writeDefinition(token);
908 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400909 }
910 this->lf(2);
911 string className(skClassName.substr(2));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500912 vector<string> classNames;
913 vector<string> constNames;
914 vector<string> constructorNames;
915 vector<string> memberNames;
916 vector<string> operatorNames;
917 size_t classMaxLen = 0;
918 size_t constMaxLen = 0;
919 size_t constructorMaxLen = 0;
920 size_t memberMaxLen = 0;
921 size_t operatorMaxLen = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400922 for (const auto& oneClass : fIClassMap) {
923 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
924 continue;
925 }
926 string structName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500927 classMaxLen = SkTMax(classMaxLen, structName.length());
928 classNames.emplace_back(structName);
Cary Clark8032b982017-07-28 11:04:54 -0400929 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500930 for (const auto& oneEnum : fIEnumMap) {
931 string enumName = oneEnum.first;
932 constMaxLen = SkTMax(constMaxLen, enumName.length());
933 constNames.emplace_back(enumName);
Cary Clark8032b982017-07-28 11:04:54 -0400934 }
Cary Clark8032b982017-07-28 11:04:54 -0400935 for (const auto& token : classMap.fTokens) {
936 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
937 continue;
938 }
Cary Clark9174bda2017-09-19 17:39:32 -0400939 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400940 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
941 continue;
942 }
Cary Clark9174bda2017-09-19 17:39:32 -0400943 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
944 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500945 constructorMaxLen = SkTMax(constructorMaxLen, name.length());
Cary Clark9174bda2017-09-19 17:39:32 -0400946 constructorNames.emplace_back(name);
947 continue;
948 }
949 if (name.substr(0, 8) == "operator") {
950 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500951 operatorMaxLen = SkTMax(operatorMaxLen, name.length());
Cary Clark9174bda2017-09-19 17:39:32 -0400952 operatorNames.emplace_back(name);
953 continue;
954 }
Cary Clark8032b982017-07-28 11:04:54 -0400955 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
956 continue;
957 }
Cary Clark9174bda2017-09-19 17:39:32 -0400958 if ("SK_TO_STRING_NONVIRT" == name) {
959 name = "toString";
960 }
Cary Clark8032b982017-07-28 11:04:54 -0400961 size_t paren = name.find('(');
962 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark2dc84ad2018-01-26 12:56:22 -0500963 memberMaxLen = SkTMax(memberMaxLen, funcLen);
964 memberNames.emplace_back(name);
Cary Clark8032b982017-07-28 11:04:54 -0400965 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500966 this->writeTag("Topic", "Overview");
967 this->lf(2);
968 this->writeTag("Subtopic", "Subtopics");
969 string classesName = classMaxLen ? "Classes_and_Structs" : "";
970 string constsName = constructorMaxLen ? "Constants" : "";
971 string constructorsName = constructorMaxLen ? "Constructors" : "";
972 string membersName = memberMaxLen ? "Member_Functions" : "";
973 string operatorsName = operatorMaxLen ? "Operators" : "";
974 size_t nameLen = SkTMax(classesName.size(), SkTMax(constsName.size(),
975 SkTMax(constructorsName.size(), SkTMax(membersName.size(), operatorsName.size()))));
976 this->writeTableHeader("name", nameLen, "description");
977 string classDesc = classMaxLen ? "embedded struct and class members" : "";
978 string constDesc = constMaxLen ? "enum and enum class, const values" : "";
979 string constructorDesc = constructorMaxLen ? "functions that construct " + className : "";
980 string memberDesc = memberMaxLen ? "static functions and member methods" : "";
981 string operatorDesc = operatorMaxLen ? "operator overloading methods" : "";
982 size_t descLen = SkTMax(classDesc.size(), SkTMax(constDesc.size(), SkTMax(constructorDesc.size(),
983 SkTMax(memberDesc.size(), operatorDesc.size()))));
984 if (classMaxLen) {
985 this->writeTableRow(nameLen, classesName, descLen, classDesc);
Cary Clark9174bda2017-09-19 17:39:32 -0400986 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500987 if (constMaxLen) {
988 this->writeTableRow(nameLen, constsName, descLen, constDesc);
Cary Clark9174bda2017-09-19 17:39:32 -0400989 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500990 if (constructorMaxLen) {
991 this->writeTableRow(nameLen, constructorsName, descLen, constructorDesc);
992 }
993 if (memberMaxLen) {
994 this->writeTableRow(nameLen, membersName, descLen, memberDesc);
995 }
996 if (operatorMaxLen) {
997 this->writeTableRow(nameLen, operatorsName, descLen, operatorDesc);
Cary Clark8032b982017-07-28 11:04:54 -0400998 }
Cary Clark9174bda2017-09-19 17:39:32 -0400999 this->writeTableTrailer();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001000 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001001 this->lf(2);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001002 if (classMaxLen) {
1003 std::sort(classNames.begin(), classNames.end());
1004 this->writeTag("Subtopic", "Classes_and_Structs");
1005 this->writeTableHeader("name", classMaxLen, "description");
1006 for (auto& name : classNames) {
1007 this->writeTableRow(classMaxLen, name);
1008 }
1009 this->writeTableTrailer();
1010 this->writeEndTag("Subtopic");
1011 this->lf(2);
1012 }
1013 if (constMaxLen) {
1014 std::sort(constNames.begin(), constNames.end());
1015 this->writeTag("Subtopic", "Constants");
1016 this->writeTableHeader("name", constMaxLen, "description");
1017 for (auto& name : constNames) {
1018 this->writeTableRow(constMaxLen, name);
1019 }
1020 this->writeTableTrailer();
1021 this->writeEndTag("Subtopic");
1022 this->lf(2);
1023 }
1024 if (constructorMaxLen) {
1025 std::sort(constructorNames.begin(), constructorNames.end());
1026 this->writeTag("Subtopic", "Constructors");
1027 this->writeTableHeader("name", constructorMaxLen, "description");
1028 for (auto& name : constructorNames) {
1029 this->writeTableRow(constructorMaxLen, name);
1030 }
1031 this->writeTableTrailer();
1032 this->writeEndTag("Subtopic");
1033 this->lf(2);
1034 }
1035 if (operatorMaxLen) {
1036 std::sort(operatorNames.begin(), operatorNames.end());
1037 this->writeTag("Subtopic", "Operators");
1038 this->writeTableHeader("name", operatorMaxLen, "description");
1039 for (auto& name : operatorNames) {
1040 this->writeTableRow(operatorMaxLen, name);
1041 }
1042 this->writeTableTrailer();
1043 this->writeEndTag("Subtopic");
1044 this->lf(2);
1045 }
1046 if (memberMaxLen) {
1047 std::sort(memberNames.begin(), memberNames.end());
1048 this->writeTag("Subtopic", "Member_Functions");
1049 this->writeTableHeader("name", memberMaxLen, "description");
1050 for (auto& name : memberNames) {
1051 size_t paren = name.find('(');
1052 size_t funcLen = string::npos == paren ? name.length() : paren;
1053 this->writeTableRow(memberMaxLen, name.substr(0, funcLen));
1054 }
1055 this->writeTableTrailer();
1056 this->writeEndTag("Subtopic");
1057 this->lf(2);
1058 }
Cary Clark9174bda2017-09-19 17:39:32 -04001059 this->writeEndTag("Topic");
1060 this->lf(2);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001061 for (auto& oneEnum : fIEnumMap) {
1062 this->writeString(
1063 "# ------------------------------------------------------------------------------");
1064 this->dumpEnum(oneEnum.second, oneEnum.first);
1065 this->lf(2);
1066 this->writeTag("Example");
1067 this->lfcr();
1068 this->writeString("// incomplete");
1069 this->writeEndTag();
1070 this->lf(2);
1071 this->writeTag("SeeAlso", "incomplete");
1072 this->lf(2);
1073 this->writeEndTag("Enum", oneEnum.first);
1074 this->lf(2);
1075 }
Cary Clark8032b982017-07-28 11:04:54 -04001076 for (auto& oneClass : fIClassMap) {
1077 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1078 continue;
1079 }
1080 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -04001081 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -04001082 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -04001083 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -05001084 KeyWord keyword = oneClass.second.fKeyWord;
1085 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1086 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001087 this->writeTag(containerType, innerName);
1088 this->lf(2);
1089 this->writeTag("Code");
1090 this->writeEndTag("ToDo", "fill this in manually");
1091 this->writeEndTag();
1092 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001093 for (auto& token : oneClass.second.fTokens) {
1094 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1095 continue;
1096 }
Cary Clark9174bda2017-09-19 17:39:32 -04001097 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001098 }
1099 this->lf(2);
1100 this->dumpClassTokens(oneClass.second);
1101 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001102 this->writeEndTag(containerType, innerName);
1103 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001104 }
1105 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001106 this->writeEndTag(containerType, skClassName);
1107 this->lf(2);
1108 this->writeEndTag("Topic", topicName);
1109 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001110 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001111 SkDebugf("wrote %s\n", fileName.c_str());
1112 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001113}
1114
1115bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1116 // add comment preceding class, if any
1117 const Definition* parent = includeDef.fParent;
1118 int index = includeDef.fParentIndex;
1119 auto wordIter = parent->fTokens.begin();
1120 std::advance(wordIter, index);
1121 SkASSERT(&*wordIter == &includeDef);
1122 while (parent->fTokens.begin() != wordIter) {
1123 auto testIter = std::prev(wordIter);
1124 if (Definition::Type::kWord != testIter->fType
1125 && Definition::Type::kKeyWord != testIter->fType
1126 && (Definition::Type::kBracket != testIter->fType
1127 || Bracket::kAngle != testIter->fBracket)
1128 && (Definition::Type::kPunctuation != testIter->fType
1129 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1130 break;
1131 }
1132 wordIter = testIter;
1133 }
1134 auto commentIter = wordIter;
1135 while (parent->fTokens.begin() != commentIter) {
1136 auto testIter = std::prev(commentIter);
1137 bool isComment = Definition::Type::kBracket == testIter->fType
1138 && (Bracket::kSlashSlash == testIter->fBracket
1139 || Bracket::kSlashStar == testIter->fBracket);
1140 if (!isComment) {
1141 break;
1142 }
1143 commentIter = testIter;
1144 }
1145 while (commentIter != wordIter) {
1146 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1147 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1148 return false;
1149 }
1150 commentIter = std::next(commentIter);
1151 }
1152 return true;
1153}
1154
Cary Clarkbad5ad72017-08-03 17:14:08 -04001155bool IncludeParser::internalName(const Definition& token) const {
1156 return 0 == token.fName.find("internal_")
1157 || 0 == token.fName.find("Internal_")
1158 || 0 == token.fName.find("legacy_")
1159 || 0 == token.fName.find("temporary_")
1160 || 0 == token.fName.find("private_");
1161}
1162
Cary Clark8032b982017-07-28 11:04:54 -04001163// caller calls reportError, so just return false here
1164bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1165 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001166 // parse class header
1167 auto iter = includeDef->fTokens.begin();
1168 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1169 // todo : documentation is ignoring this for now
1170 iter = std::next(iter);
1171 }
1172 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1173 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001174 iter = std::next(iter);
1175 if (iter == includeDef->fTokens.end()) {
1176 return true; // forward declaration only
1177 }
Cary Clark8032b982017-07-28 11:04:54 -04001178 do {
1179 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001180 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001181 }
1182 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1183 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001184 }
Cary Clark8032b982017-07-28 11:04:54 -04001185 } while (static_cast<void>(iter = std::next(iter)), true);
1186 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001187 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001188 }
1189 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1190 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001191 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001192 }
1193 markupDef->fStart = iter->fStart;
1194 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001195 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001196 }
1197// if (1 != includeDef->fChildren.size()) {
1198// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1199// }
1200 includeDef = includeDef->fChildren.front();
1201 iter = includeDef->fTokens.begin();
1202 // skip until public
1203 int publicIndex = 0;
1204 if (IsStruct::kNo == isStruct) {
1205 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1206 size_t publicLen = strlen(publicName);
1207 while (iter != includeDef->fTokens.end()
1208 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1209 || strncmp(iter->fStart, publicName, publicLen))) {
1210 iter = std::next(iter);
1211 ++publicIndex;
1212 }
1213 }
1214 auto childIter = includeDef->fChildren.begin();
1215 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1216 (*childIter)->fPrivate = true;
1217 childIter = std::next(childIter);
1218 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001219 int keyIndex = publicIndex;
1220 KeyWord currentKey = KeyWord::kPublic;
1221 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1222 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001223 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1224 size_t protectedLen = strlen(protectedName);
1225 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1226 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001227 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001228 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001229 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1230 const char* testStart = iter->fStart;
1231 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1232 iter = std::next(iter);
1233 ++keyIndex;
1234 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1235 currentKey = KeyWord::kPublic;
1236 break;
1237 }
1238 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1239 currentKey = KeyWord::kProtected;
1240 break;
1241 }
1242 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1243 currentKey = KeyWord::kPrivate;
1244 break;
1245 }
1246 }
1247 fLastObject = nullptr;
1248 if (KeyWord::kPublic == currentKey) {
1249 if (!this->parseObject(child, markupDef)) {
1250 return false;
1251 }
1252 } else {
1253 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001254 }
Cary Clark73fa9722017-08-29 17:36:51 -04001255 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001256 childIter = std::next(childIter);
1257 }
Cary Clark8032b982017-07-28 11:04:54 -04001258 SkASSERT(fParent->fParent);
1259 fParent = fParent->fParent;
1260 return true;
1261}
1262
1263bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1264 int lineCount, Definition* markupDef) {
1265 TextParser parser(filename, start, end, lineCount);
1266 // parse doxygen if present
1267 if (parser.startsWith("**")) {
1268 parser.next();
1269 parser.next();
1270 parser.skipWhiteSpace();
1271 if ('\\' == parser.peek()) {
1272 parser.next();
1273 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1274 return reportError<bool>("missing object type");
1275 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001276 if (!parser.skipWord(markupDef->fName.c_str()) &&
1277 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001278 return reportError<bool>("missing object name");
1279 }
1280
1281 }
1282 }
1283 // remove leading '*' if present
1284 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1285 while (!parser.eof() && parser.skipWhiteSpace()) {
1286 while ('*' == parser.peek()) {
1287 parser.next();
1288 if (parser.eof()) {
1289 break;
1290 }
1291 parser.skipWhiteSpace();
1292 }
1293 if (parser.eof()) {
1294 break;
1295 }
1296 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001297 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001298 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001299 parser.skipToEndBracket('\n');
1300 }
1301 return true;
1302}
1303
1304bool IncludeParser::parseDefine() {
1305
1306 return true;
1307}
1308
1309bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001310 TextParser parser(child);
1311 parser.skipToEndBracket('{');
1312 if (parser.eof()) {
1313 return true; // if enum is a forward declaration, do nothing
1314 }
1315 parser.next();
1316 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001317 if (child->fTokens.size() > 0) {
1318 auto token = child->fTokens.begin();
1319 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1320 token = token->fTokens.begin();
1321 }
1322 if (Definition::Type::kWord == token->fType) {
1323 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1324 }
1325 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001326 Definition* markupChild;
1327 if (!markupDef) {
1328 auto finder = fIEnumMap.find(nameStr);
1329 if (fIEnumMap.end() != finder) {
1330 return child->reportError<bool>("duplicate global enum name");
1331 }
1332 markupChild = &fIEnumMap[nameStr];
1333 markupChild->fContentStart = child->fContentStart;
1334 markupChild->fName = nameStr;
1335 markupChild->fFiddle = nameStr;
1336 markupChild->fContentEnd = child->fContentEnd;
1337 markupChild->fFileName = child->fFileName;
1338 markupChild->fLineCount = child->fLineCount;
1339 } else {
1340 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001341 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001342 markupChild = &markupDef->fTokens.back();
1343 }
Cary Clark8032b982017-07-28 11:04:54 -04001344 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1345 markupChild->fKeyWord = KeyWord::kEnum;
1346 TextParser enumName(child);
1347 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001348 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001349 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001350 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001351 markupChild->fMarkType = MarkType::kEnumClass;
1352 }
Cary Clark8032b982017-07-28 11:04:54 -04001353 const char* nameStart = enumName.fChar;
1354 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001355 if (markupDef) {
1356 markupChild->fName = markupDef->fName + "::";
1357 }
1358 markupChild->fName += string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark8032b982017-07-28 11:04:54 -04001359 if (!this->findComments(*child, markupChild)) {
1360 return false;
1361 }
Cary Clark8032b982017-07-28 11:04:54 -04001362 const char* dataEnd;
1363 do {
Cary Clark8032b982017-07-28 11:04:54 -04001364 parser.skipWhiteSpace();
1365 if ('}' == parser.peek()) {
1366 break;
1367 }
1368 Definition* comment = nullptr;
1369 // note that comment, if any, can be before or after (on the same line, though) as member
1370 if ('#' == parser.peek()) {
1371 // fixme: handle preprecessor, but just skip it for now
1372 parser.skipToLineStart();
1373 }
1374 while (parser.startsWith("/*") || parser.startsWith("//")) {
1375 parser.next();
1376 const char* start = parser.fChar;
1377 const char* end;
1378 if ('*' == parser.peek()) {
1379 end = parser.strnstr("*/", parser.fEnd);
1380 parser.fChar = end;
1381 parser.next();
1382 parser.next();
1383 } else {
1384 end = parser.trimmedLineEnd();
1385 parser.skipToLineStart();
1386 }
1387 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001388 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001389 comment = &markupChild->fTokens.back();
1390 comment->fTerminator = end;
1391 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1392 return false;
1393 }
1394 parser.skipWhiteSpace();
1395 }
1396 parser.skipWhiteSpace();
1397 const char* memberStart = parser.fChar;
1398 if ('}' == memberStart[0]) {
1399 break;
1400 }
Cary Clark9174bda2017-09-19 17:39:32 -04001401 // if there's comment on same the line as member def, output first as if it was before
1402
Cary Clark8032b982017-07-28 11:04:54 -04001403 parser.skipToNonAlphaNum();
1404 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001405 if (parser.eof() || !parser.skipWhiteSpace()) {
1406 return this->reportError<bool>("enum member must end with comma 1");
1407 }
Cary Clark8032b982017-07-28 11:04:54 -04001408 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001409 if ('=' == parser.peek()) {
1410 parser.skipToEndBracket(',');
1411 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001412 if (!parser.eof() && '#' == parser.peek()) {
1413 // fixme: handle preprecessor, but just skip it for now
1414 continue;
1415 }
Cary Clark9174bda2017-09-19 17:39:32 -04001416 if (parser.eof() || ',' != parser.peek()) {
1417 return this->reportError<bool>("enum member must end with comma 2");
1418 }
1419 dataEnd = parser.fChar;
1420 const char* start = parser.anyOf("/\n");
1421 SkASSERT(start);
1422 parser.skipTo(start);
1423 if ('/' == parser.next()) {
1424 char slashStar = parser.next();
1425 if ('/' == slashStar || '*' == slashStar) {
1426 TextParser::Save save(&parser);
1427 char doxCheck = parser.next();
1428 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1429 save.restore();
1430 }
1431 }
1432 parser.skipWhiteSpace();
1433 const char* commentStart = parser.fChar;
1434 if ('/' == slashStar) {
1435 parser.skipToEndBracket('\n');
1436 } else {
1437 parser.skipToEndBracket("*/");
1438 }
1439 SkASSERT(!parser.eof());
1440 const char* commentEnd = parser.fChar;
1441 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001442 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001443 comment = &markupChild->fTokens.back();
1444 comment->fTerminator = commentEnd;
1445 }
Cary Clark8032b982017-07-28 11:04:54 -04001446 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001447 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001448 Definition* member = &markupChild->fTokens.back();
1449 member->fName = memberName;
1450 if (comment) {
1451 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001452 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001453 }
1454 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001455 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001456 for (auto outsideMember : child->fChildren) {
1457 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001458 continue;
1459 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001460 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1461 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001462 continue;
1463 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001464 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1465 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001466 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001467 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001468 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001469 // FIXME: ? add comment as well ?
1470 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001471 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001472 if (markupDef) {
1473 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1474 SkASSERT(classDef.fStart);
1475 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1476 markupChild->fName = uniqueName;
1477 classDef.fEnums[uniqueName] = markupChild;
1478 }
Cary Clark8032b982017-07-28 11:04:54 -04001479 return true;
1480}
1481
1482bool IncludeParser::parseInclude(const string& name) {
1483 fParent = &fIncludeMap[name];
1484 fParent->fName = name;
1485 fParent->fFileName = fFileName;
1486 fParent->fType = Definition::Type::kFileType;
1487 fParent->fContentStart = fChar;
1488 fParent->fContentEnd = fEnd;
1489 // parse include file into tree
1490 while (fChar < fEnd) {
1491 if (!this->parseChar()) {
1492 return false;
1493 }
1494 }
1495 // parse tree and add named objects to maps
1496 fParent = &fIncludeMap[name];
1497 if (!this->parseObjects(fParent, nullptr)) {
1498 return false;
1499 }
1500 return true;
1501}
1502
1503bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1504 const char* typeStart = child->fChildren[0]->fContentStart;
1505 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001506 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001507 Definition* markupChild = &markupDef->fTokens.back();
1508 TextParser nameParser(child);
1509 nameParser.skipToNonAlphaNum();
1510 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1511 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1512 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1513 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001514 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001515 classDef.fMembers[uniqueName] = markupChild;
1516 if (child->fParentIndex >= 2) {
1517 auto comment = child->fParent->fTokens.begin();
1518 std::advance(comment, child->fParentIndex - 2);
1519 if (Definition::Type::kBracket == comment->fType
1520 && (Bracket::kSlashStar == comment->fBracket
1521 || Bracket::kSlashSlash == comment->fBracket)) {
1522 TextParser parser(&*comment);
1523 do {
1524 parser.skipToAlpha();
1525 if (parser.eof()) {
1526 break;
1527 }
1528 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001529 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001530 if (Bracket::kSlashStar == comment->fBracket) {
1531 const char* commentEnd = parser.strnstr("*/", end);
1532 if (commentEnd) {
1533 end = commentEnd;
1534 }
1535 }
1536 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001537 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001538 Definition* commentChild = &markupDef->fTokens.back();
1539 markupChild->fChildren.emplace_back(commentChild);
1540 parser.skipTo(end);
1541 } while (!parser.eof());
1542 }
1543 }
1544 return true;
1545}
1546
1547bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1548 auto tokenIter = child->fParent->fTokens.begin();
1549 std::advance(tokenIter, child->fParentIndex);
1550 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001551 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001552 bool addConst = false;
1553 auto operatorCheck = tokenIter;
1554 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1555 operatorCheck = std::prev(tokenIter);
1556 }
1557 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001558 auto closeParen = std::next(tokenIter);
1559 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1560 '(' == closeParen->fContentStart[0]);
1561 nameEnd = closeParen->fContentEnd + 1;
1562 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001563 if (Definition::Type::kKeyWord == closeParen->fType &&
1564 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001565 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001566 }
Cary Clarka560c472017-11-27 10:44:06 -05001567 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001568 }
1569 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001570 if (addConst) {
1571 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001572 }
Cary Clark8032b982017-07-28 11:04:54 -04001573 while (tokenIter != child->fParent->fTokens.begin()) {
1574 auto testIter = std::prev(tokenIter);
1575 switch (testIter->fType) {
1576 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001577 if (testIter == child->fParent->fTokens.begin() &&
1578 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1579 KeyWord::kIfndef == child->fParent->fKeyWord ||
1580 KeyWord::kIf == child->fParent->fKeyWord)) {
1581 std::next(tokenIter);
1582 break;
1583 }
Cary Clark8032b982017-07-28 11:04:54 -04001584 goto keepGoing;
1585 case Definition::Type::kKeyWord: {
1586 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1587 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1588 goto keepGoing;
1589 }
1590 } break;
1591 case Definition::Type::kBracket:
1592 if (Bracket::kAngle == testIter->fBracket) {
1593 goto keepGoing;
1594 }
1595 break;
1596 case Definition::Type::kPunctuation:
1597 if (Punctuation::kSemicolon == testIter->fPunctuation
1598 || Punctuation::kLeftBrace == testIter->fPunctuation
1599 || Punctuation::kColon == testIter->fPunctuation) {
1600 break;
1601 }
1602 keepGoing:
1603 tokenIter = testIter;
1604 continue;
1605 default:
1606 break;
1607 }
1608 break;
1609 }
1610 tokenIter->fName = nameStr;
1611 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001612 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001613 auto testIter = child->fParent->fTokens.begin();
1614 SkASSERT(child->fParentIndex > 0);
1615 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001616 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1617 0 == tokenIter->fParentIndex) {
1618 tokenIter = std::next(tokenIter);
1619 }
Cary Clark8032b982017-07-28 11:04:54 -04001620 const char* start = tokenIter->fContentStart;
1621 const char* end = tokenIter->fContentEnd;
1622 const char kDebugCodeStr[] = "SkDEBUGCODE";
1623 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1624 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1625 std::advance(testIter, 1);
1626 start = testIter->fContentStart + 1;
1627 end = testIter->fContentEnd - 1;
1628 } else {
1629 end = testIter->fContentEnd;
1630 while (testIter != child->fParent->fTokens.end()) {
1631 testIter = std::next(testIter);
1632 switch (testIter->fType) {
1633 case Definition::Type::kPunctuation:
1634 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1635 || Punctuation::kLeftBrace == testIter->fPunctuation
1636 || Punctuation::kColon == testIter->fPunctuation);
1637 end = testIter->fStart;
1638 break;
1639 case Definition::Type::kKeyWord: {
1640 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1641 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1642 continue;
1643 }
1644 } break;
1645 default:
1646 continue;
1647 }
1648 break;
1649 }
1650 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001651 while (end > start && ' ' >= end[-1]) {
1652 --end;
1653 }
Cary Clark9174bda2017-09-19 17:39:32 -04001654 if (!markupDef) {
1655 auto parentIter = child->fParent->fTokens.begin();
1656 SkASSERT(child->fParentIndex > 0);
1657 std::advance(parentIter, child->fParentIndex - 1);
1658 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001659 TextParser nameParser(methodName);
1660 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001661 return true; // expect this is inline class definition outside of class
1662 }
Cary Clarka560c472017-11-27 10:44:06 -05001663 string name(nameParser.fLine, nameParser.lineLength());
1664 auto finder = fIFunctionMap.find(name);
1665 if (fIFunctionMap.end() != finder) {
1666 // create unique name
1667 SkASSERT(0); // incomplete
1668 }
1669 auto globalFunction = &fIFunctionMap[name];
1670 globalFunction->fContentStart = start;
1671 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001672 globalFunction->fFiddle = name;
1673 globalFunction->fContentEnd = end;
1674 globalFunction->fMarkType = MarkType::kMethod;
1675 globalFunction->fLineCount = tokenIter->fLineCount;
1676 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001677 }
Cary Clark8032b982017-07-28 11:04:54 -04001678 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001679 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001680 Definition* markupChild = &markupDef->fTokens.back();
1681 // do find instead -- I wonder if there is a way to prevent this in c++
1682 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1683 SkASSERT(classDef.fStart);
1684 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1685 markupChild->fName = uniqueName;
1686 if (!this->findComments(*child, markupChild)) {
1687 return false;
1688 }
1689 classDef.fMethods[uniqueName] = markupChild;
1690 return true;
1691}
1692
Cary Clark8032b982017-07-28 11:04:54 -04001693bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1694 for (auto& child : parent->fChildren) {
1695 if (!this->parseObject(child, markupDef)) {
1696 return false;
1697 }
1698 }
1699 return true;
1700}
1701
1702bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1703 // set up for error reporting
1704 fLine = fChar = child->fStart;
1705 fEnd = child->fContentEnd;
1706 // todo: put original line number in child as well
1707 switch (child->fType) {
1708 case Definition::Type::kKeyWord:
1709 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001710 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001711 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001712 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001713 }
1714 break;
1715 case KeyWord::kEnum:
1716 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001717 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001718 }
1719 break;
1720 case KeyWord::kStruct:
1721 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001722 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001723 }
1724 break;
1725 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001726 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001727 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001728 }
1729 break;
1730 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001731 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001732 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001733 }
1734 break;
1735 case KeyWord::kUnion:
1736 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001737 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001738 }
1739 break;
1740 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001741 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001742 }
1743 break;
1744 case Definition::Type::kBracket:
1745 switch (child->fBracket) {
1746 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001747 if (fLastObject) {
1748 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1749 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001750 if (!checkDeprecated.eof()) {
1751 checkDeprecated.skipWhiteSpace();
1752 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1753 break;
1754 }
1755 }
1756 }
1757 {
1758 auto tokenIter = child->fParent->fTokens.begin();
1759 std::advance(tokenIter, child->fParentIndex);
1760 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05001761 TextParser previousToken(&*tokenIter);
1762 if (previousToken.startsWith("SK_ATTR_DEPRECATED")) {
Cary Clark73fa9722017-08-29 17:36:51 -04001763 break;
1764 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05001765 if (Bracket::kPound == child->fParent->fBracket &&
1766 KeyWord::kIf == child->fParent->fKeyWord) {
1767 // TODO: this will skip methods named defined() -- for the
1768 // moment there aren't any
1769 if (previousToken.startsWith("defined")) {
1770 break;
1771 }
1772 }
Cary Clark73fa9722017-08-29 17:36:51 -04001773 }
Cary Clark8032b982017-07-28 11:04:54 -04001774 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001775 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001776 }
Cary Clark73fa9722017-08-29 17:36:51 -04001777 break;
Cary Clark8032b982017-07-28 11:04:54 -04001778 case Bracket::kSlashSlash:
1779 case Bracket::kSlashStar:
1780 // comments are picked up by parsing objects first
1781 break;
1782 case Bracket::kPound:
1783 // special-case the #xxx xxx_DEFINED entries
1784 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001785 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001786 case KeyWord::kIfndef:
1787 case KeyWord::kIfdef:
1788 if (child->boilerplateIfDef(fParent)) {
1789 if (!this->parseObjects(child, markupDef)) {
1790 return false;
1791 }
1792 break;
1793 }
1794 goto preproError;
1795 case KeyWord::kDefine:
1796 if (child->boilerplateDef(fParent)) {
1797 break;
1798 }
1799 goto preproError;
1800 case KeyWord::kEndif:
1801 if (child->boilerplateEndIf()) {
1802 break;
1803 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001804 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04001805 case KeyWord::kInclude:
1806 // ignored for now
1807 break;
1808 case KeyWord::kElse:
1809 case KeyWord::kElif:
1810 // todo: handle these
1811 break;
1812 default:
1813 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001814 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001815 }
1816 break;
1817 case Bracket::kAngle:
1818 // pick up templated function pieces when method is found
1819 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001820 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001821 if (!this->parseObjects(child, markupDef)) {
1822 return false;
1823 }
Cary Clark73fa9722017-08-29 17:36:51 -04001824 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001825 case Bracket::kSquare: {
1826 // check to see if parent is operator, the only case we handle so far
1827 auto prev = child->fParent->fTokens.begin();
1828 std::advance(prev, child->fParentIndex - 1);
1829 if (KeyWord::kOperator != prev->fKeyWord) {
1830 return child->reportError<bool>("expected operator overload");
1831 }
1832 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001833 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001834 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001835 }
1836 break;
1837 case Definition::Type::kWord:
1838 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001839 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001840 }
1841 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001842 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001843 }
1844 break;
1845 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001846 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001847 break;
1848 }
1849 return true;
1850}
1851
Cary Clarkbbfda252018-03-09 15:32:01 -05001852bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
1853 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04001854}
1855
Cary Clark2f466242017-12-11 16:03:17 -05001856bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1857 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05001858 typedefParser.skipExact("typedef");
1859 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05001860 string nameStr = typedefParser.typedefName();
1861 if (!markupDef) {
1862 Definition& typedefDef = fITypedefMap[nameStr];
1863 SkASSERT(!typedefDef.fStart);
1864 typedefDef.fStart = child->fContentStart;
1865 typedefDef.fContentStart = child->fContentStart;
1866 typedefDef.fName = nameStr;
1867 typedefDef.fFiddle = nameStr;
1868 typedefDef.fContentEnd = child->fContentEnd;
1869 typedefDef.fTerminator = child->fContentEnd;
1870 typedefDef.fMarkType = MarkType::kTypedef;
1871 typedefDef.fLineCount = child->fLineCount;
1872 return true;
1873 }
1874 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001875 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05001876 Definition* markupChild = &markupDef->fTokens.back();
1877 markupChild->fName = nameStr;
1878 markupChild->fTerminator = markupChild->fContentEnd;
1879 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1880 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001881 return true;
1882}
1883
1884bool IncludeParser::parseUnion() {
1885
1886 return true;
1887}
1888
1889bool IncludeParser::parseChar() {
1890 char test = *fChar;
1891 if ('\\' == fPrev) {
1892 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001893// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001894 fLine = fChar + 1;
1895 }
1896 goto done;
1897 }
1898 switch (test) {
1899 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001900// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001901 fLine = fChar + 1;
1902 if (fInChar) {
1903 return reportError<bool>("malformed char");
1904 }
1905 if (fInString) {
1906 return reportError<bool>("malformed string");
1907 }
1908 if (!this->checkForWord()) {
1909 return false;
1910 }
1911 if (Bracket::kPound == this->topBracket()) {
1912 KeyWord keyWord = fParent->fKeyWord;
1913 if (KeyWord::kNone == keyWord) {
1914 return this->reportError<bool>("unhandled preprocessor directive");
1915 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001916 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001917 this->popBracket();
1918 }
1919 } else if (Bracket::kSlashSlash == this->topBracket()) {
1920 this->popBracket();
1921 }
1922 break;
1923 case '*':
1924 if (!fInCharCommentString && '/' == fPrev) {
1925 this->pushBracket(Bracket::kSlashStar);
1926 }
1927 if (!this->checkForWord()) {
1928 return false;
1929 }
1930 if (!fInCharCommentString) {
1931 this->addPunctuation(Punctuation::kAsterisk);
1932 }
1933 break;
1934 case '/':
1935 if ('*' == fPrev) {
1936 if (!fInCharCommentString) {
1937 return reportError<bool>("malformed closing comment");
1938 }
1939 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001940 TextParser::Save save(this);
1941 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001942 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001943 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001944 }
1945 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001946 }
Cary Clark8032b982017-07-28 11:04:54 -04001947 if (!fInCharCommentString && '/' == fPrev) {
1948 this->pushBracket(Bracket::kSlashSlash);
1949 break;
1950 }
1951 if (!this->checkForWord()) {
1952 return false;
1953 }
1954 break;
1955 case '\'':
1956 if (Bracket::kChar == this->topBracket()) {
1957 this->popBracket();
1958 } else if (!fInComment && !fInString) {
1959 if (fIncludeWord) {
1960 return this->reportError<bool>("word then single-quote");
1961 }
1962 this->pushBracket(Bracket::kChar);
1963 }
1964 break;
1965 case '\"':
1966 if (Bracket::kString == this->topBracket()) {
1967 this->popBracket();
1968 } else if (!fInComment && !fInChar) {
1969 if (fIncludeWord) {
1970 return this->reportError<bool>("word then double-quote");
1971 }
1972 this->pushBracket(Bracket::kString);
1973 }
1974 break;
1975 case ':':
1976 case '(':
1977 case '[':
1978 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001979 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1980 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1981 this->pushBracket(Bracket::kDebugCode);
1982 break;
1983 }
Cary Clark8032b982017-07-28 11:04:54 -04001984 if (fInCharCommentString) {
1985 break;
1986 }
1987 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1988 break;
1989 }
1990 if (!fInBrace) {
1991 if (!this->checkForWord()) {
1992 return false;
1993 }
1994 if (':' == test && !fInFunction) {
1995 break;
1996 }
1997 if ('{' == test) {
1998 this->addPunctuation(Punctuation::kLeftBrace);
1999 } else if (':' == test) {
2000 this->addPunctuation(Punctuation::kColon);
2001 }
2002 }
2003 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2004 && Bracket::kColon == fInBrace->fBracket) {
2005 Definition* braceParent = fParent->fParent;
2006 braceParent->fChildren.pop_back();
2007 braceParent->fTokens.pop_back();
2008 fParent = braceParent;
2009 fInBrace = nullptr;
2010 }
2011 this->pushBracket(
2012 '(' == test ? Bracket::kParen :
2013 '[' == test ? Bracket::kSquare :
2014 '{' == test ? Bracket::kBrace :
2015 Bracket::kColon);
2016 if (!fInBrace
2017 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2018 && fInFunction) {
2019 fInBrace = fParent;
2020 }
2021 } break;
2022 case '<':
2023 if (fInCharCommentString || fInBrace) {
2024 break;
2025 }
2026 if (!this->checkForWord()) {
2027 return false;
2028 }
2029 if (fInEnum) {
2030 break;
2031 }
2032 this->pushBracket(Bracket::kAngle);
2033 break;
2034 case ')':
2035 case ']':
2036 case '}': {
2037 if (fInCharCommentString) {
2038 break;
2039 }
2040 if (!fInBrace) {
2041 if (!this->checkForWord()) {
2042 return false;
2043 }
2044 }
2045 bool popBraceParent = fInBrace == fParent;
2046 if ((')' == test ? Bracket::kParen :
2047 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2048 this->popBracket();
2049 if (!fInFunction) {
2050 bool deprecatedMacro = false;
2051 if (')' == test) {
2052 auto iter = fParent->fTokens.end();
2053 bool lookForWord = false;
2054 while (fParent->fTokens.begin() != iter) {
2055 --iter;
2056 if (lookForWord) {
2057 if (Definition::Type::kWord != iter->fType) {
2058 break;
2059 }
2060 string word(iter->fContentStart, iter->length());
2061 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
2062 deprecatedMacro = true;
2063 // remove macro paren (would confuse method parsing later)
Ben Wagner63fd7602017-10-09 15:45:33 -04002064 fParent->fTokens.pop_back();
Cary Clark8032b982017-07-28 11:04:54 -04002065 fParent->fChildren.pop_back();
2066 }
2067 break;
2068 }
2069 if (Definition::Type::kBracket != iter->fType) {
2070 break;
2071 }
2072 if (Bracket::kParen != iter->fBracket) {
2073 break;
2074 }
2075 lookForWord = true;
2076 }
2077 }
2078 fInFunction = ')' == test && !deprecatedMacro;
2079 } else {
2080 fInFunction = '}' != test;
2081 }
Cary Clark73fa9722017-08-29 17:36:51 -04002082 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2083 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002084 } else {
2085 return reportError<bool>("malformed close bracket");
2086 }
2087 if (popBraceParent) {
2088 Definition* braceParent = fInBrace->fParent;
2089 braceParent->fChildren.pop_back();
2090 braceParent->fTokens.pop_back();
2091 fInBrace = nullptr;
2092 }
2093 } break;
2094 case '>':
2095 if (fInCharCommentString || fInBrace) {
2096 break;
2097 }
2098 if (!this->checkForWord()) {
2099 return false;
2100 }
2101 if (fInEnum) {
2102 break;
2103 }
Cary Clarka560c472017-11-27 10:44:06 -05002104 if (Bracket::kPound == this->topBracket()) {
2105 break;
2106 }
Cary Clark8032b982017-07-28 11:04:54 -04002107 if (Bracket::kAngle == this->topBracket()) {
2108 this->popBracket();
2109 } else {
2110 return reportError<bool>("malformed close angle bracket");
2111 }
2112 break;
2113 case '#': {
2114 if (fInCharCommentString || fInBrace) {
2115 break;
2116 }
2117 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2118 this->pushBracket(Bracket::kPound);
2119 break;
2120 }
2121 case '&':
2122 case ',':
2123 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05002124 case '+':
2125 case '=':
2126 case '-':
2127 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002128 if (fInCharCommentString || fInBrace) {
2129 break;
2130 }
2131 if (!this->checkForWord()) {
2132 return false;
2133 }
2134 break;
2135 case ';':
2136 if (fInCharCommentString || fInBrace) {
2137 break;
2138 }
2139 if (!this->checkForWord()) {
2140 return false;
2141 }
2142 if (Definition::Type::kKeyWord == fParent->fType
2143 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002144 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2145 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002146 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2147 this->popObject();
2148 }
Cary Clark8032b982017-07-28 11:04:54 -04002149 if (KeyWord::kEnum == fParent->fKeyWord) {
2150 fInEnum = false;
2151 }
2152 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002153 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2154 this->popObject();
2155 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002156 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002157 } else if (Definition::Type::kBracket == fParent->fType
2158 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2159 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2160 list<Definition>::iterator baseIter = fParent->fTokens.end();
2161 list<Definition>::iterator namedIter = fParent->fTokens.end();
2162 for (auto tokenIter = fParent->fTokens.end();
2163 fParent->fTokens.begin() != tokenIter--; ) {
2164 if (tokenIter->fLineCount == fLineCount) {
2165 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2166 if (namedIter != fParent->fTokens.end()) {
2167 return reportError<bool>("found two named member tokens");
2168 }
2169 namedIter = tokenIter;
2170 }
2171 baseIter = tokenIter;
2172 } else {
2173 break;
2174 }
2175 }
2176 // FIXME: if a member definition spans multiple lines, this won't work
2177 if (namedIter != fParent->fTokens.end()) {
2178 if (baseIter == namedIter) {
2179 return this->reportError<bool>("expected type before named token");
2180 }
2181 Definition* member = &*namedIter;
2182 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002183 if (!member->fTerminator) {
2184 member->fTerminator = member->fContentEnd;
2185 }
Cary Clark8032b982017-07-28 11:04:54 -04002186 fParent->fChildren.push_back(member);
2187 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2188 member->fChildren.push_back(&*nameType);
2189 }
Cary Clark8032b982017-07-28 11:04:54 -04002190 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002191 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002192 } else if (fParent->fChildren.size() > 0) {
2193 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002194 Definition* priorEnum = fPriorEnum;
2195 fPriorEnum = nullptr;
2196 if (!priorEnum) {
2197 while (fParent->fChildren.begin() != lastIter) {
2198 std::advance(lastIter, -1);
2199 priorEnum = *lastIter;
2200 if (Definition::Type::kBracket != priorEnum->fType ||
2201 (Bracket::kSlashSlash != priorEnum->fBracket
2202 && Bracket::kSlashStar != priorEnum->fBracket)) {
2203 break;
2204 }
Cary Clark8032b982017-07-28 11:04:54 -04002205 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002206 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002207 }
2208 if (Definition::Type::kKeyWord == priorEnum->fType
2209 && KeyWord::kEnum == priorEnum->fKeyWord) {
2210 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002211 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002212 while (tokenWalker != fParent->fTokens.end()) {
2213 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002214 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002215 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2216 break;
2217 }
2218 }
2219 while (tokenWalker != fParent->fTokens.end()) {
2220 std::advance(tokenWalker, 1);
2221 const Definition* test = &*tokenWalker;
2222 if (Definition::Type::kBracket != test->fType ||
2223 (Bracket::kSlashSlash != test->fBracket
2224 && Bracket::kSlashStar != test->fBracket)) {
2225 break;
2226 }
2227 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002228 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002229 Definition* start = &*tokenWalker;
2230 bool foundExpected = true;
2231 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2232 const Definition* test = &*tokenWalker;
2233 if (expected != test->fKeyWord) {
2234 foundExpected = false;
2235 break;
2236 }
2237 if (tokenWalker == fParent->fTokens.end()) {
2238 break;
2239 }
2240 std::advance(tokenWalker, 1);
2241 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002242 if (!foundExpected) {
2243 foundExpected = true;
2244 tokenWalker = saveTokenWalker;
2245 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2246 const Definition* test = &*tokenWalker;
2247 if (expected != test->fKeyWord) {
2248 foundExpected = false;
2249 break;
2250 }
2251 if (tokenWalker == fParent->fTokens.end()) {
2252 break;
2253 }
2254 if (KeyWord::kNone != expected) {
2255 std::advance(tokenWalker, 1);
2256 }
2257 }
2258 if (foundExpected) {
2259 auto nameToken = priorEnum->fTokens.begin();
2260 string enumName = string(nameToken->fContentStart,
2261 nameToken->fContentEnd - nameToken->fContentStart);
2262 const Definition* test = &*tokenWalker;
2263 string constType = string(test->fContentStart,
2264 test->fContentEnd - test->fContentStart);
2265 if (enumName != constType) {
2266 foundExpected = false;
2267 } else {
2268 std::advance(tokenWalker, 1);
2269 }
2270 }
2271 }
Cary Clark8032b982017-07-28 11:04:54 -04002272 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2273 const char* nameStart = tokenWalker->fStart;
2274 std::advance(tokenWalker, 1);
2275 if (tokenWalker != fParent->fTokens.end()) {
2276 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2277 tp.skipToNonAlphaNum();
2278 start->fName = string(nameStart, tp.fChar - nameStart);
2279 start->fContentEnd = fChar;
2280 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002281 fPriorEnum = priorEnum;
2282 }
Cary Clark8032b982017-07-28 11:04:54 -04002283 }
2284 }
2285 }
2286 this->addPunctuation(Punctuation::kSemicolon);
2287 fInFunction = false;
2288 break;
2289 case '~':
2290 if (fInEnum) {
2291 break;
2292 }
2293 case '0': case '1': case '2': case '3': case '4':
2294 case '5': case '6': case '7': case '8': case '9':
2295 // TODO: don't want to parse numbers, but do need to track for enum defs
2296 // break;
2297 case 'A': case 'B': case 'C': case 'D': case 'E':
2298 case 'F': case 'G': case 'H': case 'I': case 'J':
2299 case 'K': case 'L': case 'M': case 'N': case 'O':
2300 case 'P': case 'Q': case 'R': case 'S': case 'T':
2301 case 'U': case 'V': case 'W': case 'X': case 'Y':
2302 case 'Z': case '_':
2303 case 'a': case 'b': case 'c': case 'd': case 'e':
2304 case 'f': case 'g': case 'h': case 'i': case 'j':
2305 case 'k': case 'l': case 'm': case 'n': case 'o':
2306 case 'p': case 'q': case 'r': case 's': case 't':
2307 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002308 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002309 if (fInCharCommentString || fInBrace) {
2310 break;
2311 }
2312 if (!fIncludeWord) {
2313 fIncludeWord = fChar;
2314 }
2315 break;
2316 }
2317done:
2318 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002319 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002320 return true;
2321}
2322
2323void IncludeParser::validate() const {
2324 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2325 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2326 }
2327 IncludeParser::ValidateKeyWords();
2328}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002329
2330void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2331 if (!sk_isdir(includes)) {
2332 IncludeParser::RemoveOneFile(docs, includes);
2333 } else {
2334 SkOSFile::Iter it(includes, ".h");
2335 for (SkString file; it.next(&file); ) {
2336 SkString p = SkOSPath::Join(includes, file.c_str());
2337 const char* hunk = p.c_str();
2338 if (!SkStrEndsWith(hunk, ".h")) {
2339 continue;
2340 }
2341 IncludeParser::RemoveOneFile(docs, hunk);
2342 }
2343 }
2344}
2345
2346void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2347 const char* lastForward = strrchr(includesFile, '/');
2348 const char* lastBackward = strrchr(includesFile, '\\');
2349 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2350 if (!last) {
2351 last = includesFile;
2352 } else {
2353 last += 1;
2354 }
2355 SkString baseName(last);
2356 SkASSERT(baseName.endsWith(".h"));
2357 baseName.remove(baseName.size() - 2, 2);
2358 baseName.append("_Reference.bmh");
2359 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2360 remove(fullName.c_str());
2361}