blob: ebc2384d61a65ec03af1f785e0181de493e4ae21 [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 Clark89b14562018-03-19 09:04:10 -040012const char IncludeParser::gAttrDeprecated[] = "SK_ATTR_DEPRECATED";
13const size_t IncludeParser::kAttrDeprecatedLen = sizeof(gAttrDeprecated) - 1;
14
Cary Clark8032b982017-07-28 11:04:54 -040015const IncludeKey kKeyWords[] = {
16 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040017 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040018 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040019 { "bool", KeyWord::kBool, KeyProperty::kNumber },
20 { "char", KeyWord::kChar, KeyProperty::kNumber },
21 { "class", KeyWord::kClass, KeyProperty::kObject },
22 { "const", KeyWord::kConst, KeyProperty::kModifier },
23 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
24 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
25 { "double", KeyWord::kDouble, KeyProperty::kNumber },
26 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
27 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
28 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
29 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050030 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040031 { "float", KeyWord::kFloat, KeyProperty::kNumber },
32 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
33 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
34 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
35 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
36 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
37 { "inline", KeyWord::kInline, KeyProperty::kModifier },
38 { "int", KeyWord::kInt, KeyProperty::kNumber },
39 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
40 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
41 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
42 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
43 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
44 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
45 { "static", KeyWord::kStatic, KeyProperty::kModifier },
46 { "struct", KeyWord::kStruct, KeyProperty::kObject },
47 { "template", KeyWord::kTemplate, KeyProperty::kObject },
48 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040049 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040050 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040051 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
52 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040053 { "union", KeyWord::kUnion, KeyProperty::kObject },
54 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
55 { "void", KeyWord::kVoid, KeyProperty::kNumber },
56};
57
58const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
59
60KeyWord IncludeParser::FindKey(const char* start, const char* end) {
61 int ch = 0;
62 for (size_t index = 0; index < kKeyWordCount; ) {
63 if (start[ch] > kKeyWords[index].fName[ch]) {
64 ++index;
65 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
66 return KeyWord::kNone;
67 }
68 continue;
69 }
70 if (start[ch] < kKeyWords[index].fName[ch]) {
71 return KeyWord::kNone;
72 }
73 ++ch;
74 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040075 if (end - start < (int) strlen(kKeyWords[index].fName)) {
76 return KeyWord::kNone;
77 }
Cary Clark8032b982017-07-28 11:04:54 -040078 return kKeyWords[index].fKeyWord;
79 }
80 }
81 return KeyWord::kNone;
82}
83
84void IncludeParser::ValidateKeyWords() {
85 for (size_t index = 1; index < kKeyWordCount; ++index) {
86 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
87 == (int) kKeyWords[index].fKeyWord);
88 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
89 }
90}
91
92void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050093 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040094 fIncludeWord = nullptr;
95 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
96 Definition* def = &fParent->fTokens.back();
97 this->addDefinition(def);
98 if (KeyWord::kEnum == fParent->fKeyWord) {
99 fInEnum = true;
100 }
101 }
102}
103
Ben Wagner63fd7602017-10-09 15:45:33 -0400104void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400105 const vector<string>& foundParams) {
106 for (auto& methodParam : methodParams) {
107 bool found = false;
108 for (auto& foundParam : foundParams) {
109 if (methodParam == foundParam) {
110 found = true;
111 break;
112 }
113 }
114 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400115 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400116 }
117 }
118 for (auto& foundParam : foundParams) {
119 bool found = false;
120 for (auto& methodParam : methodParams) {
121 if (methodParam == foundParam) {
122 found = true;
123 break;
124 }
125 }
126 if (!found) {
127 this->reportError("doxygen param does not match method declaration");
128 }
129 }
130}
131
132bool IncludeParser::checkForWord() {
133 if (!fIncludeWord) {
134 return true;
135 }
136 KeyWord keyWord = FindKey(fIncludeWord, fChar);
137 if (KeyWord::kNone != keyWord) {
138 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
139 this->addKeyword(keyWord);
140 return true;
141 }
142 } else {
143 this->addWord();
144 return true;
145 }
146 Definition* poundDef = fParent;
147 if (!fParent) {
148 return reportError<bool>("expected parent");
149 }
150 if (Definition::Type::kBracket != poundDef->fType) {
151 return reportError<bool>("expected bracket");
152 }
153 if (Bracket::kPound != poundDef->fBracket) {
154 return reportError<bool>("expected preprocessor");
155 }
156 if (KeyWord::kNone != poundDef->fKeyWord) {
157 return reportError<bool>("already found keyword");
158 }
159 poundDef->fKeyWord = keyWord;
160 fIncludeWord = nullptr;
161 switch (keyWord) {
162 // these do not link to other # directives
163 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400164 if (!fInBrace) {
165 SkASSERT(!fInDefine);
166 fInDefine = true;
167 }
Cary Clark8032b982017-07-28 11:04:54 -0400168 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500169 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400170 break;
171 // these start a # directive link
172 case KeyWord::kIf:
173 case KeyWord::kIfdef:
174 case KeyWord::kIfndef:
175 break;
176 // these continue a # directive link
177 case KeyWord::kElif:
178 case KeyWord::kElse: {
179 this->popObject(); // pop elif
180 if (Bracket::kPound != fParent->fBracket) {
181 return this->reportError<bool>("expected preprocessor directive");
182 }
183 this->popBracket(); // pop if
184 poundDef->fParent = fParent;
185 this->addDefinition(poundDef); // push elif back
186 } break;
187 // this ends a # directive link
188 case KeyWord::kEndif:
189 // FIXME : should this be calling popBracket() instead?
190 this->popObject(); // pop endif
191 if (Bracket::kPound != fParent->fBracket) {
192 return this->reportError<bool>("expected preprocessor directive");
193 }
194 this->popBracket(); // pop if/else
195 break;
196 default:
197 SkASSERT(0);
198 }
199 return true;
200}
201
202string IncludeParser::className() const {
203 string name(fParent->fName);
204 size_t slash = name.find_last_of("/");
205 if (string::npos == slash) {
206 slash = name.find_last_of("\\");
207 }
208 SkASSERT(string::npos != slash);
209 string result = name.substr(slash);
210 result = result.substr(1, result.size() - 3);
211 return result;
212}
213
Cary Clark884dd7d2017-10-11 10:37:52 -0400214#include <sstream>
215#include <iostream>
216
Cary Clark8032b982017-07-28 11:04:54 -0400217bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400218 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400219 string className = classMapper.first;
220 auto finder = bmhParser.fClassMap.find(className);
221 if (bmhParser.fClassMap.end() == finder) {
222 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400223 continue;
224 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400225 RootDefinition* root = &finder->second;
226 root->clearVisited();
227 }
228 for (auto& classMapper : fIClassMap) {
229 string className = classMapper.first;
230 std::istringstream iss(className);
231 string classStr;
232 string classBase;
233 RootDefinition* root = nullptr;
234 while (std::getline(iss, classStr, ':')) {
235 if (root) {
236 if (!classStr.length()) {
237 continue;
238 }
239 classBase += "::" + classStr;
240 auto finder = root->fBranches.find(classBase);
241 if (root->fBranches.end() != finder) {
242 root = finder->second;
243 } else {
244 SkASSERT(0);
245 }
246 } else {
247 classBase = classStr;
248 auto finder = bmhParser.fClassMap.find(classBase);
249 if (bmhParser.fClassMap.end() != finder) {
250 root = &finder->second;
251 } else {
252 SkASSERT(0);
253 }
254 }
255 }
Cary Clark8032b982017-07-28 11:04:54 -0400256 auto& classMap = classMapper.second;
257 auto& tokens = classMap.fTokens;
258 for (const auto& token : tokens) {
259 if (token.fPrivate) {
260 continue;
261 }
262 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400263 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400264 switch (token.fMarkType) {
265 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400266 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400267 continue;
268 }
Cary Clark8032b982017-07-28 11:04:54 -0400269 if (!def) {
270 string paramName = className + "::";
271 paramName += string(token.fContentStart,
272 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400273 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400274 if (!def && 0 == token.fName.find("operator")) {
275 string operatorName = className + "::";
276 TextParser oper("", token.fStart, token.fContentEnd, 0);
277 const char* start = oper.strnstr("operator", token.fContentEnd);
278 SkASSERT(start);
279 oper.skipTo(start);
280 oper.skipToEndBracket('(');
281 int parens = 0;
282 do {
283 if ('(' == oper.peek()) {
284 ++parens;
285 } else if (')' == oper.peek()) {
286 --parens;
287 }
288 } while (!oper.eof() && oper.next() && parens > 0);
289 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400290 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400291 }
292 }
293 if (!def) {
294 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
295 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
296 string constructorName = className + "::";
297 constructorName += string(token.fContentStart + skip,
298 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400299 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400300 }
301 if (!def && 0 == token.fName.find("SK_")) {
302 string incName = token.fName + "()";
303 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400304 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400305 if (def) {
306 if (def->fName == incName) {
307 def->fVisited = true;
308 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400309 def = root->find(className + "::toString",
310 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400311 if (def) {
312 def->fVisited = true;
313 } else {
314 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500315 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400316 }
317 }
318 break;
319 } else {
320 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500321 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400322 }
323 }
324 }
325 if (!def) {
326 bool allLower = true;
327 for (size_t index = 0; index < token.fName.length(); ++index) {
328 if (!islower(token.fName[index])) {
329 allLower = false;
330 break;
331 }
332 }
333 if (allLower) {
334 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400335 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400336 }
337 }
338 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400339 if (gAttrDeprecated == token.fName) {
340 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400341 break;
342 }
343 if (0 == token.fName.find("SkDEBUGCODE")) {
344 break;
345 }
346 }
347 if (!def) {
348 // simple method names inside nested classes have a bug and are missing trailing parens
349 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400350 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400351 }
352 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500353 if (!root->fDeprecated) {
354 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
355 fFailed = true;
356 }
Cary Clark8032b982017-07-28 11:04:54 -0400357 break;
358 }
Cary Clark73fa9722017-08-29 17:36:51 -0400359 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400360 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400361 if (MarkType::kDefinedBy == def->fMarkType) {
362 def->fParent->fVisited = true;
363 }
Cary Clark89b14562018-03-19 09:04:10 -0400364 if (token.fDeprecated && !def->fDeprecated) {
365 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
366 }
Cary Clark8032b982017-07-28 11:04:54 -0400367 } else {
368 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500369 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400370 }
371 } break;
372 case MarkType::kComment:
373 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400374 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400375 case MarkType::kEnum: {
376 if (!def) {
377 // work backwards from first word to deduce #Enum name
378 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
379 SkAssertResult(firstMember.skipName("enum"));
380 SkAssertResult(firstMember.skipToEndBracket('{'));
381 firstMember.next();
382 firstMember.skipWhiteSpace();
383 SkASSERT('k' == firstMember.peek());
384 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400385 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400386 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400387 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400388 const char* lastUnderscore = nullptr;
389 do {
390 if (!firstMember.skipToEndBracket('_')) {
391 break;
392 }
393 if (firstMember.fChar > wordEnd) {
394 break;
395 }
396 lastUnderscore = firstMember.fChar;
397 } while (firstMember.next());
398 if (lastUnderscore) {
399 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400400 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400401 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400402 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400403 }
404 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500405 if (!root->fDeprecated) {
406 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
407 fFailed = true;
408 }
Cary Clark8032b982017-07-28 11:04:54 -0400409 break;
410 }
411 }
412 def->fVisited = true;
413 for (auto& child : def->fChildren) {
414 if (MarkType::kCode == child->fMarkType) {
415 def = child;
416 break;
417 }
418 }
419 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500420 if (!root->fDeprecated) {
421 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
422 fFailed = true;
423 }
Cary Clark8032b982017-07-28 11:04:54 -0400424 break;
425 }
426 if (def->crossCheck(token)) {
427 def->fVisited = true;
428 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500429 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
430 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400431 }
432 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400433 string constName = MarkType::kEnumClass == token.fMarkType ?
434 fullName : className;
435 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400436 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400437 if (!def) {
438 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400439 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400440 }
441 if (!def) {
442 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500443 if (!root->fDeprecated) {
444 SkDebugf("const missing from bmh: %s\n", constName.c_str());
445 fFailed = true;
446 }
Cary Clark8032b982017-07-28 11:04:54 -0400447 }
448 } else {
449 def->fVisited = true;
450 }
451 }
452 } break;
453 case MarkType::kMember:
454 if (def) {
455 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500456 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400457 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500458 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400459 }
460 break;
Cary Clark2f466242017-12-11 16:03:17 -0500461 case MarkType::kTypedef:
462 if (def) {
463 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500464 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500465 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500466 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500467 }
468 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400469 case MarkType::kConst:
470 if (def) {
471 def->fVisited = true;
472 } else if (!root->fDeprecated) {
473 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
474 fFailed = true;
475 }
476 break;
Cary Clark8032b982017-07-28 11:04:54 -0400477 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400478 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400479 break;
480 }
481 }
482 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500483 int crossChecks = 0;
484 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400485 for (auto& classMapper : fIClassMap) {
486 string className = classMapper.first;
487 auto finder = bmhParser.fClassMap.find(className);
488 if (bmhParser.fClassMap.end() == finder) {
489 continue;
490 }
491 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500492 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500493 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400494 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500495 if (crossChecks) {
496 SkDebugf(".");
497 } else {
498 SkDebugf("cross-check");
499 firstCheck = className;
500 }
501 ++crossChecks;
502 }
503 if (crossChecks) {
504 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500505 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500506 }
507 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400508 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400509 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500510 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400511}
512
513IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400514 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400515 string className;
516 const Definition* test = fParent;
517 while (Definition::Type::kFileType != test->fType) {
518 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
519 className = test->fName + "::";
520 break;
521 }
522 test = test->fParent;
523 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400524 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400525 unordered_map<string, IClassDefinition>& map = fIClassMap;
526 IClassDefinition& markupDef = map[className];
527 if (markupDef.fStart) {
528 typedef IClassDefinition* IClassDefPtr;
529 return INHERITED::reportError<IClassDefPtr>("class already defined");
530 }
531 markupDef.fFileName = fFileName;
532 markupDef.fStart = includeDef.fStart;
533 markupDef.fContentStart = includeDef.fStart;
534 markupDef.fName = className;
535 markupDef.fContentEnd = includeDef.fContentEnd;
536 markupDef.fTerminator = includeDef.fTerminator;
537 markupDef.fParent = fParent;
538 markupDef.fLineCount = fLineCount;
539 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
540 MarkType::kStruct : MarkType::kClass;
541 markupDef.fKeyWord = includeDef.fKeyWord;
542 markupDef.fType = Definition::Type::kMark;
543 fParent = &markupDef;
544 return &markupDef;
545}
546
547void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
548 auto& tokens = classDef.fTokens;
549 for (auto& token : tokens) {
550 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
551 continue;
552 }
553 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400554 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400555 }
556 switch (token.fMarkType) {
557 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500558 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500559 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400560 break;
561 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400562 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400563 break;
564 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400565 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400566 continue;
567 break;
568 default:
569 SkASSERT(0);
570 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400571 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400572 }
573}
Cary Clark9174bda2017-09-19 17:39:32 -0400574void IncludeParser::dumpComment(const Definition& token) {
575 fLineCount = token.fLineCount;
576 fChar = fLine = token.fContentStart;
577 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400578 bool sawParam = false;
579 bool multiline = false;
580 bool sawReturn = false;
581 bool sawComment = false;
582 bool methodHasReturn = false;
583 vector<string> methodParams;
584 vector<string> foundParams;
585 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400586 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
587 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500588 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400589 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500590 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400591 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400592 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500593 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400594 && !methodParser.strnchr('~', methodParser.fEnd);
595 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
596 const char* nextEnd = paren;
597 do {
598 string paramName;
599 methodParser.fChar = nextEnd + 1;
600 methodParser.skipSpace();
601 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
602 continue;
603 }
604 methodParams.push_back(paramName);
605 } while (')' != nextEnd[0]);
606 }
Cary Clark9174bda2017-09-19 17:39:32 -0400607 for (const auto& child : token.fTokens) {
608 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
609 break;
610 }
Cary Clark8032b982017-07-28 11:04:54 -0400611 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400612 if (child.fPrivate) {
613 break;
614 }
Cary Clark8032b982017-07-28 11:04:54 -0400615 if ('@' == child.fContentStart[0]) {
616 TextParser parser(&child);
617 do {
618 parser.next();
619 if (parser.startsWith("param ")) {
620 parser.skipWord("param");
621 const char* parmStart = parser.fChar;
622 parser.skipToSpace();
623 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
624 parser.skipWhiteSpace();
625 do {
626 size_t nextComma = parmName.find(',');
627 string piece;
628 if (string::npos == nextComma) {
629 piece = parmName;
630 parmName = "";
631 } else {
632 piece = parmName.substr(0, nextComma);
633 parmName = parmName.substr(nextComma + 1);
634 }
635 if (sawParam) {
636 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400637 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400638 }
Cary Clark9174bda2017-09-19 17:39:32 -0400639 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400640 } else {
641 if (sawComment) {
642 this->nl();
643 }
644 this->lf(2);
645 }
646 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400647 this->writeTag("Param", piece);
648 this->writeSpace(2);
649 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
650 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400651 sawParam = true;
652 sawComment = false;
653 } while (parmName.length());
654 parser.skipTo(parser.fEnd);
655 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
656 parser.skipWord("return");
657 if ('s' == parser.peek()) {
658 parser.next();
659 }
660 if (sawParam) {
661 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400662 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400663 }
Cary Clark9174bda2017-09-19 17:39:32 -0400664 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400665 }
666 this->checkForMissingParams(methodParams, foundParams);
667 sawParam = false;
668 sawComment = false;
669 multiline = false;
670 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400671 this->writeTag("Return");
672 this->writeSpace(2);
673 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
674 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400675 sawReturn = true;
676 parser.skipTo(parser.fEnd);
677 } else {
678 this->reportError("unexpected doxygen directive");
679 }
680 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400681 } else if (child.length() > 1) {
682 const char* start = child.fContentStart;
683 ptrdiff_t length = child.fContentEnd - start;
684 SkASSERT(length >= 0);
685 while (length && '/' == start[0]) {
686 start += 1;
687 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400688 }
Cary Clark9174bda2017-09-19 17:39:32 -0400689 while (length && '/' == start[length - 1]) {
690 length -= 1;
691 if (length && '*' == start[length - 1]) {
692 length -= 1;
693 }
694 }
695 if (length) {
696 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
697 if (sawParam || sawReturn) {
698 this->indentToColumn(8);
699 }
700 this->writeBlock(length, start);
701 this->writeSpace();
702 sawComment = true;
703 if (sawParam || sawReturn) {
704 multiline = true;
705 }
Cary Clark8032b982017-07-28 11:04:54 -0400706 }
707 }
708 }
709 }
710 if (sawParam || sawReturn) {
711 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400712 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400713 }
Cary Clark9174bda2017-09-19 17:39:32 -0400714 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400715 }
716 if (!sawReturn) {
717 if (!sawParam) {
718 if (sawComment) {
719 this->nl();
720 }
721 this->lf(2);
722 }
723 this->checkForMissingParams(methodParams, foundParams);
724 }
725 if (methodHasReturn != sawReturn) {
726 if (!methodHasReturn) {
727 this->reportError("unexpected doxygen return");
728 } else {
729 if (sawComment) {
730 this->nl();
731 }
732 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400733 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400734 }
735 }
736}
737
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400738void IncludeParser::dumpCommonTail(const Definition& token) {
739 this->lf(2);
740 this->writeTag("Example");
741 this->lf(1);
742 this->writeString("// incomplete");
743 this->lf(1);
744 this->writeEndTag();
745 this->lf(2);
746 this->writeTag("SeeAlso");
747 this->writeSpace();
748 this->writeString("incomplete");
749 this->lf(2);
750 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
751 this->lf(2);
752}
753
754void IncludeParser::dumpDefine(const Definition& token) {
755 this->writeTag("Define", token.fName);
756 this->lf(2);
757 this->writeTag("Code");
758 this->lfAlways(1);
759 this->writeString("###$");
760 this->lfAlways(1);
761 this->indentToColumn(4);
762 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
763 this->lf(1);
764 this->indentToColumn(0);
765 this->writeString("$$$#");
766
767 this->writeEndTag();
768 this->lf(2);
769 this->dumpComment(token);
770 for (auto& child : token.fTokens) {
771 if (MarkType::kComment == child.fMarkType) {
772 continue;
773 }
774 this->writeTag("Param", child.fName);
775 this->writeSpace();
776 this->writeString("incomplete");
777 this->writeSpace();
778 this->writeString("##");
779 this->lf(1);
780 }
781}
782
783void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500784 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400785 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400786 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400787 this->lfAlways(1);
788 this->indentToColumn(4);
789 this->writeString("enum");
790 this->writeSpace();
791 if ("_anonymous" != token.fName.substr(0, 10)) {
792 this->writeString(token.fName);
793 this->writeSpace();
794 }
795 this->writeString("{");
796 this->lfAlways(1);
797 for (auto& child : token.fChildren) {
798 this->indentToColumn(8);
799 this->writeString(child->fName);
800 if (child->length()) {
801 this->writeSpace();
802 this->writeBlock(child->length(), child->fContentStart);
803 }
804 if (',' != fLastChar) {
805 this->writeString(",");
806 }
807 this->lfAlways(1);
808 }
809 this->indentToColumn(4);
810 this->writeString("};");
811 this->lf(1);
812 this->writeString("##");
813 this->lf(2);
814 this->dumpComment(token);
815 for (auto& child : token.fChildren) {
816 // start here;
817 // get comments before
818 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400819 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400820 this->writeSpace();
821 this->writeString(child->fName);
822 TextParser val(child);
823 if (!val.eof()) {
824 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
825 val.next();
826 val.skipSpace();
827 const char* valEnd = val.anyOf(",\n");
828 if (!valEnd) {
829 valEnd = val.fEnd;
830 }
831 this->writeSpace();
832 this->writeBlock(valEnd - val.fStart, val.fStart);
833 } else {
834 this->writeSpace();
835 this->writeDefinition(*child);
836 }
837 }
838 this->lf(1);
839 for (auto comment : child->fChildren) {
840 if (MarkType::kComment == comment->fMarkType) {
841 TextParser parser(comment);
842 parser.skipExact("*");
843 parser.skipExact("*");
844 while (!parser.eof() && parser.skipWhiteSpace()) {
845 parser.skipExact("*");
846 parser.skipWhiteSpace();
847 const char* start = parser.fChar;
848 parser.skipToEndBracket('\n');
849 this->lf(1);
850 this->writeBlock(parser.fChar - start, start);
851 }
852 }
853 }
854 this->writeEndTag();
855 }
856 this->lf(2);
857}
858
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400859bool IncludeParser::dumpGlobals() {
860 size_t lastBSlash = fFileName.rfind('\\');
861 size_t lastSlash = fFileName.rfind('/');
862 size_t lastDotH = fFileName.rfind(".h");
863 SkASSERT(string::npos != lastDotH);
864 if (string::npos != lastBSlash && (string::npos == lastSlash
865 || lastBSlash < lastSlash)) {
866 lastSlash = lastBSlash;
867 } else if (string::npos == lastSlash) {
868 lastSlash = -1;
869 }
870 lastSlash += 1;
871 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
872 string fileName = globalsName + "_Reference.bmh";
873 fOut = fopen(fileName.c_str(), "wb");
874 if (!fOut) {
875 SkDebugf("could not open output file %s\n", globalsName.c_str());
876 return false;
877 }
878 string prefixName = globalsName.substr(0, 2);
879 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
880 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
881 this->writeTagNoLF("Topic", topicName);
882 this->writeTag("Alias", topicName + "_Reference");
883 this->lf(2);
884 this->writeTag("Subtopic", "Overview");
885 fIndent += 4;
886 this->writeTag("Subtopic", "Subtopic");
887 fIndent += 4;
888 this->writeTag("Populate");
889 fIndent -= 4;
890 this->writeEndTag();
891 fIndent -= 4;
892 this->writeEndTag();
893 this->lf(2);
894 if (!fIDefineMap.empty()) {
895 this->writeTag("Subtopic", "Define");
896 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400897 this->writeEndTag();
898 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400899 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400900 if (!fIFunctionMap.empty()) {
901 this->writeTag("Subtopic", "Function");
902 this->writeTag("Populate");
903 this->writeEndTag();
904 this->lf(2);
905 }
906 if (!fIEnumMap.empty()) {
907 this->writeTag("Subtopic", "Enum");
908 this->writeTag("Populate");
909 this->writeEndTag();
910 this->lf(2);
911 }
912 if (!fITemplateMap.empty()) {
913 this->writeTag("Subtopic", "Template");
914 this->writeTag("Populate");
915 this->writeEndTag();
916 this->lf(2);
917 }
918 if (!fITypedefMap.empty()) {
919 this->writeTag("Subtopic", "Typedef");
920 this->writeTag("Populate");
921 this->writeEndTag();
922 this->lf(2);
923 }
924 if (!fIUnionMap.empty()) {
925 this->writeTag("Subtopic", "Union");
926 this->writeTag("Populate");
927 this->writeEndTag();
928 this->lf(2);
929 }
930 std::map<int, Definition*> sortedDefs;
931 for (const auto& entry : fIDefineMap) {
932 sortedDefs[entry.second->fLineCount] = entry.second;
933 }
934 for (const auto& entry : fIFunctionMap) {
935 sortedDefs[entry.second->fLineCount] = entry.second;
936 }
937 for (const auto& entry : fIEnumMap) {
938 sortedDefs[entry.second->fLineCount] = entry.second;
939 }
940 for (const auto& entry : fITemplateMap) {
941 sortedDefs[entry.second->fLineCount] = entry.second;
942 }
943 for (const auto& entry : fITypedefMap) {
944 sortedDefs[entry.second->fLineCount] = entry.second;
945 }
946 for (const auto& entry : fIUnionMap) {
947 sortedDefs[entry.second->fLineCount] = entry.second;
948 }
949 for (const auto& entry : sortedDefs) {
950 const Definition* def = entry.second;
951 this->writeBlockSeparator();
952 switch (def->fMarkType) {
953 case MarkType::kDefine:
954 this->dumpDefine(*def);
955 break;
956 case MarkType::kMethod:
957 this->dumpMethod(*def, globalsName);
958 break;
959 case MarkType::kEnum:
960 case MarkType::kEnumClass:
961 this->dumpEnum(*def, globalsName);
962 break;
963 case MarkType::kTemplate:
964 SkASSERT(0); // incomplete
965 break;
966 case MarkType::kTypedef: {
967 this->writeTag("Typedef");
968 this->writeSpace();
969 TextParser parser(def);
970 if (!parser.skipExact("typedef")) {
971 return false;
972 }
973 if (!parser.skipSpace()) {
974 return false;
975 }
976 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
977 this->lf(2);
978 this->dumpComment(*def);
979 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
980 this->lf(2);
981 } continue;
982 case MarkType::kUnion:
983 SkASSERT(0); // incomplete
984 break;
985 default:
986 SkASSERT(0);
987 }
988 this->dumpCommonTail(*def);
989 }
990 this->writeEndTag("Topic", topicName);
991 this->lfAlways(1);
992 fclose(fOut);
993 SkDebugf("wrote %s\n", fileName.c_str());
994 return true;
995}
996
997bool IncludeParser::isClone(const Definition& token) {
998 string name = token.fName;
999 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1000}
1001
1002bool IncludeParser::isConstructor(const Definition& token, string className) {
1003 string name = token.fName;
1004 return 0 == name.find(className) || '~' == name[0];
1005}
1006
1007bool IncludeParser::isInternalName(const Definition& token) {
1008 string name = token.fName;
1009 // exception for this SkCanvas function .. for now
1010 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1011 return false;
1012 }
1013 return name.substr(0, 7) == "android"
1014 || 0 == token.fName.find("internal_")
1015 || 0 == token.fName.find("Internal_")
1016 || 0 == token.fName.find("legacy_")
1017 || 0 == token.fName.find("temporary_")
1018 || 0 == token.fName.find("private_");
1019}
1020
1021bool IncludeParser::isOperator(const Definition& token) {
1022 return "operator" == token.fName.substr(0, 8);
1023}
1024
1025void IncludeParser::dumpMethod(const Definition& token, string className) {
1026 this->writeTag("Method");
1027 this->writeSpace();
1028
1029 string name = string(token.fStart ? token.fStart : token.fContentStart,
1030 token.length());
1031 if (this->isOperator(token)) {
1032 string spaceConst(" const");
1033 size_t constPos = name.rfind(spaceConst);
1034 if (name.length() - spaceConst.length() == constPos) {
1035 name = name.substr(0, constPos) + "_const";
1036 }
1037 }
1038 this->writeString(name);
1039 string inType;
1040 if (this->isConstructor(token, className)) {
1041 inType = "Constructor";
1042 } else if (this->isOperator(token)) {
1043 inType = "Operator";
1044 } else {
1045 inType = "incomplete";
1046 }
1047 this->writeTag("In", inType);
1048 this->writeTag("Line");
1049 this->writeSpace(1);
1050 this->writeString("#");
1051 this->writeSpace(1);
1052 this->writeString("incomplete");
1053 this->writeSpace(1);
1054 this->writeString("##");
1055 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001056 this->dumpComment(token);
1057}
1058
1059void IncludeParser::dumpMember(const Definition& token) {
1060 this->writeTag("Member");
1061 this->writeSpace();
1062 this->writeDefinition(token, token.fName, 2);
1063 lf(1);
1064 for (auto child : token.fChildren) {
1065 this->writeDefinition(*child);
1066 }
1067 this->writeEndTag();
1068 lf(2);
1069}
1070
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001071bool IncludeParser::dumpTokens() {
1072 if (!this->dumpGlobals()) {
1073 return false;
1074 }
Cary Clark9174bda2017-09-19 17:39:32 -04001075 for (const auto& member : fIClassMap) {
1076 if (string::npos != member.first.find("::")) {
1077 continue;
1078 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001079 if (!this->dumpTokens(member.first)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001080 return false;
1081 }
1082 }
1083 return true;
1084}
1085
Ben Wagner63fd7602017-10-09 15:45:33 -04001086 // dump equivalent markup
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001087bool IncludeParser::dumpTokens(string skClassName) {
1088 string fileName = skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -04001089 fOut = fopen(fileName.c_str(), "wb");
1090 if (!fOut) {
1091 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -04001092 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001093 }
1094 string prefixName = skClassName.substr(0, 2);
1095 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1096 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -04001097 this->writeTagNoLF("Topic", topicName);
1098 this->writeTag("Alias", topicName + "_Reference");
1099 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001100 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001101 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1102 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001103 this->writeTag(containerType, skClassName);
1104 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001105 auto& tokens = classMap.fTokens;
1106 for (auto& token : tokens) {
1107 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1108 continue;
1109 }
Cary Clark9174bda2017-09-19 17:39:32 -04001110 this->writeDefinition(token);
1111 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001112 }
1113 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001114 bool hasClass = false;
1115 bool hasConst = !fIEnumMap.empty();
1116 bool hasConstructor = false;
1117 bool hasMember = false;
1118 bool hasOperator = false;
Cary Clark8032b982017-07-28 11:04:54 -04001119 for (const auto& oneClass : fIClassMap) {
1120 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1121 continue;
1122 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001123 hasClass = true;
1124 break;
Cary Clark8032b982017-07-28 11:04:54 -04001125 }
Cary Clark8032b982017-07-28 11:04:54 -04001126 for (const auto& token : classMap.fTokens) {
1127 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1128 continue;
1129 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001130 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001131 continue;
1132 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001133 if (this->isConstructor(token, skClassName)) {
1134 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001135 continue;
1136 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001137 if (this->isOperator(token)) {
1138 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001139 continue;
1140 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001141 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001142 continue;
1143 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001144 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001145 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001146 this->writeTag("Subtopic", "Overview");
1147 fIndent += 4;
1148 this->writeTag("Subtopic", "Subtopic");
1149 fIndent += 4;
1150 this->writeTag("Populate");
1151 fIndent -= 4;
1152 this->writeEndTag();
1153 fIndent -= 4;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001154 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001155 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001156
1157 if (hasClass) {
1158 this->writeTag("Subtopic", "Class_or_Struct");
1159 this->writeTag("Populate");
1160 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001161 this->lf(2);
1162 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001163 if (hasConst) {
1164 this->writeTag("Subtopic", "Constant");
1165 this->writeTag("Populate");
1166 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001167 this->lf(2);
1168 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001169 if (hasConstructor) {
1170 this->writeTag("Subtopic", "Constructor");
1171 this->writeTag("Populate");
1172 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001173 this->lf(2);
1174 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001175 if (hasOperator) {
1176 this->writeTag("Subtopic", "Operator");
1177 this->writeTag("Populate");
1178 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001179 this->lf(2);
1180 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001181 if (hasMember) {
1182 this->writeTag("Subtopic", "Member_Function");
1183 this->writeTag("Populate");
1184 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001185 this->lf(2);
1186 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001187 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001188 this->writeBlockSeparator();
1189 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001190 this->lf(2);
1191 this->writeTag("Example");
1192 this->lfcr();
1193 this->writeString("// incomplete");
1194 this->writeEndTag();
1195 this->lf(2);
1196 this->writeTag("SeeAlso", "incomplete");
1197 this->lf(2);
1198 this->writeEndTag("Enum", oneEnum.first);
1199 this->lf(2);
1200 }
Cary Clark8032b982017-07-28 11:04:54 -04001201 for (auto& oneClass : fIClassMap) {
1202 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1203 continue;
1204 }
1205 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001206 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001207 KeyWord keyword = oneClass.second.fKeyWord;
1208 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1209 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001210 this->writeTag(containerType, innerName);
1211 this->lf(2);
1212 this->writeTag("Code");
1213 this->writeEndTag("ToDo", "fill this in manually");
1214 this->writeEndTag();
1215 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001216 for (auto& token : oneClass.second.fTokens) {
1217 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1218 continue;
1219 }
Cary Clark9174bda2017-09-19 17:39:32 -04001220 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001221 }
1222 this->lf(2);
1223 this->dumpClassTokens(oneClass.second);
1224 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001225 this->writeEndTag(containerType, innerName);
1226 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001227 }
1228 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001229 this->writeEndTag(containerType, skClassName);
1230 this->lf(2);
1231 this->writeEndTag("Topic", topicName);
1232 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001233 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001234 SkDebugf("wrote %s\n", fileName.c_str());
1235 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001236}
1237
1238bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1239 // add comment preceding class, if any
1240 const Definition* parent = includeDef.fParent;
1241 int index = includeDef.fParentIndex;
1242 auto wordIter = parent->fTokens.begin();
1243 std::advance(wordIter, index);
1244 SkASSERT(&*wordIter == &includeDef);
1245 while (parent->fTokens.begin() != wordIter) {
1246 auto testIter = std::prev(wordIter);
1247 if (Definition::Type::kWord != testIter->fType
1248 && Definition::Type::kKeyWord != testIter->fType
1249 && (Definition::Type::kBracket != testIter->fType
1250 || Bracket::kAngle != testIter->fBracket)
1251 && (Definition::Type::kPunctuation != testIter->fType
1252 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1253 break;
1254 }
1255 wordIter = testIter;
1256 }
1257 auto commentIter = wordIter;
1258 while (parent->fTokens.begin() != commentIter) {
1259 auto testIter = std::prev(commentIter);
1260 bool isComment = Definition::Type::kBracket == testIter->fType
1261 && (Bracket::kSlashSlash == testIter->fBracket
1262 || Bracket::kSlashStar == testIter->fBracket);
1263 if (!isComment) {
1264 break;
1265 }
1266 commentIter = testIter;
1267 }
1268 while (commentIter != wordIter) {
1269 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1270 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1271 return false;
1272 }
1273 commentIter = std::next(commentIter);
1274 }
1275 return true;
1276}
1277
1278// caller calls reportError, so just return false here
1279bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1280 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001281 // parse class header
1282 auto iter = includeDef->fTokens.begin();
1283 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1284 // todo : documentation is ignoring this for now
1285 iter = std::next(iter);
1286 }
1287 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1288 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001289 iter = std::next(iter);
1290 if (iter == includeDef->fTokens.end()) {
1291 return true; // forward declaration only
1292 }
Cary Clark8032b982017-07-28 11:04:54 -04001293 do {
1294 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001295 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001296 }
1297 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1298 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001299 }
Cary Clark8032b982017-07-28 11:04:54 -04001300 } while (static_cast<void>(iter = std::next(iter)), true);
1301 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001302 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001303 }
1304 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1305 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001306 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001307 }
1308 markupDef->fStart = iter->fStart;
1309 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001310 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001311 }
1312// if (1 != includeDef->fChildren.size()) {
1313// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1314// }
1315 includeDef = includeDef->fChildren.front();
1316 iter = includeDef->fTokens.begin();
1317 // skip until public
1318 int publicIndex = 0;
1319 if (IsStruct::kNo == isStruct) {
1320 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1321 size_t publicLen = strlen(publicName);
1322 while (iter != includeDef->fTokens.end()
1323 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1324 || strncmp(iter->fStart, publicName, publicLen))) {
1325 iter = std::next(iter);
1326 ++publicIndex;
1327 }
1328 }
1329 auto childIter = includeDef->fChildren.begin();
1330 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1331 (*childIter)->fPrivate = true;
1332 childIter = std::next(childIter);
1333 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001334 int keyIndex = publicIndex;
1335 KeyWord currentKey = KeyWord::kPublic;
1336 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1337 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001338 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1339 size_t protectedLen = strlen(protectedName);
1340 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1341 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001342 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001343 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001344 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1345 const char* testStart = iter->fStart;
1346 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1347 iter = std::next(iter);
1348 ++keyIndex;
1349 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1350 currentKey = KeyWord::kPublic;
1351 break;
1352 }
1353 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1354 currentKey = KeyWord::kProtected;
1355 break;
1356 }
1357 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1358 currentKey = KeyWord::kPrivate;
1359 break;
1360 }
1361 }
1362 fLastObject = nullptr;
1363 if (KeyWord::kPublic == currentKey) {
1364 if (!this->parseObject(child, markupDef)) {
1365 return false;
1366 }
1367 } else {
1368 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001369 }
Cary Clark73fa9722017-08-29 17:36:51 -04001370 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001371 childIter = std::next(childIter);
1372 }
Cary Clark8032b982017-07-28 11:04:54 -04001373 SkASSERT(fParent->fParent);
1374 fParent = fParent->fParent;
1375 return true;
1376}
1377
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001378bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001379 int lineCount, Definition* markupDef) {
1380 TextParser parser(filename, start, end, lineCount);
1381 // parse doxygen if present
1382 if (parser.startsWith("**")) {
1383 parser.next();
1384 parser.next();
1385 parser.skipWhiteSpace();
1386 if ('\\' == parser.peek()) {
1387 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001388 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1389 if (parser.skipExact("file")) {
1390 if (Definition::Type::kFileType != fParent->fType) {
1391 return reportError<bool>("expected parent is file");
1392 }
1393 string filename = markupDef->fileName();
1394 if (!parser.skipWord(filename.c_str())) {
1395 return reportError<bool>("missing object type");
1396 }
1397 } else if (parser.skipExact("fn")) {
1398 SkASSERT(0); // incomplete
1399 } else {
1400 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1401 return reportError<bool>("missing object type");
1402 }
1403 if (!parser.skipWord(markupDef->fName.c_str()) &&
1404 KeyWord::kEnum != markupDef->fKeyWord) {
1405 return reportError<bool>("missing object name");
1406 }
Cary Clark8032b982017-07-28 11:04:54 -04001407 }
Cary Clark8032b982017-07-28 11:04:54 -04001408 }
1409 }
1410 // remove leading '*' if present
1411 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1412 while (!parser.eof() && parser.skipWhiteSpace()) {
1413 while ('*' == parser.peek()) {
1414 parser.next();
1415 if (parser.eof()) {
1416 break;
1417 }
1418 parser.skipWhiteSpace();
1419 }
1420 if (parser.eof()) {
1421 break;
1422 }
1423 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001424 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001425 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001426 parser.skipToEndBracket('\n');
1427 }
1428 return true;
1429}
1430
Cary Clarkd98f78c2018-04-26 08:32:37 -04001431bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
1432 // todo: hard code to constexpr for now
1433 TextParser constParser(child);
1434 if (!constParser.skipExact("static")) {
1435 return false;
1436 }
1437 constParser.skipWhiteSpace();
1438 if (!constParser.skipExact("constexpr")) {
1439 return false;
1440 }
1441 constParser.skipWhiteSpace();
1442 const char* typeStart = constParser.fChar;
1443 constParser.skipToSpace();
1444 KeyWord constType = FindKey(typeStart, constParser.fChar);
1445 if (KeyWord::kNone == constType) {
1446 // todo: this could be a non-keyword, ... do we need to look for type?
1447 return false;
1448 }
1449 constParser.skipWhiteSpace();
1450 const char* nameStart = constParser.fChar;
1451 constParser.skipToSpace();
1452 string nameStr = string(nameStart, constParser.fChar - nameStart);
1453 if (!markupDef) {
1454 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1455 child->fLineCount, fParent, '\0');
1456 Definition* globalMarkupChild = &fGlobals.back();
1457 string globalUniqueName = this->uniqueName(fIConstMap, nameStr);
1458 globalMarkupChild->fName = globalUniqueName;
1459 if (!this->findComments(*child, globalMarkupChild)) {
1460 return false;
1461 }
1462 fIConstMap[globalUniqueName] = globalMarkupChild;
1463 return true;
1464 }
1465 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1466 child->fLineCount, markupDef, '\0');
1467 Definition* markupChild = &markupDef->fTokens.back();
1468 markupChild->fName = nameStr;
1469 markupChild->fTerminator = markupChild->fContentEnd;
1470 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1471 classDef.fConsts[nameStr] = markupChild;
1472 return true;
1473}
1474
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001475bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1476 TextParser parser(child);
1477 if (!parser.skipExact("#define")) {
1478 return false;
1479 }
1480 if (!parser.skipSpace()) {
1481 return false;
1482 }
1483 const char* nameStart = parser.fChar;
1484 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1485 if (parser.eof()) {
1486 return true; // do nothing if #define doesn't define anything
1487 }
1488 string nameStr(nameStart, parser.fChar - nameStart);
1489 struct Param {
1490 const char* fStart;
1491 const char* fEnd;
1492 };
1493 vector<Param> params;
1494 if ('(' == parser.peek()) {
1495 parser.next();
1496 if (!parser.skipSpace()) {
1497 return false;
1498 }
1499 do {
1500 const char* paramStart = parser.fChar;
1501 if (!parser.skipExact("...")) {
1502 parser.skipToNonAlphaNum();
1503 }
1504 if (parser.eof()) {
1505 return false;
1506 }
1507 params.push_back({paramStart, parser.fChar});
1508 if (!parser.skipSpace()) {
1509 return false;
1510 }
1511 if (')' == parser.peek()) {
1512 parser.next();
1513 break;
1514 }
1515 if (',' != parser.next()) {
1516 return false;
1517 }
1518 if (!parser.skipSpace()) {
1519 return false;
1520 }
1521 } while (true);
1522 }
1523 if (!parser.skipSpace()) {
1524 return false;
1525 }
1526 if (!markupDef) {
1527 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1528 child->fLineCount, fParent, '\0');
1529 Definition* globalMarkupChild = &fGlobals.back();
1530 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1531 globalMarkupChild->fName = globalUniqueName;
1532 globalMarkupChild->fTerminator = child->fContentEnd;
1533 if (!this->findComments(*child, globalMarkupChild)) {
1534 return false;
1535 }
1536 fIDefineMap[globalUniqueName] = globalMarkupChild;
1537 for (Param param : params) {
1538 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1539 child->fLineCount, globalMarkupChild, '\0');
1540 Definition* paramChild = &globalMarkupChild->fTokens.back();
1541 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1542 paramChild->fTerminator = param.fEnd;
1543 }
1544 return true;
1545 }
1546 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1547 child->fLineCount, markupDef, '\0');
1548 Definition* markupChild = &markupDef->fTokens.back();
1549 markupChild->fName = nameStr;
1550 markupChild->fTerminator = markupChild->fContentEnd;
1551 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1552 if (!this->findComments(*child, markupChild)) {
1553 return false;
1554 }
1555 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001556 return true;
1557}
1558
1559bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001560 TextParser parser(child);
1561 parser.skipToEndBracket('{');
1562 if (parser.eof()) {
1563 return true; // if enum is a forward declaration, do nothing
1564 }
1565 parser.next();
1566 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001567 if (child->fTokens.size() > 0) {
1568 auto token = child->fTokens.begin();
1569 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1570 token = token->fTokens.begin();
1571 }
1572 if (Definition::Type::kWord == token->fType) {
1573 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1574 }
1575 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001576 Definition* markupChild;
1577 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001578 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1579 child->fLineCount, fParent, '\0');
1580 markupChild = &fGlobals.back();
1581 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1582 markupChild->fName = globalUniqueName;
1583 markupChild->fTerminator = child->fContentEnd;
1584 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001585 } else {
1586 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001587 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001588 markupChild = &markupDef->fTokens.back();
1589 }
Cary Clark8032b982017-07-28 11:04:54 -04001590 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1591 markupChild->fKeyWord = KeyWord::kEnum;
1592 TextParser enumName(child);
1593 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001594 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001595 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001596 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001597 markupChild->fMarkType = MarkType::kEnumClass;
1598 }
Cary Clark8032b982017-07-28 11:04:54 -04001599 const char* nameStart = enumName.fChar;
1600 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001601 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001602 markupChild->fName = markupDef->fName + "::" +
1603 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001604 }
Cary Clark8032b982017-07-28 11:04:54 -04001605 if (!this->findComments(*child, markupChild)) {
1606 return false;
1607 }
Cary Clark8032b982017-07-28 11:04:54 -04001608 const char* dataEnd;
1609 do {
Cary Clark8032b982017-07-28 11:04:54 -04001610 parser.skipWhiteSpace();
1611 if ('}' == parser.peek()) {
1612 break;
1613 }
1614 Definition* comment = nullptr;
1615 // note that comment, if any, can be before or after (on the same line, though) as member
1616 if ('#' == parser.peek()) {
1617 // fixme: handle preprecessor, but just skip it for now
1618 parser.skipToLineStart();
1619 }
1620 while (parser.startsWith("/*") || parser.startsWith("//")) {
1621 parser.next();
1622 const char* start = parser.fChar;
1623 const char* end;
1624 if ('*' == parser.peek()) {
1625 end = parser.strnstr("*/", parser.fEnd);
1626 parser.fChar = end;
1627 parser.next();
1628 parser.next();
1629 } else {
1630 end = parser.trimmedLineEnd();
1631 parser.skipToLineStart();
1632 }
1633 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001634 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001635 comment = &markupChild->fTokens.back();
1636 comment->fTerminator = end;
1637 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1638 return false;
1639 }
1640 parser.skipWhiteSpace();
1641 }
1642 parser.skipWhiteSpace();
1643 const char* memberStart = parser.fChar;
1644 if ('}' == memberStart[0]) {
1645 break;
1646 }
Cary Clark9174bda2017-09-19 17:39:32 -04001647 // if there's comment on same the line as member def, output first as if it was before
1648
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001649 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001650 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001651 if (parser.eof() || !parser.skipWhiteSpace()) {
1652 return this->reportError<bool>("enum member must end with comma 1");
1653 }
Cary Clark8032b982017-07-28 11:04:54 -04001654 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001655 if ('=' == parser.peek()) {
1656 parser.skipToEndBracket(',');
1657 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001658 if (!parser.eof() && '#' == parser.peek()) {
1659 // fixme: handle preprecessor, but just skip it for now
1660 continue;
1661 }
Cary Clark9174bda2017-09-19 17:39:32 -04001662 if (parser.eof() || ',' != parser.peek()) {
1663 return this->reportError<bool>("enum member must end with comma 2");
1664 }
1665 dataEnd = parser.fChar;
1666 const char* start = parser.anyOf("/\n");
1667 SkASSERT(start);
1668 parser.skipTo(start);
1669 if ('/' == parser.next()) {
1670 char slashStar = parser.next();
1671 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001672 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001673 char doxCheck = parser.next();
1674 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1675 save.restore();
1676 }
1677 }
1678 parser.skipWhiteSpace();
1679 const char* commentStart = parser.fChar;
1680 if ('/' == slashStar) {
1681 parser.skipToEndBracket('\n');
1682 } else {
1683 parser.skipToEndBracket("*/");
1684 }
1685 SkASSERT(!parser.eof());
1686 const char* commentEnd = parser.fChar;
1687 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001688 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001689 comment = &markupChild->fTokens.back();
1690 comment->fTerminator = commentEnd;
1691 }
Cary Clark8032b982017-07-28 11:04:54 -04001692 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001693 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001694 Definition* member = &markupChild->fTokens.back();
1695 member->fName = memberName;
1696 if (comment) {
1697 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001698 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001699 }
1700 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001701 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001702 for (auto outsideMember : child->fChildren) {
1703 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001704 continue;
1705 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001706 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1707 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001708 continue;
1709 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001710 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1711 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001712 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001713 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001714 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001715 // FIXME: ? add comment as well ?
1716 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001717 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001718 if (markupDef) {
1719 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1720 SkASSERT(classDef.fStart);
1721 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1722 markupChild->fName = uniqueName;
1723 classDef.fEnums[uniqueName] = markupChild;
1724 }
Cary Clark8032b982017-07-28 11:04:54 -04001725 return true;
1726}
1727
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001728bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001729 fParent = &fIncludeMap[name];
1730 fParent->fName = name;
1731 fParent->fFileName = fFileName;
1732 fParent->fType = Definition::Type::kFileType;
1733 fParent->fContentStart = fChar;
1734 fParent->fContentEnd = fEnd;
1735 // parse include file into tree
1736 while (fChar < fEnd) {
1737 if (!this->parseChar()) {
1738 return false;
1739 }
1740 }
1741 // parse tree and add named objects to maps
1742 fParent = &fIncludeMap[name];
1743 if (!this->parseObjects(fParent, nullptr)) {
1744 return false;
1745 }
1746 return true;
1747}
1748
1749bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1750 const char* typeStart = child->fChildren[0]->fContentStart;
1751 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001752 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001753 Definition* markupChild = &markupDef->fTokens.back();
1754 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001755 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001756 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1757 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1758 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1759 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001760 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001761 classDef.fMembers[uniqueName] = markupChild;
1762 if (child->fParentIndex >= 2) {
1763 auto comment = child->fParent->fTokens.begin();
1764 std::advance(comment, child->fParentIndex - 2);
1765 if (Definition::Type::kBracket == comment->fType
1766 && (Bracket::kSlashStar == comment->fBracket
1767 || Bracket::kSlashSlash == comment->fBracket)) {
1768 TextParser parser(&*comment);
1769 do {
1770 parser.skipToAlpha();
1771 if (parser.eof()) {
1772 break;
1773 }
1774 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001775 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001776 if (Bracket::kSlashStar == comment->fBracket) {
1777 const char* commentEnd = parser.strnstr("*/", end);
1778 if (commentEnd) {
1779 end = commentEnd;
1780 }
1781 }
1782 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001783 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001784 Definition* commentChild = &markupDef->fTokens.back();
1785 markupChild->fChildren.emplace_back(commentChild);
1786 parser.skipTo(end);
1787 } while (!parser.eof());
1788 }
1789 }
1790 return true;
1791}
1792
1793bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1794 auto tokenIter = child->fParent->fTokens.begin();
1795 std::advance(tokenIter, child->fParentIndex);
1796 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001797 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001798 bool addConst = false;
1799 auto operatorCheck = tokenIter;
1800 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1801 operatorCheck = std::prev(tokenIter);
1802 }
1803 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001804 auto closeParen = std::next(tokenIter);
1805 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1806 '(' == closeParen->fContentStart[0]);
1807 nameEnd = closeParen->fContentEnd + 1;
1808 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001809 if (Definition::Type::kKeyWord == closeParen->fType &&
1810 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001811 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001812 }
Cary Clarka560c472017-11-27 10:44:06 -05001813 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001814 }
1815 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001816 if (addConst) {
1817 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001818 }
Cary Clark8032b982017-07-28 11:04:54 -04001819 while (tokenIter != child->fParent->fTokens.begin()) {
1820 auto testIter = std::prev(tokenIter);
1821 switch (testIter->fType) {
1822 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001823 if (testIter == child->fParent->fTokens.begin() &&
1824 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1825 KeyWord::kIfndef == child->fParent->fKeyWord ||
1826 KeyWord::kIf == child->fParent->fKeyWord)) {
1827 std::next(tokenIter);
1828 break;
1829 }
Cary Clark8032b982017-07-28 11:04:54 -04001830 goto keepGoing;
1831 case Definition::Type::kKeyWord: {
1832 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1833 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1834 goto keepGoing;
1835 }
1836 } break;
1837 case Definition::Type::kBracket:
1838 if (Bracket::kAngle == testIter->fBracket) {
1839 goto keepGoing;
1840 }
1841 break;
1842 case Definition::Type::kPunctuation:
1843 if (Punctuation::kSemicolon == testIter->fPunctuation
1844 || Punctuation::kLeftBrace == testIter->fPunctuation
1845 || Punctuation::kColon == testIter->fPunctuation) {
1846 break;
1847 }
1848 keepGoing:
1849 tokenIter = testIter;
1850 continue;
1851 default:
1852 break;
1853 }
1854 break;
1855 }
1856 tokenIter->fName = nameStr;
1857 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001858 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001859 auto testIter = child->fParent->fTokens.begin();
1860 SkASSERT(child->fParentIndex > 0);
1861 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001862 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1863 0 == tokenIter->fParentIndex) {
1864 tokenIter = std::next(tokenIter);
1865 }
Cary Clark8032b982017-07-28 11:04:54 -04001866 const char* start = tokenIter->fContentStart;
1867 const char* end = tokenIter->fContentEnd;
1868 const char kDebugCodeStr[] = "SkDEBUGCODE";
1869 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1870 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1871 std::advance(testIter, 1);
1872 start = testIter->fContentStart + 1;
1873 end = testIter->fContentEnd - 1;
1874 } else {
1875 end = testIter->fContentEnd;
1876 while (testIter != child->fParent->fTokens.end()) {
1877 testIter = std::next(testIter);
1878 switch (testIter->fType) {
1879 case Definition::Type::kPunctuation:
1880 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1881 || Punctuation::kLeftBrace == testIter->fPunctuation
1882 || Punctuation::kColon == testIter->fPunctuation);
1883 end = testIter->fStart;
1884 break;
1885 case Definition::Type::kKeyWord: {
1886 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1887 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1888 continue;
1889 }
1890 } break;
1891 default:
1892 continue;
1893 }
1894 break;
1895 }
1896 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001897 while (end > start && ' ' >= end[-1]) {
1898 --end;
1899 }
Cary Clark9174bda2017-09-19 17:39:32 -04001900 if (!markupDef) {
1901 auto parentIter = child->fParent->fTokens.begin();
1902 SkASSERT(child->fParentIndex > 0);
1903 std::advance(parentIter, child->fParentIndex - 1);
1904 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001905 TextParser nameParser(methodName);
1906 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001907 return true; // expect this is inline class definition outside of class
1908 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001909 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1910 fParent, '\0');
1911 Definition* globalMarkupChild = &fGlobals.back();
1912 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1913 globalMarkupChild->fName = globalUniqueName;
1914 if (!this->findComments(*child, globalMarkupChild)) {
1915 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001916 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001917 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001918 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001919 }
Cary Clark8032b982017-07-28 11:04:54 -04001920 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001921 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001922 Definition* markupChild = &markupDef->fTokens.back();
1923 // do find instead -- I wonder if there is a way to prevent this in c++
1924 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1925 SkASSERT(classDef.fStart);
1926 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1927 markupChild->fName = uniqueName;
1928 if (!this->findComments(*child, markupChild)) {
1929 return false;
1930 }
1931 classDef.fMethods[uniqueName] = markupChild;
1932 return true;
1933}
1934
Cary Clark8032b982017-07-28 11:04:54 -04001935bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1936 for (auto& child : parent->fChildren) {
1937 if (!this->parseObject(child, markupDef)) {
1938 return false;
1939 }
1940 }
1941 return true;
1942}
1943
1944bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1945 // set up for error reporting
1946 fLine = fChar = child->fStart;
1947 fEnd = child->fContentEnd;
1948 // todo: put original line number in child as well
1949 switch (child->fType) {
1950 case Definition::Type::kKeyWord:
1951 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001952 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001953 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001954 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001955 }
1956 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001957 case KeyWord::kStatic:
1958 if (!this->parseConst(child, markupDef)) {
1959 return child->reportError<bool>("failed to parse const or constexpr");
1960 }
1961 break;
Cary Clark8032b982017-07-28 11:04:54 -04001962 case KeyWord::kEnum:
1963 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001964 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001965 }
1966 break;
1967 case KeyWord::kStruct:
1968 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001969 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001970 }
1971 break;
1972 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001973 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001974 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001975 }
1976 break;
1977 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001978 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001979 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001980 }
1981 break;
1982 case KeyWord::kUnion:
1983 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001984 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001985 }
1986 break;
1987 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001988 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001989 }
1990 break;
1991 case Definition::Type::kBracket:
1992 switch (child->fBracket) {
1993 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001994 if (fLastObject) {
1995 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1996 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001997 if (!checkDeprecated.eof()) {
1998 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04001999 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2000 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002001 break;
2002 }
2003 }
2004 }
2005 {
2006 auto tokenIter = child->fParent->fTokens.begin();
2007 std::advance(tokenIter, child->fParentIndex);
2008 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002009 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002010 if (previousToken.startsWith(gAttrDeprecated)) {
2011 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002012 break;
2013 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002014 if (Bracket::kPound == child->fParent->fBracket &&
2015 KeyWord::kIf == child->fParent->fKeyWord) {
2016 // TODO: this will skip methods named defined() -- for the
2017 // moment there aren't any
2018 if (previousToken.startsWith("defined")) {
2019 break;
2020 }
2021 }
Cary Clark73fa9722017-08-29 17:36:51 -04002022 }
Cary Clark8032b982017-07-28 11:04:54 -04002023 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002024 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002025 }
Cary Clark89b14562018-03-19 09:04:10 -04002026 if (fAttrDeprecated) {
2027 Definition* lastMethod = &markupDef->fTokens.back();
2028 lastMethod->fDeprecated = true;
2029 fAttrDeprecated = nullptr;
2030 }
Cary Clark73fa9722017-08-29 17:36:51 -04002031 break;
Cary Clark8032b982017-07-28 11:04:54 -04002032 case Bracket::kSlashSlash:
2033 case Bracket::kSlashStar:
2034 // comments are picked up by parsing objects first
2035 break;
2036 case Bracket::kPound:
2037 // special-case the #xxx xxx_DEFINED entries
2038 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002039 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002040 case KeyWord::kIfndef:
2041 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002042 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002043 if (!this->parseObjects(child, markupDef)) {
2044 return false;
2045 }
2046 break;
2047 }
2048 goto preproError;
2049 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002050 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002051 break;
2052 }
2053 goto preproError;
2054 case KeyWord::kEndif:
2055 if (child->boilerplateEndIf()) {
2056 break;
2057 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002058 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002059 case KeyWord::kInclude:
2060 // ignored for now
2061 break;
2062 case KeyWord::kElse:
2063 case KeyWord::kElif:
2064 // todo: handle these
2065 break;
2066 default:
2067 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002068 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002069 }
2070 break;
2071 case Bracket::kAngle:
2072 // pick up templated function pieces when method is found
2073 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002074 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002075 if (!this->parseObjects(child, markupDef)) {
2076 return false;
2077 }
Cary Clark73fa9722017-08-29 17:36:51 -04002078 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002079 case Bracket::kSquare: {
2080 // check to see if parent is operator, the only case we handle so far
2081 auto prev = child->fParent->fTokens.begin();
2082 std::advance(prev, child->fParentIndex - 1);
2083 if (KeyWord::kOperator != prev->fKeyWord) {
2084 return child->reportError<bool>("expected operator overload");
2085 }
2086 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002087 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002088 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002089 }
2090 break;
2091 case Definition::Type::kWord:
2092 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002093 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002094 }
2095 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002096 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002097 }
2098 break;
2099 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002100 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002101 break;
2102 }
2103 return true;
2104}
2105
Cary Clarkbbfda252018-03-09 15:32:01 -05002106bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2107 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002108}
2109
Cary Clark2f466242017-12-11 16:03:17 -05002110bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2111 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002112 typedefParser.skipExact("typedef");
2113 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002114 string nameStr = typedefParser.typedefName();
2115 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002116 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2117 child->fLineCount, fParent, '\0');
2118 Definition* globalMarkupChild = &fGlobals.back();
2119 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2120 globalMarkupChild->fName = globalUniqueName;
2121 if (!this->findComments(*child, globalMarkupChild)) {
2122 return false;
2123 }
2124 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark2f466242017-12-11 16:03:17 -05002125 return true;
2126 }
2127 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002128 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002129 Definition* markupChild = &markupDef->fTokens.back();
2130 markupChild->fName = nameStr;
2131 markupChild->fTerminator = markupChild->fContentEnd;
2132 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2133 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002134 return true;
2135}
2136
2137bool IncludeParser::parseUnion() {
2138
2139 return true;
2140}
2141
2142bool IncludeParser::parseChar() {
2143 char test = *fChar;
2144 if ('\\' == fPrev) {
2145 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002146// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002147 fLine = fChar + 1;
2148 }
2149 goto done;
2150 }
2151 switch (test) {
2152 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002153// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002154 fLine = fChar + 1;
2155 if (fInChar) {
2156 return reportError<bool>("malformed char");
2157 }
2158 if (fInString) {
2159 return reportError<bool>("malformed string");
2160 }
2161 if (!this->checkForWord()) {
2162 return false;
2163 }
2164 if (Bracket::kPound == this->topBracket()) {
2165 KeyWord keyWord = fParent->fKeyWord;
2166 if (KeyWord::kNone == keyWord) {
2167 return this->reportError<bool>("unhandled preprocessor directive");
2168 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002169 if (fInDefine) {
2170 SkASSERT(KeyWord::kDefine == keyWord);
2171 fInDefine = false;
2172 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002173 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002174 this->popBracket();
2175 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002176 if (fInBrace) {
2177 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2178 fInBrace = nullptr;
2179 }
Cary Clark8032b982017-07-28 11:04:54 -04002180 } else if (Bracket::kSlashSlash == this->topBracket()) {
2181 this->popBracket();
2182 }
2183 break;
2184 case '*':
2185 if (!fInCharCommentString && '/' == fPrev) {
2186 this->pushBracket(Bracket::kSlashStar);
2187 }
2188 if (!this->checkForWord()) {
2189 return false;
2190 }
2191 if (!fInCharCommentString) {
2192 this->addPunctuation(Punctuation::kAsterisk);
2193 }
2194 break;
2195 case '/':
2196 if ('*' == fPrev) {
2197 if (!fInCharCommentString) {
2198 return reportError<bool>("malformed closing comment");
2199 }
2200 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002201 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002202 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002203 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002204 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002205 }
2206 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002207 }
Cary Clark8032b982017-07-28 11:04:54 -04002208 if (!fInCharCommentString && '/' == fPrev) {
2209 this->pushBracket(Bracket::kSlashSlash);
2210 break;
2211 }
2212 if (!this->checkForWord()) {
2213 return false;
2214 }
2215 break;
2216 case '\'':
2217 if (Bracket::kChar == this->topBracket()) {
2218 this->popBracket();
2219 } else if (!fInComment && !fInString) {
2220 if (fIncludeWord) {
2221 return this->reportError<bool>("word then single-quote");
2222 }
2223 this->pushBracket(Bracket::kChar);
2224 }
2225 break;
2226 case '\"':
2227 if (Bracket::kString == this->topBracket()) {
2228 this->popBracket();
2229 } else if (!fInComment && !fInChar) {
2230 if (fIncludeWord) {
2231 return this->reportError<bool>("word then double-quote");
2232 }
2233 this->pushBracket(Bracket::kString);
2234 }
2235 break;
Cary Clark8032b982017-07-28 11:04:54 -04002236 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002237 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002238 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2239 this->pushBracket(Bracket::kDebugCode);
2240 break;
2241 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002242 case ':':
2243 case '[':
2244 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002245 if (fInCharCommentString) {
2246 break;
2247 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002248 if (fInDefine && fInBrace) {
2249 break;
2250 }
Cary Clark8032b982017-07-28 11:04:54 -04002251 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2252 break;
2253 }
2254 if (!fInBrace) {
2255 if (!this->checkForWord()) {
2256 return false;
2257 }
2258 if (':' == test && !fInFunction) {
2259 break;
2260 }
2261 if ('{' == test) {
2262 this->addPunctuation(Punctuation::kLeftBrace);
2263 } else if (':' == test) {
2264 this->addPunctuation(Punctuation::kColon);
2265 }
2266 }
2267 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2268 && Bracket::kColon == fInBrace->fBracket) {
2269 Definition* braceParent = fParent->fParent;
2270 braceParent->fChildren.pop_back();
2271 braceParent->fTokens.pop_back();
2272 fParent = braceParent;
2273 fInBrace = nullptr;
2274 }
2275 this->pushBracket(
2276 '(' == test ? Bracket::kParen :
2277 '[' == test ? Bracket::kSquare :
2278 '{' == test ? Bracket::kBrace :
2279 Bracket::kColon);
2280 if (!fInBrace
2281 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2282 && fInFunction) {
2283 fInBrace = fParent;
2284 }
2285 } break;
2286 case '<':
2287 if (fInCharCommentString || fInBrace) {
2288 break;
2289 }
2290 if (!this->checkForWord()) {
2291 return false;
2292 }
2293 if (fInEnum) {
2294 break;
2295 }
2296 this->pushBracket(Bracket::kAngle);
2297 break;
2298 case ')':
2299 case ']':
2300 case '}': {
2301 if (fInCharCommentString) {
2302 break;
2303 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002304 if (fInDefine && fInBrace) {
2305 break;
2306 }
Cary Clark8032b982017-07-28 11:04:54 -04002307 if (!fInBrace) {
2308 if (!this->checkForWord()) {
2309 return false;
2310 }
2311 }
2312 bool popBraceParent = fInBrace == fParent;
2313 if ((')' == test ? Bracket::kParen :
2314 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2315 this->popBracket();
2316 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002317 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002318 } else {
2319 fInFunction = '}' != test;
2320 }
Cary Clark73fa9722017-08-29 17:36:51 -04002321 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2322 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002323 } else {
2324 return reportError<bool>("malformed close bracket");
2325 }
2326 if (popBraceParent) {
2327 Definition* braceParent = fInBrace->fParent;
2328 braceParent->fChildren.pop_back();
2329 braceParent->fTokens.pop_back();
2330 fInBrace = nullptr;
2331 }
2332 } break;
2333 case '>':
2334 if (fInCharCommentString || fInBrace) {
2335 break;
2336 }
2337 if (!this->checkForWord()) {
2338 return false;
2339 }
2340 if (fInEnum) {
2341 break;
2342 }
Cary Clarka560c472017-11-27 10:44:06 -05002343 if (Bracket::kPound == this->topBracket()) {
2344 break;
2345 }
Cary Clark8032b982017-07-28 11:04:54 -04002346 if (Bracket::kAngle == this->topBracket()) {
2347 this->popBracket();
2348 } else {
2349 return reportError<bool>("malformed close angle bracket");
2350 }
2351 break;
2352 case '#': {
2353 if (fInCharCommentString || fInBrace) {
2354 break;
2355 }
2356 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2357 this->pushBracket(Bracket::kPound);
2358 break;
2359 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002360 case ' ':
2361 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2362 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2363 fInBrace = fParent;
2364 // delimiting brackets are space ... unescaped-linefeed
2365 }
Cary Clark8032b982017-07-28 11:04:54 -04002366 case '&':
2367 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002368 case '+':
2369 case '=':
2370 case '-':
2371 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002372 if (fInCharCommentString || fInBrace) {
2373 break;
2374 }
2375 if (!this->checkForWord()) {
2376 return false;
2377 }
2378 break;
2379 case ';':
2380 if (fInCharCommentString || fInBrace) {
2381 break;
2382 }
2383 if (!this->checkForWord()) {
2384 return false;
2385 }
2386 if (Definition::Type::kKeyWord == fParent->fType
2387 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002388 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2389 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002390 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2391 this->popObject();
2392 }
Cary Clark8032b982017-07-28 11:04:54 -04002393 if (KeyWord::kEnum == fParent->fKeyWord) {
2394 fInEnum = false;
2395 }
2396 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002397 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2398 this->popObject();
2399 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002400 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002401 } else if (Definition::Type::kBracket == fParent->fType
2402 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2403 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2404 list<Definition>::iterator baseIter = fParent->fTokens.end();
2405 list<Definition>::iterator namedIter = fParent->fTokens.end();
2406 for (auto tokenIter = fParent->fTokens.end();
2407 fParent->fTokens.begin() != tokenIter--; ) {
2408 if (tokenIter->fLineCount == fLineCount) {
2409 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2410 if (namedIter != fParent->fTokens.end()) {
2411 return reportError<bool>("found two named member tokens");
2412 }
2413 namedIter = tokenIter;
2414 }
2415 baseIter = tokenIter;
2416 } else {
2417 break;
2418 }
2419 }
2420 // FIXME: if a member definition spans multiple lines, this won't work
2421 if (namedIter != fParent->fTokens.end()) {
2422 if (baseIter == namedIter) {
2423 return this->reportError<bool>("expected type before named token");
2424 }
2425 Definition* member = &*namedIter;
2426 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002427 if (!member->fTerminator) {
2428 member->fTerminator = member->fContentEnd;
2429 }
Cary Clark8032b982017-07-28 11:04:54 -04002430 fParent->fChildren.push_back(member);
2431 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2432 member->fChildren.push_back(&*nameType);
2433 }
Cary Clark8032b982017-07-28 11:04:54 -04002434 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002435 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002436 } else if (fParent->fChildren.size() > 0) {
2437 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002438 Definition* priorEnum = fPriorEnum;
2439 fPriorEnum = nullptr;
2440 if (!priorEnum) {
2441 while (fParent->fChildren.begin() != lastIter) {
2442 std::advance(lastIter, -1);
2443 priorEnum = *lastIter;
2444 if (Definition::Type::kBracket != priorEnum->fType ||
2445 (Bracket::kSlashSlash != priorEnum->fBracket
2446 && Bracket::kSlashStar != priorEnum->fBracket)) {
2447 break;
2448 }
Cary Clark8032b982017-07-28 11:04:54 -04002449 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002450 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002451 }
2452 if (Definition::Type::kKeyWord == priorEnum->fType
2453 && KeyWord::kEnum == priorEnum->fKeyWord) {
2454 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002455 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002456 while (tokenWalker != fParent->fTokens.end()) {
2457 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002458 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002459 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2460 break;
2461 }
2462 }
2463 while (tokenWalker != fParent->fTokens.end()) {
2464 std::advance(tokenWalker, 1);
2465 const Definition* test = &*tokenWalker;
2466 if (Definition::Type::kBracket != test->fType ||
2467 (Bracket::kSlashSlash != test->fBracket
2468 && Bracket::kSlashStar != test->fBracket)) {
2469 break;
2470 }
2471 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002472 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002473 Definition* start = &*tokenWalker;
2474 bool foundExpected = true;
2475 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2476 const Definition* test = &*tokenWalker;
2477 if (expected != test->fKeyWord) {
2478 foundExpected = false;
2479 break;
2480 }
2481 if (tokenWalker == fParent->fTokens.end()) {
2482 break;
2483 }
2484 std::advance(tokenWalker, 1);
2485 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002486 if (!foundExpected) {
2487 foundExpected = true;
2488 tokenWalker = saveTokenWalker;
2489 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2490 const Definition* test = &*tokenWalker;
2491 if (expected != test->fKeyWord) {
2492 foundExpected = false;
2493 break;
2494 }
2495 if (tokenWalker == fParent->fTokens.end()) {
2496 break;
2497 }
2498 if (KeyWord::kNone != expected) {
2499 std::advance(tokenWalker, 1);
2500 }
2501 }
2502 if (foundExpected) {
2503 auto nameToken = priorEnum->fTokens.begin();
2504 string enumName = string(nameToken->fContentStart,
2505 nameToken->fContentEnd - nameToken->fContentStart);
2506 const Definition* test = &*tokenWalker;
2507 string constType = string(test->fContentStart,
2508 test->fContentEnd - test->fContentStart);
2509 if (enumName != constType) {
2510 foundExpected = false;
2511 } else {
2512 std::advance(tokenWalker, 1);
2513 }
2514 }
2515 }
Cary Clark8032b982017-07-28 11:04:54 -04002516 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2517 const char* nameStart = tokenWalker->fStart;
2518 std::advance(tokenWalker, 1);
2519 if (tokenWalker != fParent->fTokens.end()) {
2520 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002521 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002522 start->fName = string(nameStart, tp.fChar - nameStart);
2523 start->fContentEnd = fChar;
2524 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002525 fPriorEnum = priorEnum;
2526 }
Cary Clark8032b982017-07-28 11:04:54 -04002527 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002528 } else { // check for static constexpr not following an enum
2529 // find first token on line
2530 auto backTokenWalker = fParent->fTokens.end();
2531 while (fParent->fTokens.begin() != backTokenWalker
2532 && (fParent->fTokens.end() == backTokenWalker
2533 || backTokenWalker->fStart > fLine)) {
2534 std::advance(backTokenWalker, -1);
2535 }
2536 if (fParent->fTokens.end() != backTokenWalker
2537 && backTokenWalker->fStart < fLine) {
2538 std::advance(backTokenWalker, 1);
2539 }
2540 // look for static constexpr
2541 Definition* start = &*backTokenWalker;
2542 bool foundExpected = true;
2543 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr}){
2544 const Definition* test = &*backTokenWalker;
2545 if (expected != test->fKeyWord) {
2546 foundExpected = false;
2547 break;
2548 }
2549 if (backTokenWalker == fParent->fTokens.end()) {
2550 break;
2551 }
2552 std::advance(backTokenWalker, 1);
2553 }
2554 if (foundExpected) {
2555 std::advance(backTokenWalker, 1);
2556 const char* nameStart = backTokenWalker->fStart;
2557 std::advance(backTokenWalker, 1);
2558 TextParser parser(fFileName, nameStart, backTokenWalker->fStart, fLineCount);
2559 parser.skipToNonAlphaNum();
2560 start->fMarkType = MarkType::kConst;
2561 start->fName = string(nameStart, parser.fChar - nameStart);
2562 start->fContentEnd = backTokenWalker->fContentEnd;
2563 fParent->fChildren.emplace_back(start);
2564 }
Cary Clark8032b982017-07-28 11:04:54 -04002565 }
2566 }
2567 this->addPunctuation(Punctuation::kSemicolon);
2568 fInFunction = false;
2569 break;
2570 case '~':
2571 if (fInEnum) {
2572 break;
2573 }
2574 case '0': case '1': case '2': case '3': case '4':
2575 case '5': case '6': case '7': case '8': case '9':
2576 // TODO: don't want to parse numbers, but do need to track for enum defs
2577 // break;
2578 case 'A': case 'B': case 'C': case 'D': case 'E':
2579 case 'F': case 'G': case 'H': case 'I': case 'J':
2580 case 'K': case 'L': case 'M': case 'N': case 'O':
2581 case 'P': case 'Q': case 'R': case 'S': case 'T':
2582 case 'U': case 'V': case 'W': case 'X': case 'Y':
2583 case 'Z': case '_':
2584 case 'a': case 'b': case 'c': case 'd': case 'e':
2585 case 'f': case 'g': case 'h': case 'i': case 'j':
2586 case 'k': case 'l': case 'm': case 'n': case 'o':
2587 case 'p': case 'q': case 'r': case 's': case 't':
2588 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002589 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002590 if (fInCharCommentString || fInBrace) {
2591 break;
2592 }
2593 if (!fIncludeWord) {
2594 fIncludeWord = fChar;
2595 }
2596 break;
2597 }
2598done:
2599 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002600 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002601 return true;
2602}
2603
2604void IncludeParser::validate() const {
2605 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2606 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2607 }
2608 IncludeParser::ValidateKeyWords();
2609}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002610
Cary Clark186d08f2018-04-03 08:43:27 -04002611bool IncludeParser::references(const SkString& file) const {
2612 // if includes weren't passed one at a time, assume all references are valid
2613 if (fIncludeMap.empty()) {
2614 return true;
2615 }
2616 SkASSERT(file.endsWith(".bmh") );
2617 string root(file.c_str(), file.size() - 4);
2618 string kReference("_Reference");
2619 if (string::npos != root.find(kReference)) {
2620 root = root.substr(0, root.length() - kReference.length());
2621 }
2622 if (fIClassMap.end() != fIClassMap.find(root)) {
2623 return true;
2624 }
2625 if (fIStructMap.end() != fIStructMap.find(root)) {
2626 return true;
2627 }
2628 // TODO incomplete: probably need to look in other places for class-less includes like SkColor.h
2629 return false;
2630}
2631
Cary Clark2dc84ad2018-01-26 12:56:22 -05002632void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2633 if (!sk_isdir(includes)) {
2634 IncludeParser::RemoveOneFile(docs, includes);
2635 } else {
2636 SkOSFile::Iter it(includes, ".h");
2637 for (SkString file; it.next(&file); ) {
2638 SkString p = SkOSPath::Join(includes, file.c_str());
2639 const char* hunk = p.c_str();
2640 if (!SkStrEndsWith(hunk, ".h")) {
2641 continue;
2642 }
2643 IncludeParser::RemoveOneFile(docs, hunk);
2644 }
2645 }
2646}
2647
2648void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2649 const char* lastForward = strrchr(includesFile, '/');
2650 const char* lastBackward = strrchr(includesFile, '\\');
2651 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2652 if (!last) {
2653 last = includesFile;
2654 } else {
2655 last += 1;
2656 }
2657 SkString baseName(last);
2658 SkASSERT(baseName.endsWith(".h"));
2659 baseName.remove(baseName.size() - 2, 2);
2660 baseName.append("_Reference.bmh");
2661 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2662 remove(fullName.c_str());
2663}