blob: b4b0f0f996f991bd03227fa0bf52d053b5ac1ecc [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) {
90 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent);
91 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 Clark8032b982017-07-28 11:04:54 -0400345 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500346 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400347 break;
348 }
Cary Clark73fa9722017-08-29 17:36:51 -0400349 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400350 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400351 if (MarkType::kDefinedBy == def->fMarkType) {
352 def->fParent->fVisited = true;
353 }
Cary Clark8032b982017-07-28 11:04:54 -0400354 } else {
355 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500356 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400357 }
358 } break;
359 case MarkType::kComment:
360 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400361 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400362 case MarkType::kEnum: {
363 if (!def) {
364 // work backwards from first word to deduce #Enum name
365 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
366 SkAssertResult(firstMember.skipName("enum"));
367 SkAssertResult(firstMember.skipToEndBracket('{'));
368 firstMember.next();
369 firstMember.skipWhiteSpace();
370 SkASSERT('k' == firstMember.peek());
371 const char* savePos = firstMember.fChar;
372 firstMember.skipToNonAlphaNum();
373 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400374 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400375 const char* lastUnderscore = nullptr;
376 do {
377 if (!firstMember.skipToEndBracket('_')) {
378 break;
379 }
380 if (firstMember.fChar > wordEnd) {
381 break;
382 }
383 lastUnderscore = firstMember.fChar;
384 } while (firstMember.next());
385 if (lastUnderscore) {
386 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400387 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400388 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400389 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400390 }
391 if (!def) {
392 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500393 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400394 break;
395 }
396 }
397 def->fVisited = true;
398 for (auto& child : def->fChildren) {
399 if (MarkType::kCode == child->fMarkType) {
400 def = child;
401 break;
402 }
403 }
404 if (MarkType::kCode != def->fMarkType) {
405 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500406 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400407 break;
408 }
409 if (def->crossCheck(token)) {
410 def->fVisited = true;
411 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500412 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
413 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400414 }
415 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400416 string constName = MarkType::kEnumClass == token.fMarkType ?
417 fullName : className;
418 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400419 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400420 if (!def) {
421 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400422 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400423 }
424 if (!def) {
425 if (string::npos == child->fName.find("Legacy_")) {
426 SkDebugf("const missing from bmh: %s\n", constName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500427 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400428 }
429 } else {
430 def->fVisited = true;
431 }
432 }
433 } break;
434 case MarkType::kMember:
435 if (def) {
436 def->fVisited = true;
437 } else {
438 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500439 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400440 }
441 break;
Cary Clark2f466242017-12-11 16:03:17 -0500442 case MarkType::kTypedef:
443 if (def) {
444 def->fVisited = true;
445 } else {
446 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500447 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500448 }
449 break;
Cary Clark8032b982017-07-28 11:04:54 -0400450 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400451 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400452 break;
453 }
454 }
455 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500456 int crossChecks = 0;
457 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400458 for (auto& classMapper : fIClassMap) {
459 string className = classMapper.first;
460 auto finder = bmhParser.fClassMap.find(className);
461 if (bmhParser.fClassMap.end() == finder) {
462 continue;
463 }
464 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500465 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500466 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400467 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500468 if (crossChecks) {
469 SkDebugf(".");
470 } else {
471 SkDebugf("cross-check");
472 firstCheck = className;
473 }
474 ++crossChecks;
475 }
476 if (crossChecks) {
477 if (1 == crossChecks) {
478 SkDebugf("%s", firstCheck.c_str());
479 }
480 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400481 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400482 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500483 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400484}
485
486IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
487 const string& name) {
488 string className;
489 const Definition* test = fParent;
490 while (Definition::Type::kFileType != test->fType) {
491 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
492 className = test->fName + "::";
493 break;
494 }
495 test = test->fParent;
496 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400497 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400498 unordered_map<string, IClassDefinition>& map = fIClassMap;
499 IClassDefinition& markupDef = map[className];
500 if (markupDef.fStart) {
501 typedef IClassDefinition* IClassDefPtr;
502 return INHERITED::reportError<IClassDefPtr>("class already defined");
503 }
504 markupDef.fFileName = fFileName;
505 markupDef.fStart = includeDef.fStart;
506 markupDef.fContentStart = includeDef.fStart;
507 markupDef.fName = className;
508 markupDef.fContentEnd = includeDef.fContentEnd;
509 markupDef.fTerminator = includeDef.fTerminator;
510 markupDef.fParent = fParent;
511 markupDef.fLineCount = fLineCount;
512 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
513 MarkType::kStruct : MarkType::kClass;
514 markupDef.fKeyWord = includeDef.fKeyWord;
515 markupDef.fType = Definition::Type::kMark;
516 fParent = &markupDef;
517 return &markupDef;
518}
519
520void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
521 auto& tokens = classDef.fTokens;
522 for (auto& token : tokens) {
523 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
524 continue;
525 }
526 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400527 this->writeString(
528 "# ------------------------------------------------------------------------------");
529 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400530 }
531 switch (token.fMarkType) {
532 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500533 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500534 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400535 break;
536 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400537 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400538 break;
539 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400540 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400541 continue;
542 break;
543 default:
544 SkASSERT(0);
545 }
546 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400547 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400548 this->lf(1);
549 this->writeString("// incomplete");
550 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400551 this->writeEndTag();
552 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400553 this->writeTag("SeeAlso");
554 this->writeSpace();
555 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400556 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500557 switch (token.fMarkType) {
558 case MarkType::kEnum:
559 case MarkType::kEnumClass:
560 this->writeEndTag("Enum");
561 break;
562 case MarkType::kMethod:
563 this->writeEndTag("Method");
564 break;
565 case MarkType::kMember:
566 this->writeEndTag("Member");
567 continue;
568 break;
569 default:
570 SkASSERT(0);
571 }
Cary Clark9174bda2017-09-19 17:39:32 -0400572 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400573 }
574}
Cary Clark9174bda2017-09-19 17:39:32 -0400575void IncludeParser::dumpComment(const Definition& token) {
576 fLineCount = token.fLineCount;
577 fChar = fLine = token.fContentStart;
578 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400579 bool sawParam = false;
580 bool multiline = false;
581 bool sawReturn = false;
582 bool sawComment = false;
583 bool methodHasReturn = false;
584 vector<string> methodParams;
585 vector<string> foundParams;
586 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400587 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
588 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500589 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400590 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500591 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400592 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400593 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500594 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400595 && !methodParser.strnchr('~', methodParser.fEnd);
596 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
597 const char* nextEnd = paren;
598 do {
599 string paramName;
600 methodParser.fChar = nextEnd + 1;
601 methodParser.skipSpace();
602 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
603 continue;
604 }
605 methodParams.push_back(paramName);
606 } while (')' != nextEnd[0]);
607 }
Cary Clark9174bda2017-09-19 17:39:32 -0400608 for (const auto& child : token.fTokens) {
609 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
610 break;
611 }
Cary Clark8032b982017-07-28 11:04:54 -0400612 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400613 if (child.fPrivate) {
614 break;
615 }
Cary Clark8032b982017-07-28 11:04:54 -0400616 if ('@' == child.fContentStart[0]) {
617 TextParser parser(&child);
618 do {
619 parser.next();
620 if (parser.startsWith("param ")) {
621 parser.skipWord("param");
622 const char* parmStart = parser.fChar;
623 parser.skipToSpace();
624 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
625 parser.skipWhiteSpace();
626 do {
627 size_t nextComma = parmName.find(',');
628 string piece;
629 if (string::npos == nextComma) {
630 piece = parmName;
631 parmName = "";
632 } else {
633 piece = parmName.substr(0, nextComma);
634 parmName = parmName.substr(nextComma + 1);
635 }
636 if (sawParam) {
637 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400638 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400639 }
Cary Clark9174bda2017-09-19 17:39:32 -0400640 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400641 } else {
642 if (sawComment) {
643 this->nl();
644 }
645 this->lf(2);
646 }
647 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400648 this->writeTag("Param", piece);
649 this->writeSpace(2);
650 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
651 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400652 sawParam = true;
653 sawComment = false;
654 } while (parmName.length());
655 parser.skipTo(parser.fEnd);
656 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
657 parser.skipWord("return");
658 if ('s' == parser.peek()) {
659 parser.next();
660 }
661 if (sawParam) {
662 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400663 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400664 }
Cary Clark9174bda2017-09-19 17:39:32 -0400665 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400666 }
667 this->checkForMissingParams(methodParams, foundParams);
668 sawParam = false;
669 sawComment = false;
670 multiline = false;
671 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400672 this->writeTag("Return");
673 this->writeSpace(2);
674 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
675 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400676 sawReturn = true;
677 parser.skipTo(parser.fEnd);
678 } else {
679 this->reportError("unexpected doxygen directive");
680 }
681 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400682 } else if (child.length() > 1) {
683 const char* start = child.fContentStart;
684 ptrdiff_t length = child.fContentEnd - start;
685 SkASSERT(length >= 0);
686 while (length && '/' == start[0]) {
687 start += 1;
688 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400689 }
Cary Clark9174bda2017-09-19 17:39:32 -0400690 while (length && '/' == start[length - 1]) {
691 length -= 1;
692 if (length && '*' == start[length - 1]) {
693 length -= 1;
694 }
695 }
696 if (length) {
697 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
698 if (sawParam || sawReturn) {
699 this->indentToColumn(8);
700 }
701 this->writeBlock(length, start);
702 this->writeSpace();
703 sawComment = true;
704 if (sawParam || sawReturn) {
705 multiline = true;
706 }
Cary Clark8032b982017-07-28 11:04:54 -0400707 }
708 }
709 }
710 }
711 if (sawParam || sawReturn) {
712 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400713 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400714 }
Cary Clark9174bda2017-09-19 17:39:32 -0400715 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400716 }
717 if (!sawReturn) {
718 if (!sawParam) {
719 if (sawComment) {
720 this->nl();
721 }
722 this->lf(2);
723 }
724 this->checkForMissingParams(methodParams, foundParams);
725 }
726 if (methodHasReturn != sawReturn) {
727 if (!methodHasReturn) {
728 this->reportError("unexpected doxygen return");
729 } else {
730 if (sawComment) {
731 this->nl();
732 }
733 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400734 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400735 }
736 }
737}
738
Cary Clark2dc84ad2018-01-26 12:56:22 -0500739void IncludeParser::dumpEnum(const Definition& token, const string& name) {
740 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400741 this->lf(2);
742 this->writeString("#Code");
743 this->lfAlways(1);
744 this->indentToColumn(4);
745 this->writeString("enum");
746 this->writeSpace();
747 if ("_anonymous" != token.fName.substr(0, 10)) {
748 this->writeString(token.fName);
749 this->writeSpace();
750 }
751 this->writeString("{");
752 this->lfAlways(1);
753 for (auto& child : token.fChildren) {
754 this->indentToColumn(8);
755 this->writeString(child->fName);
756 if (child->length()) {
757 this->writeSpace();
758 this->writeBlock(child->length(), child->fContentStart);
759 }
760 if (',' != fLastChar) {
761 this->writeString(",");
762 }
763 this->lfAlways(1);
764 }
765 this->indentToColumn(4);
766 this->writeString("};");
767 this->lf(1);
768 this->writeString("##");
769 this->lf(2);
770 this->dumpComment(token);
771 for (auto& child : token.fChildren) {
772 // start here;
773 // get comments before
774 // or after const values
775 this->writeString("#Const");
776 this->writeSpace();
777 this->writeString(child->fName);
778 TextParser val(child);
779 if (!val.eof()) {
780 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
781 val.next();
782 val.skipSpace();
783 const char* valEnd = val.anyOf(",\n");
784 if (!valEnd) {
785 valEnd = val.fEnd;
786 }
787 this->writeSpace();
788 this->writeBlock(valEnd - val.fStart, val.fStart);
789 } else {
790 this->writeSpace();
791 this->writeDefinition(*child);
792 }
793 }
794 this->lf(1);
795 for (auto comment : child->fChildren) {
796 if (MarkType::kComment == comment->fMarkType) {
797 TextParser parser(comment);
798 parser.skipExact("*");
799 parser.skipExact("*");
800 while (!parser.eof() && parser.skipWhiteSpace()) {
801 parser.skipExact("*");
802 parser.skipWhiteSpace();
803 const char* start = parser.fChar;
804 parser.skipToEndBracket('\n');
805 this->lf(1);
806 this->writeBlock(parser.fChar - start, start);
807 }
808 }
809 }
810 this->writeEndTag();
811 }
812 this->lf(2);
813}
814
815void IncludeParser::dumpMethod(const Definition& token) {
816 this->writeString("#Method");
817 this->writeSpace();
818 if ("SK_TO_STRING_NONVIRT" == token.fName) {
819 this->writeString("void toString(SkString* str) const;");
820 this->lf(2);
821 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
822 this->lf(2);
823 this->writeTag("Private");
824 this->lf(1);
825 this->writeString("macro expands to: void toString(SkString* str) const;");
826 this->writeEndTag();
827 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400828 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400829 "Creates string representation. The representation is read by\n"
830 "internal debugging tools. The interface and implementation may be\n"
831 "suppressed by defining SK_IGNORE_TO_STRING.";
832 this->writeBlock(sizeof(desc) - 1, desc);
833 this->lf(2);
834 this->writeTag("Param", "str");
835 this->writeSpace(2);
836 this->writeString("storage for string representation");
837 this->writeSpace();
838 this->writeString("##");
839 this->lf(2);
840 return;
841 }
842 this->writeBlock(token.length(), token.fStart);
843 this->lf(1);
844 this->dumpComment(token);
845}
846
847void IncludeParser::dumpMember(const Definition& token) {
848 this->writeTag("Member");
849 this->writeSpace();
850 this->writeDefinition(token, token.fName, 2);
851 lf(1);
852 for (auto child : token.fChildren) {
853 this->writeDefinition(*child);
854 }
855 this->writeEndTag();
856 lf(2);
857}
858
Cary Clarkd0530ba2017-09-14 11:25:39 -0400859bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400860 for (const auto& member : fIClassMap) {
861 if (string::npos != member.first.find("::")) {
862 continue;
863 }
864 if (!this->dumpTokens(dir, member.first)) {
865 return false;
866 }
867 }
868 return true;
869}
870
Ben Wagner63fd7602017-10-09 15:45:33 -0400871 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400872bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400873 string fileName = dir;
874 if (dir.length() && '/' != dir[dir.length() - 1]) {
875 fileName += '/';
876 }
877 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400878 fOut = fopen(fileName.c_str(), "wb");
879 if (!fOut) {
880 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400881 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400882 }
883 string prefixName = skClassName.substr(0, 2);
884 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
885 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400886 this->writeTagNoLF("Topic", topicName);
887 this->writeTag("Alias", topicName + "_Reference");
888 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400889 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500890 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
891 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400892 this->writeTag(containerType, skClassName);
893 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400894 auto& tokens = classMap.fTokens;
895 for (auto& token : tokens) {
896 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
897 continue;
898 }
Cary Clark9174bda2017-09-19 17:39:32 -0400899 this->writeDefinition(token);
900 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400901 }
902 this->lf(2);
903 string className(skClassName.substr(2));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500904 vector<string> classNames;
905 vector<string> constNames;
906 vector<string> constructorNames;
907 vector<string> memberNames;
908 vector<string> operatorNames;
909 size_t classMaxLen = 0;
910 size_t constMaxLen = 0;
911 size_t constructorMaxLen = 0;
912 size_t memberMaxLen = 0;
913 size_t operatorMaxLen = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400914 for (const auto& oneClass : fIClassMap) {
915 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
916 continue;
917 }
918 string structName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500919 classMaxLen = SkTMax(classMaxLen, structName.length());
920 classNames.emplace_back(structName);
Cary Clark8032b982017-07-28 11:04:54 -0400921 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500922 for (const auto& oneEnum : fIEnumMap) {
923 string enumName = oneEnum.first;
924 constMaxLen = SkTMax(constMaxLen, enumName.length());
925 constNames.emplace_back(enumName);
Cary Clark8032b982017-07-28 11:04:54 -0400926 }
Cary Clark8032b982017-07-28 11:04:54 -0400927 for (const auto& token : classMap.fTokens) {
928 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
929 continue;
930 }
Cary Clark9174bda2017-09-19 17:39:32 -0400931 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400932 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
933 continue;
934 }
Cary Clark9174bda2017-09-19 17:39:32 -0400935 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
936 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500937 constructorMaxLen = SkTMax(constructorMaxLen, name.length());
Cary Clark9174bda2017-09-19 17:39:32 -0400938 constructorNames.emplace_back(name);
939 continue;
940 }
941 if (name.substr(0, 8) == "operator") {
942 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500943 operatorMaxLen = SkTMax(operatorMaxLen, name.length());
Cary Clark9174bda2017-09-19 17:39:32 -0400944 operatorNames.emplace_back(name);
945 continue;
946 }
Cary Clark8032b982017-07-28 11:04:54 -0400947 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
948 continue;
949 }
Cary Clark9174bda2017-09-19 17:39:32 -0400950 if ("SK_TO_STRING_NONVIRT" == name) {
951 name = "toString";
952 }
Cary Clark8032b982017-07-28 11:04:54 -0400953 size_t paren = name.find('(');
954 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark2dc84ad2018-01-26 12:56:22 -0500955 memberMaxLen = SkTMax(memberMaxLen, funcLen);
956 memberNames.emplace_back(name);
Cary Clark8032b982017-07-28 11:04:54 -0400957 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500958 this->writeTag("Topic", "Overview");
959 this->lf(2);
960 this->writeTag("Subtopic", "Subtopics");
961 string classesName = classMaxLen ? "Classes_and_Structs" : "";
962 string constsName = constructorMaxLen ? "Constants" : "";
963 string constructorsName = constructorMaxLen ? "Constructors" : "";
964 string membersName = memberMaxLen ? "Member_Functions" : "";
965 string operatorsName = operatorMaxLen ? "Operators" : "";
966 size_t nameLen = SkTMax(classesName.size(), SkTMax(constsName.size(),
967 SkTMax(constructorsName.size(), SkTMax(membersName.size(), operatorsName.size()))));
968 this->writeTableHeader("name", nameLen, "description");
969 string classDesc = classMaxLen ? "embedded struct and class members" : "";
970 string constDesc = constMaxLen ? "enum and enum class, const values" : "";
971 string constructorDesc = constructorMaxLen ? "functions that construct " + className : "";
972 string memberDesc = memberMaxLen ? "static functions and member methods" : "";
973 string operatorDesc = operatorMaxLen ? "operator overloading methods" : "";
974 size_t descLen = SkTMax(classDesc.size(), SkTMax(constDesc.size(), SkTMax(constructorDesc.size(),
975 SkTMax(memberDesc.size(), operatorDesc.size()))));
976 if (classMaxLen) {
977 this->writeTableRow(nameLen, classesName, descLen, classDesc);
Cary Clark9174bda2017-09-19 17:39:32 -0400978 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500979 if (constMaxLen) {
980 this->writeTableRow(nameLen, constsName, descLen, constDesc);
Cary Clark9174bda2017-09-19 17:39:32 -0400981 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500982 if (constructorMaxLen) {
983 this->writeTableRow(nameLen, constructorsName, descLen, constructorDesc);
984 }
985 if (memberMaxLen) {
986 this->writeTableRow(nameLen, membersName, descLen, memberDesc);
987 }
988 if (operatorMaxLen) {
989 this->writeTableRow(nameLen, operatorsName, descLen, operatorDesc);
Cary Clark8032b982017-07-28 11:04:54 -0400990 }
Cary Clark9174bda2017-09-19 17:39:32 -0400991 this->writeTableTrailer();
Cary Clark2dc84ad2018-01-26 12:56:22 -0500992 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -0400993 this->lf(2);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500994 if (classMaxLen) {
995 std::sort(classNames.begin(), classNames.end());
996 this->writeTag("Subtopic", "Classes_and_Structs");
997 this->writeTableHeader("name", classMaxLen, "description");
998 for (auto& name : classNames) {
999 this->writeTableRow(classMaxLen, name);
1000 }
1001 this->writeTableTrailer();
1002 this->writeEndTag("Subtopic");
1003 this->lf(2);
1004 }
1005 if (constMaxLen) {
1006 std::sort(constNames.begin(), constNames.end());
1007 this->writeTag("Subtopic", "Constants");
1008 this->writeTableHeader("name", constMaxLen, "description");
1009 for (auto& name : constNames) {
1010 this->writeTableRow(constMaxLen, name);
1011 }
1012 this->writeTableTrailer();
1013 this->writeEndTag("Subtopic");
1014 this->lf(2);
1015 }
1016 if (constructorMaxLen) {
1017 std::sort(constructorNames.begin(), constructorNames.end());
1018 this->writeTag("Subtopic", "Constructors");
1019 this->writeTableHeader("name", constructorMaxLen, "description");
1020 for (auto& name : constructorNames) {
1021 this->writeTableRow(constructorMaxLen, name);
1022 }
1023 this->writeTableTrailer();
1024 this->writeEndTag("Subtopic");
1025 this->lf(2);
1026 }
1027 if (operatorMaxLen) {
1028 std::sort(operatorNames.begin(), operatorNames.end());
1029 this->writeTag("Subtopic", "Operators");
1030 this->writeTableHeader("name", operatorMaxLen, "description");
1031 for (auto& name : operatorNames) {
1032 this->writeTableRow(operatorMaxLen, name);
1033 }
1034 this->writeTableTrailer();
1035 this->writeEndTag("Subtopic");
1036 this->lf(2);
1037 }
1038 if (memberMaxLen) {
1039 std::sort(memberNames.begin(), memberNames.end());
1040 this->writeTag("Subtopic", "Member_Functions");
1041 this->writeTableHeader("name", memberMaxLen, "description");
1042 for (auto& name : memberNames) {
1043 size_t paren = name.find('(');
1044 size_t funcLen = string::npos == paren ? name.length() : paren;
1045 this->writeTableRow(memberMaxLen, name.substr(0, funcLen));
1046 }
1047 this->writeTableTrailer();
1048 this->writeEndTag("Subtopic");
1049 this->lf(2);
1050 }
Cary Clark9174bda2017-09-19 17:39:32 -04001051 this->writeEndTag("Topic");
1052 this->lf(2);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001053 for (auto& oneEnum : fIEnumMap) {
1054 this->writeString(
1055 "# ------------------------------------------------------------------------------");
1056 this->dumpEnum(oneEnum.second, oneEnum.first);
1057 this->lf(2);
1058 this->writeTag("Example");
1059 this->lfcr();
1060 this->writeString("// incomplete");
1061 this->writeEndTag();
1062 this->lf(2);
1063 this->writeTag("SeeAlso", "incomplete");
1064 this->lf(2);
1065 this->writeEndTag("Enum", oneEnum.first);
1066 this->lf(2);
1067 }
Cary Clark8032b982017-07-28 11:04:54 -04001068 for (auto& oneClass : fIClassMap) {
1069 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1070 continue;
1071 }
1072 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -04001073 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -04001074 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -04001075 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -05001076 KeyWord keyword = oneClass.second.fKeyWord;
1077 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1078 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001079 this->writeTag(containerType, innerName);
1080 this->lf(2);
1081 this->writeTag("Code");
1082 this->writeEndTag("ToDo", "fill this in manually");
1083 this->writeEndTag();
1084 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001085 for (auto& token : oneClass.second.fTokens) {
1086 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1087 continue;
1088 }
Cary Clark9174bda2017-09-19 17:39:32 -04001089 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001090 }
1091 this->lf(2);
1092 this->dumpClassTokens(oneClass.second);
1093 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001094 this->writeEndTag(containerType, innerName);
1095 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001096 }
1097 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001098 this->writeEndTag(containerType, skClassName);
1099 this->lf(2);
1100 this->writeEndTag("Topic", topicName);
1101 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001102 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001103 SkDebugf("wrote %s\n", fileName.c_str());
1104 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001105}
1106
1107bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1108 // add comment preceding class, if any
1109 const Definition* parent = includeDef.fParent;
1110 int index = includeDef.fParentIndex;
1111 auto wordIter = parent->fTokens.begin();
1112 std::advance(wordIter, index);
1113 SkASSERT(&*wordIter == &includeDef);
1114 while (parent->fTokens.begin() != wordIter) {
1115 auto testIter = std::prev(wordIter);
1116 if (Definition::Type::kWord != testIter->fType
1117 && Definition::Type::kKeyWord != testIter->fType
1118 && (Definition::Type::kBracket != testIter->fType
1119 || Bracket::kAngle != testIter->fBracket)
1120 && (Definition::Type::kPunctuation != testIter->fType
1121 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1122 break;
1123 }
1124 wordIter = testIter;
1125 }
1126 auto commentIter = wordIter;
1127 while (parent->fTokens.begin() != commentIter) {
1128 auto testIter = std::prev(commentIter);
1129 bool isComment = Definition::Type::kBracket == testIter->fType
1130 && (Bracket::kSlashSlash == testIter->fBracket
1131 || Bracket::kSlashStar == testIter->fBracket);
1132 if (!isComment) {
1133 break;
1134 }
1135 commentIter = testIter;
1136 }
1137 while (commentIter != wordIter) {
1138 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1139 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1140 return false;
1141 }
1142 commentIter = std::next(commentIter);
1143 }
1144 return true;
1145}
1146
Cary Clarkbad5ad72017-08-03 17:14:08 -04001147bool IncludeParser::internalName(const Definition& token) const {
1148 return 0 == token.fName.find("internal_")
1149 || 0 == token.fName.find("Internal_")
1150 || 0 == token.fName.find("legacy_")
1151 || 0 == token.fName.find("temporary_")
1152 || 0 == token.fName.find("private_");
1153}
1154
Cary Clark8032b982017-07-28 11:04:54 -04001155// caller calls reportError, so just return false here
1156bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1157 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001158 // parse class header
1159 auto iter = includeDef->fTokens.begin();
1160 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1161 // todo : documentation is ignoring this for now
1162 iter = std::next(iter);
1163 }
1164 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1165 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001166 iter = std::next(iter);
1167 if (iter == includeDef->fTokens.end()) {
1168 return true; // forward declaration only
1169 }
Cary Clark8032b982017-07-28 11:04:54 -04001170 do {
1171 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001172 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001173 }
1174 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1175 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001176 }
Cary Clark8032b982017-07-28 11:04:54 -04001177 } while (static_cast<void>(iter = std::next(iter)), true);
1178 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001179 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001180 }
1181 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1182 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001183 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001184 }
1185 markupDef->fStart = iter->fStart;
1186 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001187 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001188 }
1189// if (1 != includeDef->fChildren.size()) {
1190// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1191// }
1192 includeDef = includeDef->fChildren.front();
1193 iter = includeDef->fTokens.begin();
1194 // skip until public
1195 int publicIndex = 0;
1196 if (IsStruct::kNo == isStruct) {
1197 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1198 size_t publicLen = strlen(publicName);
1199 while (iter != includeDef->fTokens.end()
1200 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1201 || strncmp(iter->fStart, publicName, publicLen))) {
1202 iter = std::next(iter);
1203 ++publicIndex;
1204 }
1205 }
1206 auto childIter = includeDef->fChildren.begin();
1207 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1208 (*childIter)->fPrivate = true;
1209 childIter = std::next(childIter);
1210 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001211 int keyIndex = publicIndex;
1212 KeyWord currentKey = KeyWord::kPublic;
1213 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1214 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001215 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1216 size_t protectedLen = strlen(protectedName);
1217 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1218 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001219 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001220 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001221 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1222 const char* testStart = iter->fStart;
1223 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1224 iter = std::next(iter);
1225 ++keyIndex;
1226 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1227 currentKey = KeyWord::kPublic;
1228 break;
1229 }
1230 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1231 currentKey = KeyWord::kProtected;
1232 break;
1233 }
1234 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1235 currentKey = KeyWord::kPrivate;
1236 break;
1237 }
1238 }
1239 fLastObject = nullptr;
1240 if (KeyWord::kPublic == currentKey) {
1241 if (!this->parseObject(child, markupDef)) {
1242 return false;
1243 }
1244 } else {
1245 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001246 }
Cary Clark73fa9722017-08-29 17:36:51 -04001247 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001248 childIter = std::next(childIter);
1249 }
Cary Clark8032b982017-07-28 11:04:54 -04001250 SkASSERT(fParent->fParent);
1251 fParent = fParent->fParent;
1252 return true;
1253}
1254
1255bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1256 int lineCount, Definition* markupDef) {
1257 TextParser parser(filename, start, end, lineCount);
1258 // parse doxygen if present
1259 if (parser.startsWith("**")) {
1260 parser.next();
1261 parser.next();
1262 parser.skipWhiteSpace();
1263 if ('\\' == parser.peek()) {
1264 parser.next();
1265 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1266 return reportError<bool>("missing object type");
1267 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001268 if (!parser.skipWord(markupDef->fName.c_str()) &&
1269 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001270 return reportError<bool>("missing object name");
1271 }
1272
1273 }
1274 }
1275 // remove leading '*' if present
1276 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1277 while (!parser.eof() && parser.skipWhiteSpace()) {
1278 while ('*' == parser.peek()) {
1279 parser.next();
1280 if (parser.eof()) {
1281 break;
1282 }
1283 parser.skipWhiteSpace();
1284 }
1285 if (parser.eof()) {
1286 break;
1287 }
1288 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001289 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark8032b982017-07-28 11:04:54 -04001290 parser.fLineCount, parent);
1291 parser.skipToEndBracket('\n');
1292 }
1293 return true;
1294}
1295
1296bool IncludeParser::parseDefine() {
1297
1298 return true;
1299}
1300
1301bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001302 TextParser parser(child);
1303 parser.skipToEndBracket('{');
1304 if (parser.eof()) {
1305 return true; // if enum is a forward declaration, do nothing
1306 }
1307 parser.next();
1308 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001309 if (child->fTokens.size() > 0) {
1310 auto token = child->fTokens.begin();
1311 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1312 token = token->fTokens.begin();
1313 }
1314 if (Definition::Type::kWord == token->fType) {
1315 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1316 }
1317 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001318 Definition* markupChild;
1319 if (!markupDef) {
1320 auto finder = fIEnumMap.find(nameStr);
1321 if (fIEnumMap.end() != finder) {
1322 return child->reportError<bool>("duplicate global enum name");
1323 }
1324 markupChild = &fIEnumMap[nameStr];
1325 markupChild->fContentStart = child->fContentStart;
1326 markupChild->fName = nameStr;
1327 markupChild->fFiddle = nameStr;
1328 markupChild->fContentEnd = child->fContentEnd;
1329 markupChild->fFileName = child->fFileName;
1330 markupChild->fLineCount = child->fLineCount;
1331 } else {
1332 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1333 child->fLineCount, markupDef);
1334 markupChild = &markupDef->fTokens.back();
1335 }
Cary Clark8032b982017-07-28 11:04:54 -04001336 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1337 markupChild->fKeyWord = KeyWord::kEnum;
1338 TextParser enumName(child);
1339 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001340 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001341 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001342 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001343 markupChild->fMarkType = MarkType::kEnumClass;
1344 }
Cary Clark8032b982017-07-28 11:04:54 -04001345 const char* nameStart = enumName.fChar;
1346 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001347 if (markupDef) {
1348 markupChild->fName = markupDef->fName + "::";
1349 }
1350 markupChild->fName += string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark8032b982017-07-28 11:04:54 -04001351 if (!this->findComments(*child, markupChild)) {
1352 return false;
1353 }
Cary Clark8032b982017-07-28 11:04:54 -04001354 const char* dataEnd;
1355 do {
Cary Clark8032b982017-07-28 11:04:54 -04001356 parser.skipWhiteSpace();
1357 if ('}' == parser.peek()) {
1358 break;
1359 }
1360 Definition* comment = nullptr;
1361 // note that comment, if any, can be before or after (on the same line, though) as member
1362 if ('#' == parser.peek()) {
1363 // fixme: handle preprecessor, but just skip it for now
1364 parser.skipToLineStart();
1365 }
1366 while (parser.startsWith("/*") || parser.startsWith("//")) {
1367 parser.next();
1368 const char* start = parser.fChar;
1369 const char* end;
1370 if ('*' == parser.peek()) {
1371 end = parser.strnstr("*/", parser.fEnd);
1372 parser.fChar = end;
1373 parser.next();
1374 parser.next();
1375 } else {
1376 end = parser.trimmedLineEnd();
1377 parser.skipToLineStart();
1378 }
1379 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1380 markupChild);
1381 comment = &markupChild->fTokens.back();
1382 comment->fTerminator = end;
1383 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1384 return false;
1385 }
1386 parser.skipWhiteSpace();
1387 }
1388 parser.skipWhiteSpace();
1389 const char* memberStart = parser.fChar;
1390 if ('}' == memberStart[0]) {
1391 break;
1392 }
Cary Clark9174bda2017-09-19 17:39:32 -04001393 // if there's comment on same the line as member def, output first as if it was before
1394
Cary Clark8032b982017-07-28 11:04:54 -04001395 parser.skipToNonAlphaNum();
1396 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001397 if (parser.eof() || !parser.skipWhiteSpace()) {
1398 return this->reportError<bool>("enum member must end with comma 1");
1399 }
Cary Clark8032b982017-07-28 11:04:54 -04001400 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001401 if ('=' == parser.peek()) {
1402 parser.skipToEndBracket(',');
1403 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001404 if (!parser.eof() && '#' == parser.peek()) {
1405 // fixme: handle preprecessor, but just skip it for now
1406 continue;
1407 }
Cary Clark9174bda2017-09-19 17:39:32 -04001408 if (parser.eof() || ',' != parser.peek()) {
1409 return this->reportError<bool>("enum member must end with comma 2");
1410 }
1411 dataEnd = parser.fChar;
1412 const char* start = parser.anyOf("/\n");
1413 SkASSERT(start);
1414 parser.skipTo(start);
1415 if ('/' == parser.next()) {
1416 char slashStar = parser.next();
1417 if ('/' == slashStar || '*' == slashStar) {
1418 TextParser::Save save(&parser);
1419 char doxCheck = parser.next();
1420 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1421 save.restore();
1422 }
1423 }
1424 parser.skipWhiteSpace();
1425 const char* commentStart = parser.fChar;
1426 if ('/' == slashStar) {
1427 parser.skipToEndBracket('\n');
1428 } else {
1429 parser.skipToEndBracket("*/");
1430 }
1431 SkASSERT(!parser.eof());
1432 const char* commentEnd = parser.fChar;
1433 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
1434 parser.fLineCount, markupChild);
1435 comment = &markupChild->fTokens.back();
1436 comment->fTerminator = commentEnd;
1437 }
Cary Clark8032b982017-07-28 11:04:54 -04001438 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1439 markupChild);
1440 Definition* member = &markupChild->fTokens.back();
1441 member->fName = memberName;
1442 if (comment) {
1443 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001444 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001445 }
1446 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001447 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001448 for (auto outsideMember : child->fChildren) {
1449 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001450 continue;
1451 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001452 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1453 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001454 continue;
1455 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001456 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1457 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
1458 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild);
Cary Clark884dd7d2017-10-11 10:37:52 -04001459 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001460 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001461 // FIXME: ? add comment as well ?
1462 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001463 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001464 if (markupDef) {
1465 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1466 SkASSERT(classDef.fStart);
1467 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1468 markupChild->fName = uniqueName;
1469 classDef.fEnums[uniqueName] = markupChild;
1470 }
Cary Clark8032b982017-07-28 11:04:54 -04001471 return true;
1472}
1473
1474bool IncludeParser::parseInclude(const string& name) {
1475 fParent = &fIncludeMap[name];
1476 fParent->fName = name;
1477 fParent->fFileName = fFileName;
1478 fParent->fType = Definition::Type::kFileType;
1479 fParent->fContentStart = fChar;
1480 fParent->fContentEnd = fEnd;
1481 // parse include file into tree
1482 while (fChar < fEnd) {
1483 if (!this->parseChar()) {
1484 return false;
1485 }
1486 }
1487 // parse tree and add named objects to maps
1488 fParent = &fIncludeMap[name];
1489 if (!this->parseObjects(fParent, nullptr)) {
1490 return false;
1491 }
1492 return true;
1493}
1494
1495bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1496 const char* typeStart = child->fChildren[0]->fContentStart;
1497 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1498 child->fLineCount, markupDef);
1499 Definition* markupChild = &markupDef->fTokens.back();
1500 TextParser nameParser(child);
1501 nameParser.skipToNonAlphaNum();
1502 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1503 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1504 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1505 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001506 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001507 classDef.fMembers[uniqueName] = markupChild;
1508 if (child->fParentIndex >= 2) {
1509 auto comment = child->fParent->fTokens.begin();
1510 std::advance(comment, child->fParentIndex - 2);
1511 if (Definition::Type::kBracket == comment->fType
1512 && (Bracket::kSlashStar == comment->fBracket
1513 || Bracket::kSlashSlash == comment->fBracket)) {
1514 TextParser parser(&*comment);
1515 do {
1516 parser.skipToAlpha();
1517 if (parser.eof()) {
1518 break;
1519 }
1520 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001521 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001522 if (Bracket::kSlashStar == comment->fBracket) {
1523 const char* commentEnd = parser.strnstr("*/", end);
1524 if (commentEnd) {
1525 end = commentEnd;
1526 }
1527 }
1528 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1529 markupDef);
1530 Definition* commentChild = &markupDef->fTokens.back();
1531 markupChild->fChildren.emplace_back(commentChild);
1532 parser.skipTo(end);
1533 } while (!parser.eof());
1534 }
1535 }
1536 return true;
1537}
1538
1539bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1540 auto tokenIter = child->fParent->fTokens.begin();
1541 std::advance(tokenIter, child->fParentIndex);
1542 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001543 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001544 bool addConst = false;
1545 auto operatorCheck = tokenIter;
1546 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1547 operatorCheck = std::prev(tokenIter);
1548 }
1549 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001550 auto closeParen = std::next(tokenIter);
1551 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1552 '(' == closeParen->fContentStart[0]);
1553 nameEnd = closeParen->fContentEnd + 1;
1554 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001555 if (Definition::Type::kKeyWord == closeParen->fType &&
1556 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001557 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001558 }
Cary Clarka560c472017-11-27 10:44:06 -05001559 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001560 }
1561 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001562 if (addConst) {
1563 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001564 }
Cary Clark8032b982017-07-28 11:04:54 -04001565 while (tokenIter != child->fParent->fTokens.begin()) {
1566 auto testIter = std::prev(tokenIter);
1567 switch (testIter->fType) {
1568 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001569 if (testIter == child->fParent->fTokens.begin() &&
1570 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1571 KeyWord::kIfndef == child->fParent->fKeyWord ||
1572 KeyWord::kIf == child->fParent->fKeyWord)) {
1573 std::next(tokenIter);
1574 break;
1575 }
Cary Clark8032b982017-07-28 11:04:54 -04001576 goto keepGoing;
1577 case Definition::Type::kKeyWord: {
1578 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1579 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1580 goto keepGoing;
1581 }
1582 } break;
1583 case Definition::Type::kBracket:
1584 if (Bracket::kAngle == testIter->fBracket) {
1585 goto keepGoing;
1586 }
1587 break;
1588 case Definition::Type::kPunctuation:
1589 if (Punctuation::kSemicolon == testIter->fPunctuation
1590 || Punctuation::kLeftBrace == testIter->fPunctuation
1591 || Punctuation::kColon == testIter->fPunctuation) {
1592 break;
1593 }
1594 keepGoing:
1595 tokenIter = testIter;
1596 continue;
1597 default:
1598 break;
1599 }
1600 break;
1601 }
1602 tokenIter->fName = nameStr;
1603 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001604 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001605 auto testIter = child->fParent->fTokens.begin();
1606 SkASSERT(child->fParentIndex > 0);
1607 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001608 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1609 0 == tokenIter->fParentIndex) {
1610 tokenIter = std::next(tokenIter);
1611 }
Cary Clark8032b982017-07-28 11:04:54 -04001612 const char* start = tokenIter->fContentStart;
1613 const char* end = tokenIter->fContentEnd;
1614 const char kDebugCodeStr[] = "SkDEBUGCODE";
1615 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1616 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1617 std::advance(testIter, 1);
1618 start = testIter->fContentStart + 1;
1619 end = testIter->fContentEnd - 1;
1620 } else {
1621 end = testIter->fContentEnd;
1622 while (testIter != child->fParent->fTokens.end()) {
1623 testIter = std::next(testIter);
1624 switch (testIter->fType) {
1625 case Definition::Type::kPunctuation:
1626 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1627 || Punctuation::kLeftBrace == testIter->fPunctuation
1628 || Punctuation::kColon == testIter->fPunctuation);
1629 end = testIter->fStart;
1630 break;
1631 case Definition::Type::kKeyWord: {
1632 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1633 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1634 continue;
1635 }
1636 } break;
1637 default:
1638 continue;
1639 }
1640 break;
1641 }
1642 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001643 while (end > start && ' ' >= end[-1]) {
1644 --end;
1645 }
Cary Clark9174bda2017-09-19 17:39:32 -04001646 if (!markupDef) {
1647 auto parentIter = child->fParent->fTokens.begin();
1648 SkASSERT(child->fParentIndex > 0);
1649 std::advance(parentIter, child->fParentIndex - 1);
1650 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001651 TextParser nameParser(methodName);
1652 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001653 return true; // expect this is inline class definition outside of class
1654 }
Cary Clarka560c472017-11-27 10:44:06 -05001655 string name(nameParser.fLine, nameParser.lineLength());
1656 auto finder = fIFunctionMap.find(name);
1657 if (fIFunctionMap.end() != finder) {
1658 // create unique name
1659 SkASSERT(0); // incomplete
1660 }
1661 auto globalFunction = &fIFunctionMap[name];
1662 globalFunction->fContentStart = start;
1663 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001664 globalFunction->fFiddle = name;
1665 globalFunction->fContentEnd = end;
1666 globalFunction->fMarkType = MarkType::kMethod;
1667 globalFunction->fLineCount = tokenIter->fLineCount;
1668 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001669 }
Cary Clark8032b982017-07-28 11:04:54 -04001670 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1671 markupDef);
1672 Definition* markupChild = &markupDef->fTokens.back();
1673 // do find instead -- I wonder if there is a way to prevent this in c++
1674 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1675 SkASSERT(classDef.fStart);
1676 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1677 markupChild->fName = uniqueName;
1678 if (!this->findComments(*child, markupChild)) {
1679 return false;
1680 }
1681 classDef.fMethods[uniqueName] = markupChild;
1682 return true;
1683}
1684
Cary Clark8032b982017-07-28 11:04:54 -04001685bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1686 for (auto& child : parent->fChildren) {
1687 if (!this->parseObject(child, markupDef)) {
1688 return false;
1689 }
1690 }
1691 return true;
1692}
1693
1694bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1695 // set up for error reporting
1696 fLine = fChar = child->fStart;
1697 fEnd = child->fContentEnd;
1698 // todo: put original line number in child as well
1699 switch (child->fType) {
1700 case Definition::Type::kKeyWord:
1701 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001702 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001703 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001704 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001705 }
1706 break;
1707 case KeyWord::kEnum:
1708 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001709 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001710 }
1711 break;
1712 case KeyWord::kStruct:
1713 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001714 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001715 }
1716 break;
1717 case KeyWord::kTemplate:
1718 if (!this->parseTemplate()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001719 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001720 }
1721 break;
1722 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001723 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001724 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001725 }
1726 break;
1727 case KeyWord::kUnion:
1728 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001729 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001730 }
1731 break;
1732 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001733 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001734 }
1735 break;
1736 case Definition::Type::kBracket:
1737 switch (child->fBracket) {
1738 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001739 if (fLastObject) {
1740 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1741 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001742 if (!checkDeprecated.eof()) {
1743 checkDeprecated.skipWhiteSpace();
1744 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1745 break;
1746 }
1747 }
1748 }
1749 {
1750 auto tokenIter = child->fParent->fTokens.begin();
1751 std::advance(tokenIter, child->fParentIndex);
1752 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05001753 TextParser previousToken(&*tokenIter);
1754 if (previousToken.startsWith("SK_ATTR_DEPRECATED")) {
Cary Clark73fa9722017-08-29 17:36:51 -04001755 break;
1756 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05001757 if (Bracket::kPound == child->fParent->fBracket &&
1758 KeyWord::kIf == child->fParent->fKeyWord) {
1759 // TODO: this will skip methods named defined() -- for the
1760 // moment there aren't any
1761 if (previousToken.startsWith("defined")) {
1762 break;
1763 }
1764 }
Cary Clark73fa9722017-08-29 17:36:51 -04001765 }
Cary Clark8032b982017-07-28 11:04:54 -04001766 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001767 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001768 }
Cary Clark73fa9722017-08-29 17:36:51 -04001769 break;
Cary Clark8032b982017-07-28 11:04:54 -04001770 case Bracket::kSlashSlash:
1771 case Bracket::kSlashStar:
1772 // comments are picked up by parsing objects first
1773 break;
1774 case Bracket::kPound:
1775 // special-case the #xxx xxx_DEFINED entries
1776 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001777 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001778 case KeyWord::kIfndef:
1779 case KeyWord::kIfdef:
1780 if (child->boilerplateIfDef(fParent)) {
1781 if (!this->parseObjects(child, markupDef)) {
1782 return false;
1783 }
1784 break;
1785 }
1786 goto preproError;
1787 case KeyWord::kDefine:
1788 if (child->boilerplateDef(fParent)) {
1789 break;
1790 }
1791 goto preproError;
1792 case KeyWord::kEndif:
1793 if (child->boilerplateEndIf()) {
1794 break;
1795 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001796 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04001797 case KeyWord::kInclude:
1798 // ignored for now
1799 break;
1800 case KeyWord::kElse:
1801 case KeyWord::kElif:
1802 // todo: handle these
1803 break;
1804 default:
1805 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001806 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001807 }
1808 break;
1809 case Bracket::kAngle:
1810 // pick up templated function pieces when method is found
1811 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001812 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001813 if (!this->parseObjects(child, markupDef)) {
1814 return false;
1815 }
Cary Clark73fa9722017-08-29 17:36:51 -04001816 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001817 case Bracket::kSquare: {
1818 // check to see if parent is operator, the only case we handle so far
1819 auto prev = child->fParent->fTokens.begin();
1820 std::advance(prev, child->fParentIndex - 1);
1821 if (KeyWord::kOperator != prev->fKeyWord) {
1822 return child->reportError<bool>("expected operator overload");
1823 }
1824 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001825 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001826 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001827 }
1828 break;
1829 case Definition::Type::kWord:
1830 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001831 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001832 }
1833 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001834 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001835 }
1836 break;
1837 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001838 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001839 break;
1840 }
1841 return true;
1842}
1843
1844bool IncludeParser::parseTemplate() {
1845
1846 return true;
1847}
1848
Cary Clark2f466242017-12-11 16:03:17 -05001849bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1850 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05001851 typedefParser.skipExact("typedef");
1852 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05001853 string nameStr = typedefParser.typedefName();
1854 if (!markupDef) {
1855 Definition& typedefDef = fITypedefMap[nameStr];
1856 SkASSERT(!typedefDef.fStart);
1857 typedefDef.fStart = child->fContentStart;
1858 typedefDef.fContentStart = child->fContentStart;
1859 typedefDef.fName = nameStr;
1860 typedefDef.fFiddle = nameStr;
1861 typedefDef.fContentEnd = child->fContentEnd;
1862 typedefDef.fTerminator = child->fContentEnd;
1863 typedefDef.fMarkType = MarkType::kTypedef;
1864 typedefDef.fLineCount = child->fLineCount;
1865 return true;
1866 }
1867 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
1868 child->fLineCount, markupDef);
1869 Definition* markupChild = &markupDef->fTokens.back();
1870 markupChild->fName = nameStr;
1871 markupChild->fTerminator = markupChild->fContentEnd;
1872 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1873 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001874 return true;
1875}
1876
1877bool IncludeParser::parseUnion() {
1878
1879 return true;
1880}
1881
1882bool IncludeParser::parseChar() {
1883 char test = *fChar;
1884 if ('\\' == fPrev) {
1885 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001886// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001887 fLine = fChar + 1;
1888 }
1889 goto done;
1890 }
1891 switch (test) {
1892 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001893// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001894 fLine = fChar + 1;
1895 if (fInChar) {
1896 return reportError<bool>("malformed char");
1897 }
1898 if (fInString) {
1899 return reportError<bool>("malformed string");
1900 }
1901 if (!this->checkForWord()) {
1902 return false;
1903 }
1904 if (Bracket::kPound == this->topBracket()) {
1905 KeyWord keyWord = fParent->fKeyWord;
1906 if (KeyWord::kNone == keyWord) {
1907 return this->reportError<bool>("unhandled preprocessor directive");
1908 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001909 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001910 this->popBracket();
1911 }
1912 } else if (Bracket::kSlashSlash == this->topBracket()) {
1913 this->popBracket();
1914 }
1915 break;
1916 case '*':
1917 if (!fInCharCommentString && '/' == fPrev) {
1918 this->pushBracket(Bracket::kSlashStar);
1919 }
1920 if (!this->checkForWord()) {
1921 return false;
1922 }
1923 if (!fInCharCommentString) {
1924 this->addPunctuation(Punctuation::kAsterisk);
1925 }
1926 break;
1927 case '/':
1928 if ('*' == fPrev) {
1929 if (!fInCharCommentString) {
1930 return reportError<bool>("malformed closing comment");
1931 }
1932 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001933 TextParser::Save save(this);
1934 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001935 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001936 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001937 }
1938 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001939 }
Cary Clark8032b982017-07-28 11:04:54 -04001940 if (!fInCharCommentString && '/' == fPrev) {
1941 this->pushBracket(Bracket::kSlashSlash);
1942 break;
1943 }
1944 if (!this->checkForWord()) {
1945 return false;
1946 }
1947 break;
1948 case '\'':
1949 if (Bracket::kChar == this->topBracket()) {
1950 this->popBracket();
1951 } else if (!fInComment && !fInString) {
1952 if (fIncludeWord) {
1953 return this->reportError<bool>("word then single-quote");
1954 }
1955 this->pushBracket(Bracket::kChar);
1956 }
1957 break;
1958 case '\"':
1959 if (Bracket::kString == this->topBracket()) {
1960 this->popBracket();
1961 } else if (!fInComment && !fInChar) {
1962 if (fIncludeWord) {
1963 return this->reportError<bool>("word then double-quote");
1964 }
1965 this->pushBracket(Bracket::kString);
1966 }
1967 break;
1968 case ':':
1969 case '(':
1970 case '[':
1971 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001972 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1973 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1974 this->pushBracket(Bracket::kDebugCode);
1975 break;
1976 }
Cary Clark8032b982017-07-28 11:04:54 -04001977 if (fInCharCommentString) {
1978 break;
1979 }
1980 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1981 break;
1982 }
1983 if (!fInBrace) {
1984 if (!this->checkForWord()) {
1985 return false;
1986 }
1987 if (':' == test && !fInFunction) {
1988 break;
1989 }
1990 if ('{' == test) {
1991 this->addPunctuation(Punctuation::kLeftBrace);
1992 } else if (':' == test) {
1993 this->addPunctuation(Punctuation::kColon);
1994 }
1995 }
1996 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1997 && Bracket::kColon == fInBrace->fBracket) {
1998 Definition* braceParent = fParent->fParent;
1999 braceParent->fChildren.pop_back();
2000 braceParent->fTokens.pop_back();
2001 fParent = braceParent;
2002 fInBrace = nullptr;
2003 }
2004 this->pushBracket(
2005 '(' == test ? Bracket::kParen :
2006 '[' == test ? Bracket::kSquare :
2007 '{' == test ? Bracket::kBrace :
2008 Bracket::kColon);
2009 if (!fInBrace
2010 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2011 && fInFunction) {
2012 fInBrace = fParent;
2013 }
2014 } break;
2015 case '<':
2016 if (fInCharCommentString || fInBrace) {
2017 break;
2018 }
2019 if (!this->checkForWord()) {
2020 return false;
2021 }
2022 if (fInEnum) {
2023 break;
2024 }
2025 this->pushBracket(Bracket::kAngle);
2026 break;
2027 case ')':
2028 case ']':
2029 case '}': {
2030 if (fInCharCommentString) {
2031 break;
2032 }
2033 if (!fInBrace) {
2034 if (!this->checkForWord()) {
2035 return false;
2036 }
2037 }
2038 bool popBraceParent = fInBrace == fParent;
2039 if ((')' == test ? Bracket::kParen :
2040 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2041 this->popBracket();
2042 if (!fInFunction) {
2043 bool deprecatedMacro = false;
2044 if (')' == test) {
2045 auto iter = fParent->fTokens.end();
2046 bool lookForWord = false;
2047 while (fParent->fTokens.begin() != iter) {
2048 --iter;
2049 if (lookForWord) {
2050 if (Definition::Type::kWord != iter->fType) {
2051 break;
2052 }
2053 string word(iter->fContentStart, iter->length());
2054 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
2055 deprecatedMacro = true;
2056 // remove macro paren (would confuse method parsing later)
Ben Wagner63fd7602017-10-09 15:45:33 -04002057 fParent->fTokens.pop_back();
Cary Clark8032b982017-07-28 11:04:54 -04002058 fParent->fChildren.pop_back();
2059 }
2060 break;
2061 }
2062 if (Definition::Type::kBracket != iter->fType) {
2063 break;
2064 }
2065 if (Bracket::kParen != iter->fBracket) {
2066 break;
2067 }
2068 lookForWord = true;
2069 }
2070 }
2071 fInFunction = ')' == test && !deprecatedMacro;
2072 } else {
2073 fInFunction = '}' != test;
2074 }
Cary Clark73fa9722017-08-29 17:36:51 -04002075 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2076 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002077 } else {
2078 return reportError<bool>("malformed close bracket");
2079 }
2080 if (popBraceParent) {
2081 Definition* braceParent = fInBrace->fParent;
2082 braceParent->fChildren.pop_back();
2083 braceParent->fTokens.pop_back();
2084 fInBrace = nullptr;
2085 }
2086 } break;
2087 case '>':
2088 if (fInCharCommentString || fInBrace) {
2089 break;
2090 }
2091 if (!this->checkForWord()) {
2092 return false;
2093 }
2094 if (fInEnum) {
2095 break;
2096 }
Cary Clarka560c472017-11-27 10:44:06 -05002097 if (Bracket::kPound == this->topBracket()) {
2098 break;
2099 }
Cary Clark8032b982017-07-28 11:04:54 -04002100 if (Bracket::kAngle == this->topBracket()) {
2101 this->popBracket();
2102 } else {
2103 return reportError<bool>("malformed close angle bracket");
2104 }
2105 break;
2106 case '#': {
2107 if (fInCharCommentString || fInBrace) {
2108 break;
2109 }
2110 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2111 this->pushBracket(Bracket::kPound);
2112 break;
2113 }
2114 case '&':
2115 case ',':
2116 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05002117 case '+':
2118 case '=':
2119 case '-':
2120 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002121 if (fInCharCommentString || fInBrace) {
2122 break;
2123 }
2124 if (!this->checkForWord()) {
2125 return false;
2126 }
2127 break;
2128 case ';':
2129 if (fInCharCommentString || fInBrace) {
2130 break;
2131 }
2132 if (!this->checkForWord()) {
2133 return false;
2134 }
2135 if (Definition::Type::kKeyWord == fParent->fType
2136 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002137 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
2138 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2139 this->popObject();
2140 }
Cary Clark8032b982017-07-28 11:04:54 -04002141 if (KeyWord::kEnum == fParent->fKeyWord) {
2142 fInEnum = false;
2143 }
2144 this->popObject();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002145 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002146 } else if (Definition::Type::kBracket == fParent->fType
2147 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2148 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2149 list<Definition>::iterator baseIter = fParent->fTokens.end();
2150 list<Definition>::iterator namedIter = fParent->fTokens.end();
2151 for (auto tokenIter = fParent->fTokens.end();
2152 fParent->fTokens.begin() != tokenIter--; ) {
2153 if (tokenIter->fLineCount == fLineCount) {
2154 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2155 if (namedIter != fParent->fTokens.end()) {
2156 return reportError<bool>("found two named member tokens");
2157 }
2158 namedIter = tokenIter;
2159 }
2160 baseIter = tokenIter;
2161 } else {
2162 break;
2163 }
2164 }
2165 // FIXME: if a member definition spans multiple lines, this won't work
2166 if (namedIter != fParent->fTokens.end()) {
2167 if (baseIter == namedIter) {
2168 return this->reportError<bool>("expected type before named token");
2169 }
2170 Definition* member = &*namedIter;
2171 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002172 if (!member->fTerminator) {
2173 member->fTerminator = member->fContentEnd;
2174 }
Cary Clark8032b982017-07-28 11:04:54 -04002175 fParent->fChildren.push_back(member);
2176 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2177 member->fChildren.push_back(&*nameType);
2178 }
Cary Clark8032b982017-07-28 11:04:54 -04002179 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002180 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002181 } else if (fParent->fChildren.size() > 0) {
2182 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002183 Definition* priorEnum = fPriorEnum;
2184 fPriorEnum = nullptr;
2185 if (!priorEnum) {
2186 while (fParent->fChildren.begin() != lastIter) {
2187 std::advance(lastIter, -1);
2188 priorEnum = *lastIter;
2189 if (Definition::Type::kBracket != priorEnum->fType ||
2190 (Bracket::kSlashSlash != priorEnum->fBracket
2191 && Bracket::kSlashStar != priorEnum->fBracket)) {
2192 break;
2193 }
Cary Clark8032b982017-07-28 11:04:54 -04002194 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002195 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002196 }
2197 if (Definition::Type::kKeyWord == priorEnum->fType
2198 && KeyWord::kEnum == priorEnum->fKeyWord) {
2199 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002200 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002201 while (tokenWalker != fParent->fTokens.end()) {
2202 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002203 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002204 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2205 break;
2206 }
2207 }
2208 while (tokenWalker != fParent->fTokens.end()) {
2209 std::advance(tokenWalker, 1);
2210 const Definition* test = &*tokenWalker;
2211 if (Definition::Type::kBracket != test->fType ||
2212 (Bracket::kSlashSlash != test->fBracket
2213 && Bracket::kSlashStar != test->fBracket)) {
2214 break;
2215 }
2216 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002217 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002218 Definition* start = &*tokenWalker;
2219 bool foundExpected = true;
2220 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2221 const Definition* test = &*tokenWalker;
2222 if (expected != test->fKeyWord) {
2223 foundExpected = false;
2224 break;
2225 }
2226 if (tokenWalker == fParent->fTokens.end()) {
2227 break;
2228 }
2229 std::advance(tokenWalker, 1);
2230 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002231 if (!foundExpected) {
2232 foundExpected = true;
2233 tokenWalker = saveTokenWalker;
2234 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2235 const Definition* test = &*tokenWalker;
2236 if (expected != test->fKeyWord) {
2237 foundExpected = false;
2238 break;
2239 }
2240 if (tokenWalker == fParent->fTokens.end()) {
2241 break;
2242 }
2243 if (KeyWord::kNone != expected) {
2244 std::advance(tokenWalker, 1);
2245 }
2246 }
2247 if (foundExpected) {
2248 auto nameToken = priorEnum->fTokens.begin();
2249 string enumName = string(nameToken->fContentStart,
2250 nameToken->fContentEnd - nameToken->fContentStart);
2251 const Definition* test = &*tokenWalker;
2252 string constType = string(test->fContentStart,
2253 test->fContentEnd - test->fContentStart);
2254 if (enumName != constType) {
2255 foundExpected = false;
2256 } else {
2257 std::advance(tokenWalker, 1);
2258 }
2259 }
2260 }
Cary Clark8032b982017-07-28 11:04:54 -04002261 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2262 const char* nameStart = tokenWalker->fStart;
2263 std::advance(tokenWalker, 1);
2264 if (tokenWalker != fParent->fTokens.end()) {
2265 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2266 tp.skipToNonAlphaNum();
2267 start->fName = string(nameStart, tp.fChar - nameStart);
2268 start->fContentEnd = fChar;
2269 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002270 fPriorEnum = priorEnum;
2271 }
Cary Clark8032b982017-07-28 11:04:54 -04002272 }
2273 }
2274 }
2275 this->addPunctuation(Punctuation::kSemicolon);
2276 fInFunction = false;
2277 break;
2278 case '~':
2279 if (fInEnum) {
2280 break;
2281 }
2282 case '0': case '1': case '2': case '3': case '4':
2283 case '5': case '6': case '7': case '8': case '9':
2284 // TODO: don't want to parse numbers, but do need to track for enum defs
2285 // break;
2286 case 'A': case 'B': case 'C': case 'D': case 'E':
2287 case 'F': case 'G': case 'H': case 'I': case 'J':
2288 case 'K': case 'L': case 'M': case 'N': case 'O':
2289 case 'P': case 'Q': case 'R': case 'S': case 'T':
2290 case 'U': case 'V': case 'W': case 'X': case 'Y':
2291 case 'Z': case '_':
2292 case 'a': case 'b': case 'c': case 'd': case 'e':
2293 case 'f': case 'g': case 'h': case 'i': case 'j':
2294 case 'k': case 'l': case 'm': case 'n': case 'o':
2295 case 'p': case 'q': case 'r': case 's': case 't':
2296 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002297 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002298 if (fInCharCommentString || fInBrace) {
2299 break;
2300 }
2301 if (!fIncludeWord) {
2302 fIncludeWord = fChar;
2303 }
2304 break;
2305 }
2306done:
2307 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002308 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002309 return true;
2310}
2311
2312void IncludeParser::validate() const {
2313 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2314 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2315 }
2316 IncludeParser::ValidateKeyWords();
2317}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002318
2319void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2320 if (!sk_isdir(includes)) {
2321 IncludeParser::RemoveOneFile(docs, includes);
2322 } else {
2323 SkOSFile::Iter it(includes, ".h");
2324 for (SkString file; it.next(&file); ) {
2325 SkString p = SkOSPath::Join(includes, file.c_str());
2326 const char* hunk = p.c_str();
2327 if (!SkStrEndsWith(hunk, ".h")) {
2328 continue;
2329 }
2330 IncludeParser::RemoveOneFile(docs, hunk);
2331 }
2332 }
2333}
2334
2335void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2336 const char* lastForward = strrchr(includesFile, '/');
2337 const char* lastBackward = strrchr(includesFile, '\\');
2338 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2339 if (!last) {
2340 last = includesFile;
2341 } else {
2342 last += 1;
2343 }
2344 SkString baseName(last);
2345 SkASSERT(baseName.endsWith(".h"));
2346 baseName.remove(baseName.size() - 2, 2);
2347 baseName.append("_Reference.bmh");
2348 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2349 remove(fullName.c_str());
2350}