blob: 982f7b3a789d01d8a8217557f974b3dfd1e795a3 [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 Clark8032b982017-07-28 11:04:54 -0400469 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400470 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400471 break;
472 }
473 }
474 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500475 int crossChecks = 0;
476 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400477 for (auto& classMapper : fIClassMap) {
478 string className = classMapper.first;
479 auto finder = bmhParser.fClassMap.find(className);
480 if (bmhParser.fClassMap.end() == finder) {
481 continue;
482 }
483 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500484 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500485 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400486 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500487 if (crossChecks) {
488 SkDebugf(".");
489 } else {
490 SkDebugf("cross-check");
491 firstCheck = className;
492 }
493 ++crossChecks;
494 }
495 if (crossChecks) {
496 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500497 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500498 }
499 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400500 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400501 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500502 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400503}
504
505IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400506 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400507 string className;
508 const Definition* test = fParent;
509 while (Definition::Type::kFileType != test->fType) {
510 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
511 className = test->fName + "::";
512 break;
513 }
514 test = test->fParent;
515 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400516 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400517 unordered_map<string, IClassDefinition>& map = fIClassMap;
518 IClassDefinition& markupDef = map[className];
519 if (markupDef.fStart) {
520 typedef IClassDefinition* IClassDefPtr;
521 return INHERITED::reportError<IClassDefPtr>("class already defined");
522 }
523 markupDef.fFileName = fFileName;
524 markupDef.fStart = includeDef.fStart;
525 markupDef.fContentStart = includeDef.fStart;
526 markupDef.fName = className;
527 markupDef.fContentEnd = includeDef.fContentEnd;
528 markupDef.fTerminator = includeDef.fTerminator;
529 markupDef.fParent = fParent;
530 markupDef.fLineCount = fLineCount;
531 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
532 MarkType::kStruct : MarkType::kClass;
533 markupDef.fKeyWord = includeDef.fKeyWord;
534 markupDef.fType = Definition::Type::kMark;
535 fParent = &markupDef;
536 return &markupDef;
537}
538
539void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
540 auto& tokens = classDef.fTokens;
541 for (auto& token : tokens) {
542 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
543 continue;
544 }
545 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400546 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400547 }
548 switch (token.fMarkType) {
549 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500550 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500551 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400552 break;
553 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400554 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400555 break;
556 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400557 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400558 continue;
559 break;
560 default:
561 SkASSERT(0);
562 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400563 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400564 }
565}
Cary Clark9174bda2017-09-19 17:39:32 -0400566void IncludeParser::dumpComment(const Definition& token) {
567 fLineCount = token.fLineCount;
568 fChar = fLine = token.fContentStart;
569 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400570 bool sawParam = false;
571 bool multiline = false;
572 bool sawReturn = false;
573 bool sawComment = false;
574 bool methodHasReturn = false;
575 vector<string> methodParams;
576 vector<string> foundParams;
577 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400578 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
579 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500580 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400581 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500582 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400583 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400584 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500585 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400586 && !methodParser.strnchr('~', methodParser.fEnd);
587 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
588 const char* nextEnd = paren;
589 do {
590 string paramName;
591 methodParser.fChar = nextEnd + 1;
592 methodParser.skipSpace();
593 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
594 continue;
595 }
596 methodParams.push_back(paramName);
597 } while (')' != nextEnd[0]);
598 }
Cary Clark9174bda2017-09-19 17:39:32 -0400599 for (const auto& child : token.fTokens) {
600 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
601 break;
602 }
Cary Clark8032b982017-07-28 11:04:54 -0400603 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400604 if (child.fPrivate) {
605 break;
606 }
Cary Clark8032b982017-07-28 11:04:54 -0400607 if ('@' == child.fContentStart[0]) {
608 TextParser parser(&child);
609 do {
610 parser.next();
611 if (parser.startsWith("param ")) {
612 parser.skipWord("param");
613 const char* parmStart = parser.fChar;
614 parser.skipToSpace();
615 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
616 parser.skipWhiteSpace();
617 do {
618 size_t nextComma = parmName.find(',');
619 string piece;
620 if (string::npos == nextComma) {
621 piece = parmName;
622 parmName = "";
623 } else {
624 piece = parmName.substr(0, nextComma);
625 parmName = parmName.substr(nextComma + 1);
626 }
627 if (sawParam) {
628 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400629 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400630 }
Cary Clark9174bda2017-09-19 17:39:32 -0400631 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400632 } else {
633 if (sawComment) {
634 this->nl();
635 }
636 this->lf(2);
637 }
638 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400639 this->writeTag("Param", piece);
640 this->writeSpace(2);
641 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
642 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400643 sawParam = true;
644 sawComment = false;
645 } while (parmName.length());
646 parser.skipTo(parser.fEnd);
647 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
648 parser.skipWord("return");
649 if ('s' == parser.peek()) {
650 parser.next();
651 }
652 if (sawParam) {
653 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400654 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400655 }
Cary Clark9174bda2017-09-19 17:39:32 -0400656 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400657 }
658 this->checkForMissingParams(methodParams, foundParams);
659 sawParam = false;
660 sawComment = false;
661 multiline = false;
662 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400663 this->writeTag("Return");
664 this->writeSpace(2);
665 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
666 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400667 sawReturn = true;
668 parser.skipTo(parser.fEnd);
669 } else {
670 this->reportError("unexpected doxygen directive");
671 }
672 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400673 } else if (child.length() > 1) {
674 const char* start = child.fContentStart;
675 ptrdiff_t length = child.fContentEnd - start;
676 SkASSERT(length >= 0);
677 while (length && '/' == start[0]) {
678 start += 1;
679 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400680 }
Cary Clark9174bda2017-09-19 17:39:32 -0400681 while (length && '/' == start[length - 1]) {
682 length -= 1;
683 if (length && '*' == start[length - 1]) {
684 length -= 1;
685 }
686 }
687 if (length) {
688 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
689 if (sawParam || sawReturn) {
690 this->indentToColumn(8);
691 }
692 this->writeBlock(length, start);
693 this->writeSpace();
694 sawComment = true;
695 if (sawParam || sawReturn) {
696 multiline = true;
697 }
Cary Clark8032b982017-07-28 11:04:54 -0400698 }
699 }
700 }
701 }
702 if (sawParam || sawReturn) {
703 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400704 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400705 }
Cary Clark9174bda2017-09-19 17:39:32 -0400706 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400707 }
708 if (!sawReturn) {
709 if (!sawParam) {
710 if (sawComment) {
711 this->nl();
712 }
713 this->lf(2);
714 }
715 this->checkForMissingParams(methodParams, foundParams);
716 }
717 if (methodHasReturn != sawReturn) {
718 if (!methodHasReturn) {
719 this->reportError("unexpected doxygen return");
720 } else {
721 if (sawComment) {
722 this->nl();
723 }
724 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400725 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400726 }
727 }
728}
729
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400730void IncludeParser::dumpCommonTail(const Definition& token) {
731 this->lf(2);
732 this->writeTag("Example");
733 this->lf(1);
734 this->writeString("// incomplete");
735 this->lf(1);
736 this->writeEndTag();
737 this->lf(2);
738 this->writeTag("SeeAlso");
739 this->writeSpace();
740 this->writeString("incomplete");
741 this->lf(2);
742 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
743 this->lf(2);
744}
745
746void IncludeParser::dumpDefine(const Definition& token) {
747 this->writeTag("Define", token.fName);
748 this->lf(2);
749 this->writeTag("Code");
750 this->lfAlways(1);
751 this->writeString("###$");
752 this->lfAlways(1);
753 this->indentToColumn(4);
754 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
755 this->lf(1);
756 this->indentToColumn(0);
757 this->writeString("$$$#");
758
759 this->writeEndTag();
760 this->lf(2);
761 this->dumpComment(token);
762 for (auto& child : token.fTokens) {
763 if (MarkType::kComment == child.fMarkType) {
764 continue;
765 }
766 this->writeTag("Param", child.fName);
767 this->writeSpace();
768 this->writeString("incomplete");
769 this->writeSpace();
770 this->writeString("##");
771 this->lf(1);
772 }
773}
774
775void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500776 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400777 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400778 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400779 this->lfAlways(1);
780 this->indentToColumn(4);
781 this->writeString("enum");
782 this->writeSpace();
783 if ("_anonymous" != token.fName.substr(0, 10)) {
784 this->writeString(token.fName);
785 this->writeSpace();
786 }
787 this->writeString("{");
788 this->lfAlways(1);
789 for (auto& child : token.fChildren) {
790 this->indentToColumn(8);
791 this->writeString(child->fName);
792 if (child->length()) {
793 this->writeSpace();
794 this->writeBlock(child->length(), child->fContentStart);
795 }
796 if (',' != fLastChar) {
797 this->writeString(",");
798 }
799 this->lfAlways(1);
800 }
801 this->indentToColumn(4);
802 this->writeString("};");
803 this->lf(1);
804 this->writeString("##");
805 this->lf(2);
806 this->dumpComment(token);
807 for (auto& child : token.fChildren) {
808 // start here;
809 // get comments before
810 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400811 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400812 this->writeSpace();
813 this->writeString(child->fName);
814 TextParser val(child);
815 if (!val.eof()) {
816 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
817 val.next();
818 val.skipSpace();
819 const char* valEnd = val.anyOf(",\n");
820 if (!valEnd) {
821 valEnd = val.fEnd;
822 }
823 this->writeSpace();
824 this->writeBlock(valEnd - val.fStart, val.fStart);
825 } else {
826 this->writeSpace();
827 this->writeDefinition(*child);
828 }
829 }
830 this->lf(1);
831 for (auto comment : child->fChildren) {
832 if (MarkType::kComment == comment->fMarkType) {
833 TextParser parser(comment);
834 parser.skipExact("*");
835 parser.skipExact("*");
836 while (!parser.eof() && parser.skipWhiteSpace()) {
837 parser.skipExact("*");
838 parser.skipWhiteSpace();
839 const char* start = parser.fChar;
840 parser.skipToEndBracket('\n');
841 this->lf(1);
842 this->writeBlock(parser.fChar - start, start);
843 }
844 }
845 }
846 this->writeEndTag();
847 }
848 this->lf(2);
849}
850
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400851bool IncludeParser::dumpGlobals() {
852 size_t lastBSlash = fFileName.rfind('\\');
853 size_t lastSlash = fFileName.rfind('/');
854 size_t lastDotH = fFileName.rfind(".h");
855 SkASSERT(string::npos != lastDotH);
856 if (string::npos != lastBSlash && (string::npos == lastSlash
857 || lastBSlash < lastSlash)) {
858 lastSlash = lastBSlash;
859 } else if (string::npos == lastSlash) {
860 lastSlash = -1;
861 }
862 lastSlash += 1;
863 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
864 string fileName = globalsName + "_Reference.bmh";
865 fOut = fopen(fileName.c_str(), "wb");
866 if (!fOut) {
867 SkDebugf("could not open output file %s\n", globalsName.c_str());
868 return false;
869 }
870 string prefixName = globalsName.substr(0, 2);
871 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
872 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
873 this->writeTagNoLF("Topic", topicName);
874 this->writeTag("Alias", topicName + "_Reference");
875 this->lf(2);
876 this->writeTag("Subtopic", "Overview");
877 fIndent += 4;
878 this->writeTag("Subtopic", "Subtopic");
879 fIndent += 4;
880 this->writeTag("Populate");
881 fIndent -= 4;
882 this->writeEndTag();
883 fIndent -= 4;
884 this->writeEndTag();
885 this->lf(2);
886 if (!fIDefineMap.empty()) {
887 this->writeTag("Subtopic", "Define");
888 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400889 this->writeEndTag();
890 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400891 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400892 if (!fIFunctionMap.empty()) {
893 this->writeTag("Subtopic", "Function");
894 this->writeTag("Populate");
895 this->writeEndTag();
896 this->lf(2);
897 }
898 if (!fIEnumMap.empty()) {
899 this->writeTag("Subtopic", "Enum");
900 this->writeTag("Populate");
901 this->writeEndTag();
902 this->lf(2);
903 }
904 if (!fITemplateMap.empty()) {
905 this->writeTag("Subtopic", "Template");
906 this->writeTag("Populate");
907 this->writeEndTag();
908 this->lf(2);
909 }
910 if (!fITypedefMap.empty()) {
911 this->writeTag("Subtopic", "Typedef");
912 this->writeTag("Populate");
913 this->writeEndTag();
914 this->lf(2);
915 }
916 if (!fIUnionMap.empty()) {
917 this->writeTag("Subtopic", "Union");
918 this->writeTag("Populate");
919 this->writeEndTag();
920 this->lf(2);
921 }
922 std::map<int, Definition*> sortedDefs;
923 for (const auto& entry : fIDefineMap) {
924 sortedDefs[entry.second->fLineCount] = entry.second;
925 }
926 for (const auto& entry : fIFunctionMap) {
927 sortedDefs[entry.second->fLineCount] = entry.second;
928 }
929 for (const auto& entry : fIEnumMap) {
930 sortedDefs[entry.second->fLineCount] = entry.second;
931 }
932 for (const auto& entry : fITemplateMap) {
933 sortedDefs[entry.second->fLineCount] = entry.second;
934 }
935 for (const auto& entry : fITypedefMap) {
936 sortedDefs[entry.second->fLineCount] = entry.second;
937 }
938 for (const auto& entry : fIUnionMap) {
939 sortedDefs[entry.second->fLineCount] = entry.second;
940 }
941 for (const auto& entry : sortedDefs) {
942 const Definition* def = entry.second;
943 this->writeBlockSeparator();
944 switch (def->fMarkType) {
945 case MarkType::kDefine:
946 this->dumpDefine(*def);
947 break;
948 case MarkType::kMethod:
949 this->dumpMethod(*def, globalsName);
950 break;
951 case MarkType::kEnum:
952 case MarkType::kEnumClass:
953 this->dumpEnum(*def, globalsName);
954 break;
955 case MarkType::kTemplate:
956 SkASSERT(0); // incomplete
957 break;
958 case MarkType::kTypedef: {
959 this->writeTag("Typedef");
960 this->writeSpace();
961 TextParser parser(def);
962 if (!parser.skipExact("typedef")) {
963 return false;
964 }
965 if (!parser.skipSpace()) {
966 return false;
967 }
968 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
969 this->lf(2);
970 this->dumpComment(*def);
971 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
972 this->lf(2);
973 } continue;
974 case MarkType::kUnion:
975 SkASSERT(0); // incomplete
976 break;
977 default:
978 SkASSERT(0);
979 }
980 this->dumpCommonTail(*def);
981 }
982 this->writeEndTag("Topic", topicName);
983 this->lfAlways(1);
984 fclose(fOut);
985 SkDebugf("wrote %s\n", fileName.c_str());
986 return true;
987}
988
989bool IncludeParser::isClone(const Definition& token) {
990 string name = token.fName;
991 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
992}
993
994bool IncludeParser::isConstructor(const Definition& token, string className) {
995 string name = token.fName;
996 return 0 == name.find(className) || '~' == name[0];
997}
998
999bool IncludeParser::isInternalName(const Definition& token) {
1000 string name = token.fName;
1001 // exception for this SkCanvas function .. for now
1002 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1003 return false;
1004 }
1005 return name.substr(0, 7) == "android"
1006 || 0 == token.fName.find("internal_")
1007 || 0 == token.fName.find("Internal_")
1008 || 0 == token.fName.find("legacy_")
1009 || 0 == token.fName.find("temporary_")
1010 || 0 == token.fName.find("private_");
1011}
1012
1013bool IncludeParser::isOperator(const Definition& token) {
1014 return "operator" == token.fName.substr(0, 8);
1015}
1016
1017void IncludeParser::dumpMethod(const Definition& token, string className) {
1018 this->writeTag("Method");
1019 this->writeSpace();
1020
1021 string name = string(token.fStart ? token.fStart : token.fContentStart,
1022 token.length());
1023 if (this->isOperator(token)) {
1024 string spaceConst(" const");
1025 size_t constPos = name.rfind(spaceConst);
1026 if (name.length() - spaceConst.length() == constPos) {
1027 name = name.substr(0, constPos) + "_const";
1028 }
1029 }
1030 this->writeString(name);
1031 string inType;
1032 if (this->isConstructor(token, className)) {
1033 inType = "Constructor";
1034 } else if (this->isOperator(token)) {
1035 inType = "Operator";
1036 } else {
1037 inType = "incomplete";
1038 }
1039 this->writeTag("In", inType);
1040 this->writeTag("Line");
1041 this->writeSpace(1);
1042 this->writeString("#");
1043 this->writeSpace(1);
1044 this->writeString("incomplete");
1045 this->writeSpace(1);
1046 this->writeString("##");
1047 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001048 this->dumpComment(token);
1049}
1050
1051void IncludeParser::dumpMember(const Definition& token) {
1052 this->writeTag("Member");
1053 this->writeSpace();
1054 this->writeDefinition(token, token.fName, 2);
1055 lf(1);
1056 for (auto child : token.fChildren) {
1057 this->writeDefinition(*child);
1058 }
1059 this->writeEndTag();
1060 lf(2);
1061}
1062
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001063bool IncludeParser::dumpTokens() {
1064 if (!this->dumpGlobals()) {
1065 return false;
1066 }
Cary Clark9174bda2017-09-19 17:39:32 -04001067 for (const auto& member : fIClassMap) {
1068 if (string::npos != member.first.find("::")) {
1069 continue;
1070 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001071 if (!this->dumpTokens(member.first)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001072 return false;
1073 }
1074 }
1075 return true;
1076}
1077
Ben Wagner63fd7602017-10-09 15:45:33 -04001078 // dump equivalent markup
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001079bool IncludeParser::dumpTokens(string skClassName) {
1080 string fileName = skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -04001081 fOut = fopen(fileName.c_str(), "wb");
1082 if (!fOut) {
1083 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -04001084 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001085 }
1086 string prefixName = skClassName.substr(0, 2);
1087 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1088 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -04001089 this->writeTagNoLF("Topic", topicName);
1090 this->writeTag("Alias", topicName + "_Reference");
1091 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001092 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001093 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1094 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001095 this->writeTag(containerType, skClassName);
1096 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001097 auto& tokens = classMap.fTokens;
1098 for (auto& token : tokens) {
1099 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1100 continue;
1101 }
Cary Clark9174bda2017-09-19 17:39:32 -04001102 this->writeDefinition(token);
1103 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001104 }
1105 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001106 bool hasClass = false;
1107 bool hasConst = !fIEnumMap.empty();
1108 bool hasConstructor = false;
1109 bool hasMember = false;
1110 bool hasOperator = false;
Cary Clark8032b982017-07-28 11:04:54 -04001111 for (const auto& oneClass : fIClassMap) {
1112 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1113 continue;
1114 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001115 hasClass = true;
1116 break;
Cary Clark8032b982017-07-28 11:04:54 -04001117 }
Cary Clark8032b982017-07-28 11:04:54 -04001118 for (const auto& token : classMap.fTokens) {
1119 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1120 continue;
1121 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001122 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001123 continue;
1124 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001125 if (this->isConstructor(token, skClassName)) {
1126 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001127 continue;
1128 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001129 if (this->isOperator(token)) {
1130 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001131 continue;
1132 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001133 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001134 continue;
1135 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001136 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001137 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001138 this->writeTag("Subtopic", "Overview");
1139 fIndent += 4;
1140 this->writeTag("Subtopic", "Subtopic");
1141 fIndent += 4;
1142 this->writeTag("Populate");
1143 fIndent -= 4;
1144 this->writeEndTag();
1145 fIndent -= 4;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001146 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001147 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001148
1149 if (hasClass) {
1150 this->writeTag("Subtopic", "Class_or_Struct");
1151 this->writeTag("Populate");
1152 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001153 this->lf(2);
1154 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001155 if (hasConst) {
1156 this->writeTag("Subtopic", "Constant");
1157 this->writeTag("Populate");
1158 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001159 this->lf(2);
1160 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001161 if (hasConstructor) {
1162 this->writeTag("Subtopic", "Constructor");
1163 this->writeTag("Populate");
1164 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001165 this->lf(2);
1166 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001167 if (hasOperator) {
1168 this->writeTag("Subtopic", "Operator");
1169 this->writeTag("Populate");
1170 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001171 this->lf(2);
1172 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001173 if (hasMember) {
1174 this->writeTag("Subtopic", "Member_Function");
1175 this->writeTag("Populate");
1176 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001177 this->lf(2);
1178 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001179 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001180 this->writeBlockSeparator();
1181 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001182 this->lf(2);
1183 this->writeTag("Example");
1184 this->lfcr();
1185 this->writeString("// incomplete");
1186 this->writeEndTag();
1187 this->lf(2);
1188 this->writeTag("SeeAlso", "incomplete");
1189 this->lf(2);
1190 this->writeEndTag("Enum", oneEnum.first);
1191 this->lf(2);
1192 }
Cary Clark8032b982017-07-28 11:04:54 -04001193 for (auto& oneClass : fIClassMap) {
1194 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1195 continue;
1196 }
1197 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001198 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001199 KeyWord keyword = oneClass.second.fKeyWord;
1200 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1201 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001202 this->writeTag(containerType, innerName);
1203 this->lf(2);
1204 this->writeTag("Code");
1205 this->writeEndTag("ToDo", "fill this in manually");
1206 this->writeEndTag();
1207 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001208 for (auto& token : oneClass.second.fTokens) {
1209 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1210 continue;
1211 }
Cary Clark9174bda2017-09-19 17:39:32 -04001212 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001213 }
1214 this->lf(2);
1215 this->dumpClassTokens(oneClass.second);
1216 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001217 this->writeEndTag(containerType, innerName);
1218 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001219 }
1220 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001221 this->writeEndTag(containerType, skClassName);
1222 this->lf(2);
1223 this->writeEndTag("Topic", topicName);
1224 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001225 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001226 SkDebugf("wrote %s\n", fileName.c_str());
1227 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001228}
1229
1230bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1231 // add comment preceding class, if any
1232 const Definition* parent = includeDef.fParent;
1233 int index = includeDef.fParentIndex;
1234 auto wordIter = parent->fTokens.begin();
1235 std::advance(wordIter, index);
1236 SkASSERT(&*wordIter == &includeDef);
1237 while (parent->fTokens.begin() != wordIter) {
1238 auto testIter = std::prev(wordIter);
1239 if (Definition::Type::kWord != testIter->fType
1240 && Definition::Type::kKeyWord != testIter->fType
1241 && (Definition::Type::kBracket != testIter->fType
1242 || Bracket::kAngle != testIter->fBracket)
1243 && (Definition::Type::kPunctuation != testIter->fType
1244 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1245 break;
1246 }
1247 wordIter = testIter;
1248 }
1249 auto commentIter = wordIter;
1250 while (parent->fTokens.begin() != commentIter) {
1251 auto testIter = std::prev(commentIter);
1252 bool isComment = Definition::Type::kBracket == testIter->fType
1253 && (Bracket::kSlashSlash == testIter->fBracket
1254 || Bracket::kSlashStar == testIter->fBracket);
1255 if (!isComment) {
1256 break;
1257 }
1258 commentIter = testIter;
1259 }
1260 while (commentIter != wordIter) {
1261 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1262 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1263 return false;
1264 }
1265 commentIter = std::next(commentIter);
1266 }
1267 return true;
1268}
1269
1270// caller calls reportError, so just return false here
1271bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1272 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001273 // parse class header
1274 auto iter = includeDef->fTokens.begin();
1275 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1276 // todo : documentation is ignoring this for now
1277 iter = std::next(iter);
1278 }
1279 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1280 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001281 iter = std::next(iter);
1282 if (iter == includeDef->fTokens.end()) {
1283 return true; // forward declaration only
1284 }
Cary Clark8032b982017-07-28 11:04:54 -04001285 do {
1286 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001287 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001288 }
1289 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1290 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001291 }
Cary Clark8032b982017-07-28 11:04:54 -04001292 } while (static_cast<void>(iter = std::next(iter)), true);
1293 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001294 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001295 }
1296 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1297 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001298 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001299 }
1300 markupDef->fStart = iter->fStart;
1301 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001302 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001303 }
1304// if (1 != includeDef->fChildren.size()) {
1305// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1306// }
1307 includeDef = includeDef->fChildren.front();
1308 iter = includeDef->fTokens.begin();
1309 // skip until public
1310 int publicIndex = 0;
1311 if (IsStruct::kNo == isStruct) {
1312 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1313 size_t publicLen = strlen(publicName);
1314 while (iter != includeDef->fTokens.end()
1315 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1316 || strncmp(iter->fStart, publicName, publicLen))) {
1317 iter = std::next(iter);
1318 ++publicIndex;
1319 }
1320 }
1321 auto childIter = includeDef->fChildren.begin();
1322 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1323 (*childIter)->fPrivate = true;
1324 childIter = std::next(childIter);
1325 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001326 int keyIndex = publicIndex;
1327 KeyWord currentKey = KeyWord::kPublic;
1328 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1329 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001330 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1331 size_t protectedLen = strlen(protectedName);
1332 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1333 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001334 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001335 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001336 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1337 const char* testStart = iter->fStart;
1338 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1339 iter = std::next(iter);
1340 ++keyIndex;
1341 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1342 currentKey = KeyWord::kPublic;
1343 break;
1344 }
1345 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1346 currentKey = KeyWord::kProtected;
1347 break;
1348 }
1349 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1350 currentKey = KeyWord::kPrivate;
1351 break;
1352 }
1353 }
1354 fLastObject = nullptr;
1355 if (KeyWord::kPublic == currentKey) {
1356 if (!this->parseObject(child, markupDef)) {
1357 return false;
1358 }
1359 } else {
1360 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001361 }
Cary Clark73fa9722017-08-29 17:36:51 -04001362 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001363 childIter = std::next(childIter);
1364 }
Cary Clark8032b982017-07-28 11:04:54 -04001365 SkASSERT(fParent->fParent);
1366 fParent = fParent->fParent;
1367 return true;
1368}
1369
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001370bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001371 int lineCount, Definition* markupDef) {
1372 TextParser parser(filename, start, end, lineCount);
1373 // parse doxygen if present
1374 if (parser.startsWith("**")) {
1375 parser.next();
1376 parser.next();
1377 parser.skipWhiteSpace();
1378 if ('\\' == parser.peek()) {
1379 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001380 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1381 if (parser.skipExact("file")) {
1382 if (Definition::Type::kFileType != fParent->fType) {
1383 return reportError<bool>("expected parent is file");
1384 }
1385 string filename = markupDef->fileName();
1386 if (!parser.skipWord(filename.c_str())) {
1387 return reportError<bool>("missing object type");
1388 }
1389 } else if (parser.skipExact("fn")) {
1390 SkASSERT(0); // incomplete
1391 } else {
1392 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1393 return reportError<bool>("missing object type");
1394 }
1395 if (!parser.skipWord(markupDef->fName.c_str()) &&
1396 KeyWord::kEnum != markupDef->fKeyWord) {
1397 return reportError<bool>("missing object name");
1398 }
Cary Clark8032b982017-07-28 11:04:54 -04001399 }
Cary Clark8032b982017-07-28 11:04:54 -04001400 }
1401 }
1402 // remove leading '*' if present
1403 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1404 while (!parser.eof() && parser.skipWhiteSpace()) {
1405 while ('*' == parser.peek()) {
1406 parser.next();
1407 if (parser.eof()) {
1408 break;
1409 }
1410 parser.skipWhiteSpace();
1411 }
1412 if (parser.eof()) {
1413 break;
1414 }
1415 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001416 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001417 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001418 parser.skipToEndBracket('\n');
1419 }
1420 return true;
1421}
1422
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001423bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1424 TextParser parser(child);
1425 if (!parser.skipExact("#define")) {
1426 return false;
1427 }
1428 if (!parser.skipSpace()) {
1429 return false;
1430 }
1431 const char* nameStart = parser.fChar;
1432 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1433 if (parser.eof()) {
1434 return true; // do nothing if #define doesn't define anything
1435 }
1436 string nameStr(nameStart, parser.fChar - nameStart);
1437 struct Param {
1438 const char* fStart;
1439 const char* fEnd;
1440 };
1441 vector<Param> params;
1442 if ('(' == parser.peek()) {
1443 parser.next();
1444 if (!parser.skipSpace()) {
1445 return false;
1446 }
1447 do {
1448 const char* paramStart = parser.fChar;
1449 if (!parser.skipExact("...")) {
1450 parser.skipToNonAlphaNum();
1451 }
1452 if (parser.eof()) {
1453 return false;
1454 }
1455 params.push_back({paramStart, parser.fChar});
1456 if (!parser.skipSpace()) {
1457 return false;
1458 }
1459 if (')' == parser.peek()) {
1460 parser.next();
1461 break;
1462 }
1463 if (',' != parser.next()) {
1464 return false;
1465 }
1466 if (!parser.skipSpace()) {
1467 return false;
1468 }
1469 } while (true);
1470 }
1471 if (!parser.skipSpace()) {
1472 return false;
1473 }
1474 if (!markupDef) {
1475 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1476 child->fLineCount, fParent, '\0');
1477 Definition* globalMarkupChild = &fGlobals.back();
1478 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1479 globalMarkupChild->fName = globalUniqueName;
1480 globalMarkupChild->fTerminator = child->fContentEnd;
1481 if (!this->findComments(*child, globalMarkupChild)) {
1482 return false;
1483 }
1484 fIDefineMap[globalUniqueName] = globalMarkupChild;
1485 for (Param param : params) {
1486 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1487 child->fLineCount, globalMarkupChild, '\0');
1488 Definition* paramChild = &globalMarkupChild->fTokens.back();
1489 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1490 paramChild->fTerminator = param.fEnd;
1491 }
1492 return true;
1493 }
1494 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1495 child->fLineCount, markupDef, '\0');
1496 Definition* markupChild = &markupDef->fTokens.back();
1497 markupChild->fName = nameStr;
1498 markupChild->fTerminator = markupChild->fContentEnd;
1499 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1500 if (!this->findComments(*child, markupChild)) {
1501 return false;
1502 }
1503 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001504 return true;
1505}
1506
1507bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001508 TextParser parser(child);
1509 parser.skipToEndBracket('{');
1510 if (parser.eof()) {
1511 return true; // if enum is a forward declaration, do nothing
1512 }
1513 parser.next();
1514 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001515 if (child->fTokens.size() > 0) {
1516 auto token = child->fTokens.begin();
1517 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1518 token = token->fTokens.begin();
1519 }
1520 if (Definition::Type::kWord == token->fType) {
1521 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1522 }
1523 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001524 Definition* markupChild;
1525 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001526 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1527 child->fLineCount, fParent, '\0');
1528 markupChild = &fGlobals.back();
1529 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1530 markupChild->fName = globalUniqueName;
1531 markupChild->fTerminator = child->fContentEnd;
1532 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001533 } else {
1534 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001535 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001536 markupChild = &markupDef->fTokens.back();
1537 }
Cary Clark8032b982017-07-28 11:04:54 -04001538 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1539 markupChild->fKeyWord = KeyWord::kEnum;
1540 TextParser enumName(child);
1541 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001542 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001543 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001544 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001545 markupChild->fMarkType = MarkType::kEnumClass;
1546 }
Cary Clark8032b982017-07-28 11:04:54 -04001547 const char* nameStart = enumName.fChar;
1548 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001549 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001550 markupChild->fName = markupDef->fName + "::" +
1551 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001552 }
Cary Clark8032b982017-07-28 11:04:54 -04001553 if (!this->findComments(*child, markupChild)) {
1554 return false;
1555 }
Cary Clark8032b982017-07-28 11:04:54 -04001556 const char* dataEnd;
1557 do {
Cary Clark8032b982017-07-28 11:04:54 -04001558 parser.skipWhiteSpace();
1559 if ('}' == parser.peek()) {
1560 break;
1561 }
1562 Definition* comment = nullptr;
1563 // note that comment, if any, can be before or after (on the same line, though) as member
1564 if ('#' == parser.peek()) {
1565 // fixme: handle preprecessor, but just skip it for now
1566 parser.skipToLineStart();
1567 }
1568 while (parser.startsWith("/*") || parser.startsWith("//")) {
1569 parser.next();
1570 const char* start = parser.fChar;
1571 const char* end;
1572 if ('*' == parser.peek()) {
1573 end = parser.strnstr("*/", parser.fEnd);
1574 parser.fChar = end;
1575 parser.next();
1576 parser.next();
1577 } else {
1578 end = parser.trimmedLineEnd();
1579 parser.skipToLineStart();
1580 }
1581 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001582 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001583 comment = &markupChild->fTokens.back();
1584 comment->fTerminator = end;
1585 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1586 return false;
1587 }
1588 parser.skipWhiteSpace();
1589 }
1590 parser.skipWhiteSpace();
1591 const char* memberStart = parser.fChar;
1592 if ('}' == memberStart[0]) {
1593 break;
1594 }
Cary Clark9174bda2017-09-19 17:39:32 -04001595 // if there's comment on same the line as member def, output first as if it was before
1596
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001597 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001598 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001599 if (parser.eof() || !parser.skipWhiteSpace()) {
1600 return this->reportError<bool>("enum member must end with comma 1");
1601 }
Cary Clark8032b982017-07-28 11:04:54 -04001602 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001603 if ('=' == parser.peek()) {
1604 parser.skipToEndBracket(',');
1605 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001606 if (!parser.eof() && '#' == parser.peek()) {
1607 // fixme: handle preprecessor, but just skip it for now
1608 continue;
1609 }
Cary Clark9174bda2017-09-19 17:39:32 -04001610 if (parser.eof() || ',' != parser.peek()) {
1611 return this->reportError<bool>("enum member must end with comma 2");
1612 }
1613 dataEnd = parser.fChar;
1614 const char* start = parser.anyOf("/\n");
1615 SkASSERT(start);
1616 parser.skipTo(start);
1617 if ('/' == parser.next()) {
1618 char slashStar = parser.next();
1619 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001620 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001621 char doxCheck = parser.next();
1622 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1623 save.restore();
1624 }
1625 }
1626 parser.skipWhiteSpace();
1627 const char* commentStart = parser.fChar;
1628 if ('/' == slashStar) {
1629 parser.skipToEndBracket('\n');
1630 } else {
1631 parser.skipToEndBracket("*/");
1632 }
1633 SkASSERT(!parser.eof());
1634 const char* commentEnd = parser.fChar;
1635 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001636 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001637 comment = &markupChild->fTokens.back();
1638 comment->fTerminator = commentEnd;
1639 }
Cary Clark8032b982017-07-28 11:04:54 -04001640 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001641 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001642 Definition* member = &markupChild->fTokens.back();
1643 member->fName = memberName;
1644 if (comment) {
1645 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001646 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001647 }
1648 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001649 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001650 for (auto outsideMember : child->fChildren) {
1651 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001652 continue;
1653 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001654 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1655 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001656 continue;
1657 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001658 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1659 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001660 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001661 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001662 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001663 // FIXME: ? add comment as well ?
1664 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001665 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001666 if (markupDef) {
1667 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1668 SkASSERT(classDef.fStart);
1669 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1670 markupChild->fName = uniqueName;
1671 classDef.fEnums[uniqueName] = markupChild;
1672 }
Cary Clark8032b982017-07-28 11:04:54 -04001673 return true;
1674}
1675
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001676bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001677 fParent = &fIncludeMap[name];
1678 fParent->fName = name;
1679 fParent->fFileName = fFileName;
1680 fParent->fType = Definition::Type::kFileType;
1681 fParent->fContentStart = fChar;
1682 fParent->fContentEnd = fEnd;
1683 // parse include file into tree
1684 while (fChar < fEnd) {
1685 if (!this->parseChar()) {
1686 return false;
1687 }
1688 }
1689 // parse tree and add named objects to maps
1690 fParent = &fIncludeMap[name];
1691 if (!this->parseObjects(fParent, nullptr)) {
1692 return false;
1693 }
1694 return true;
1695}
1696
1697bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1698 const char* typeStart = child->fChildren[0]->fContentStart;
1699 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001700 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001701 Definition* markupChild = &markupDef->fTokens.back();
1702 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001703 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001704 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1705 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1706 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1707 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001708 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001709 classDef.fMembers[uniqueName] = markupChild;
1710 if (child->fParentIndex >= 2) {
1711 auto comment = child->fParent->fTokens.begin();
1712 std::advance(comment, child->fParentIndex - 2);
1713 if (Definition::Type::kBracket == comment->fType
1714 && (Bracket::kSlashStar == comment->fBracket
1715 || Bracket::kSlashSlash == comment->fBracket)) {
1716 TextParser parser(&*comment);
1717 do {
1718 parser.skipToAlpha();
1719 if (parser.eof()) {
1720 break;
1721 }
1722 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001723 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001724 if (Bracket::kSlashStar == comment->fBracket) {
1725 const char* commentEnd = parser.strnstr("*/", end);
1726 if (commentEnd) {
1727 end = commentEnd;
1728 }
1729 }
1730 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001731 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001732 Definition* commentChild = &markupDef->fTokens.back();
1733 markupChild->fChildren.emplace_back(commentChild);
1734 parser.skipTo(end);
1735 } while (!parser.eof());
1736 }
1737 }
1738 return true;
1739}
1740
1741bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1742 auto tokenIter = child->fParent->fTokens.begin();
1743 std::advance(tokenIter, child->fParentIndex);
1744 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001745 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001746 bool addConst = false;
1747 auto operatorCheck = tokenIter;
1748 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1749 operatorCheck = std::prev(tokenIter);
1750 }
1751 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001752 auto closeParen = std::next(tokenIter);
1753 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1754 '(' == closeParen->fContentStart[0]);
1755 nameEnd = closeParen->fContentEnd + 1;
1756 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001757 if (Definition::Type::kKeyWord == closeParen->fType &&
1758 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001759 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001760 }
Cary Clarka560c472017-11-27 10:44:06 -05001761 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001762 }
1763 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001764 if (addConst) {
1765 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001766 }
Cary Clark8032b982017-07-28 11:04:54 -04001767 while (tokenIter != child->fParent->fTokens.begin()) {
1768 auto testIter = std::prev(tokenIter);
1769 switch (testIter->fType) {
1770 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001771 if (testIter == child->fParent->fTokens.begin() &&
1772 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1773 KeyWord::kIfndef == child->fParent->fKeyWord ||
1774 KeyWord::kIf == child->fParent->fKeyWord)) {
1775 std::next(tokenIter);
1776 break;
1777 }
Cary Clark8032b982017-07-28 11:04:54 -04001778 goto keepGoing;
1779 case Definition::Type::kKeyWord: {
1780 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1781 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1782 goto keepGoing;
1783 }
1784 } break;
1785 case Definition::Type::kBracket:
1786 if (Bracket::kAngle == testIter->fBracket) {
1787 goto keepGoing;
1788 }
1789 break;
1790 case Definition::Type::kPunctuation:
1791 if (Punctuation::kSemicolon == testIter->fPunctuation
1792 || Punctuation::kLeftBrace == testIter->fPunctuation
1793 || Punctuation::kColon == testIter->fPunctuation) {
1794 break;
1795 }
1796 keepGoing:
1797 tokenIter = testIter;
1798 continue;
1799 default:
1800 break;
1801 }
1802 break;
1803 }
1804 tokenIter->fName = nameStr;
1805 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001806 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001807 auto testIter = child->fParent->fTokens.begin();
1808 SkASSERT(child->fParentIndex > 0);
1809 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001810 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1811 0 == tokenIter->fParentIndex) {
1812 tokenIter = std::next(tokenIter);
1813 }
Cary Clark8032b982017-07-28 11:04:54 -04001814 const char* start = tokenIter->fContentStart;
1815 const char* end = tokenIter->fContentEnd;
1816 const char kDebugCodeStr[] = "SkDEBUGCODE";
1817 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1818 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1819 std::advance(testIter, 1);
1820 start = testIter->fContentStart + 1;
1821 end = testIter->fContentEnd - 1;
1822 } else {
1823 end = testIter->fContentEnd;
1824 while (testIter != child->fParent->fTokens.end()) {
1825 testIter = std::next(testIter);
1826 switch (testIter->fType) {
1827 case Definition::Type::kPunctuation:
1828 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1829 || Punctuation::kLeftBrace == testIter->fPunctuation
1830 || Punctuation::kColon == testIter->fPunctuation);
1831 end = testIter->fStart;
1832 break;
1833 case Definition::Type::kKeyWord: {
1834 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1835 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1836 continue;
1837 }
1838 } break;
1839 default:
1840 continue;
1841 }
1842 break;
1843 }
1844 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001845 while (end > start && ' ' >= end[-1]) {
1846 --end;
1847 }
Cary Clark9174bda2017-09-19 17:39:32 -04001848 if (!markupDef) {
1849 auto parentIter = child->fParent->fTokens.begin();
1850 SkASSERT(child->fParentIndex > 0);
1851 std::advance(parentIter, child->fParentIndex - 1);
1852 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001853 TextParser nameParser(methodName);
1854 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001855 return true; // expect this is inline class definition outside of class
1856 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001857 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1858 fParent, '\0');
1859 Definition* globalMarkupChild = &fGlobals.back();
1860 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1861 globalMarkupChild->fName = globalUniqueName;
1862 if (!this->findComments(*child, globalMarkupChild)) {
1863 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001864 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001865 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001866 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001867 }
Cary Clark8032b982017-07-28 11:04:54 -04001868 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001869 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001870 Definition* markupChild = &markupDef->fTokens.back();
1871 // do find instead -- I wonder if there is a way to prevent this in c++
1872 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1873 SkASSERT(classDef.fStart);
1874 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1875 markupChild->fName = uniqueName;
1876 if (!this->findComments(*child, markupChild)) {
1877 return false;
1878 }
1879 classDef.fMethods[uniqueName] = markupChild;
1880 return true;
1881}
1882
Cary Clark8032b982017-07-28 11:04:54 -04001883bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1884 for (auto& child : parent->fChildren) {
1885 if (!this->parseObject(child, markupDef)) {
1886 return false;
1887 }
1888 }
1889 return true;
1890}
1891
1892bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1893 // set up for error reporting
1894 fLine = fChar = child->fStart;
1895 fEnd = child->fContentEnd;
1896 // todo: put original line number in child as well
1897 switch (child->fType) {
1898 case Definition::Type::kKeyWord:
1899 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001900 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001901 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001902 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001903 }
1904 break;
1905 case KeyWord::kEnum:
1906 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001907 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001908 }
1909 break;
1910 case KeyWord::kStruct:
1911 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001912 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001913 }
1914 break;
1915 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001916 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001917 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001918 }
1919 break;
1920 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001921 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001922 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001923 }
1924 break;
1925 case KeyWord::kUnion:
1926 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001927 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001928 }
1929 break;
1930 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001931 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001932 }
1933 break;
1934 case Definition::Type::kBracket:
1935 switch (child->fBracket) {
1936 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001937 if (fLastObject) {
1938 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1939 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001940 if (!checkDeprecated.eof()) {
1941 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04001942 if (checkDeprecated.startsWith(gAttrDeprecated)) {
1943 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04001944 break;
1945 }
1946 }
1947 }
1948 {
1949 auto tokenIter = child->fParent->fTokens.begin();
1950 std::advance(tokenIter, child->fParentIndex);
1951 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05001952 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04001953 if (previousToken.startsWith(gAttrDeprecated)) {
1954 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04001955 break;
1956 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05001957 if (Bracket::kPound == child->fParent->fBracket &&
1958 KeyWord::kIf == child->fParent->fKeyWord) {
1959 // TODO: this will skip methods named defined() -- for the
1960 // moment there aren't any
1961 if (previousToken.startsWith("defined")) {
1962 break;
1963 }
1964 }
Cary Clark73fa9722017-08-29 17:36:51 -04001965 }
Cary Clark8032b982017-07-28 11:04:54 -04001966 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001967 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001968 }
Cary Clark89b14562018-03-19 09:04:10 -04001969 if (fAttrDeprecated) {
1970 Definition* lastMethod = &markupDef->fTokens.back();
1971 lastMethod->fDeprecated = true;
1972 fAttrDeprecated = nullptr;
1973 }
Cary Clark73fa9722017-08-29 17:36:51 -04001974 break;
Cary Clark8032b982017-07-28 11:04:54 -04001975 case Bracket::kSlashSlash:
1976 case Bracket::kSlashStar:
1977 // comments are picked up by parsing objects first
1978 break;
1979 case Bracket::kPound:
1980 // special-case the #xxx xxx_DEFINED entries
1981 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001982 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001983 case KeyWord::kIfndef:
1984 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001985 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04001986 if (!this->parseObjects(child, markupDef)) {
1987 return false;
1988 }
1989 break;
1990 }
1991 goto preproError;
1992 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001993 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04001994 break;
1995 }
1996 goto preproError;
1997 case KeyWord::kEndif:
1998 if (child->boilerplateEndIf()) {
1999 break;
2000 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002001 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002002 case KeyWord::kInclude:
2003 // ignored for now
2004 break;
2005 case KeyWord::kElse:
2006 case KeyWord::kElif:
2007 // todo: handle these
2008 break;
2009 default:
2010 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002011 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002012 }
2013 break;
2014 case Bracket::kAngle:
2015 // pick up templated function pieces when method is found
2016 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002017 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002018 if (!this->parseObjects(child, markupDef)) {
2019 return false;
2020 }
Cary Clark73fa9722017-08-29 17:36:51 -04002021 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002022 case Bracket::kSquare: {
2023 // check to see if parent is operator, the only case we handle so far
2024 auto prev = child->fParent->fTokens.begin();
2025 std::advance(prev, child->fParentIndex - 1);
2026 if (KeyWord::kOperator != prev->fKeyWord) {
2027 return child->reportError<bool>("expected operator overload");
2028 }
2029 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002030 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002031 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002032 }
2033 break;
2034 case Definition::Type::kWord:
2035 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002036 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002037 }
2038 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002039 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002040 }
2041 break;
2042 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002043 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002044 break;
2045 }
2046 return true;
2047}
2048
Cary Clarkbbfda252018-03-09 15:32:01 -05002049bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2050 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002051}
2052
Cary Clark2f466242017-12-11 16:03:17 -05002053bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2054 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002055 typedefParser.skipExact("typedef");
2056 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002057 string nameStr = typedefParser.typedefName();
2058 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002059 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2060 child->fLineCount, fParent, '\0');
2061 Definition* globalMarkupChild = &fGlobals.back();
2062 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2063 globalMarkupChild->fName = globalUniqueName;
2064 if (!this->findComments(*child, globalMarkupChild)) {
2065 return false;
2066 }
2067 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark2f466242017-12-11 16:03:17 -05002068 return true;
2069 }
2070 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002071 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002072 Definition* markupChild = &markupDef->fTokens.back();
2073 markupChild->fName = nameStr;
2074 markupChild->fTerminator = markupChild->fContentEnd;
2075 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2076 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002077 return true;
2078}
2079
2080bool IncludeParser::parseUnion() {
2081
2082 return true;
2083}
2084
2085bool IncludeParser::parseChar() {
2086 char test = *fChar;
2087 if ('\\' == fPrev) {
2088 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002089// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002090 fLine = fChar + 1;
2091 }
2092 goto done;
2093 }
2094 switch (test) {
2095 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002096// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002097 fLine = fChar + 1;
2098 if (fInChar) {
2099 return reportError<bool>("malformed char");
2100 }
2101 if (fInString) {
2102 return reportError<bool>("malformed string");
2103 }
2104 if (!this->checkForWord()) {
2105 return false;
2106 }
2107 if (Bracket::kPound == this->topBracket()) {
2108 KeyWord keyWord = fParent->fKeyWord;
2109 if (KeyWord::kNone == keyWord) {
2110 return this->reportError<bool>("unhandled preprocessor directive");
2111 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002112 if (fInDefine) {
2113 SkASSERT(KeyWord::kDefine == keyWord);
2114 fInDefine = false;
2115 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002116 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002117 this->popBracket();
2118 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002119 if (fInBrace) {
2120 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2121 fInBrace = nullptr;
2122 }
Cary Clark8032b982017-07-28 11:04:54 -04002123 } else if (Bracket::kSlashSlash == this->topBracket()) {
2124 this->popBracket();
2125 }
2126 break;
2127 case '*':
2128 if (!fInCharCommentString && '/' == fPrev) {
2129 this->pushBracket(Bracket::kSlashStar);
2130 }
2131 if (!this->checkForWord()) {
2132 return false;
2133 }
2134 if (!fInCharCommentString) {
2135 this->addPunctuation(Punctuation::kAsterisk);
2136 }
2137 break;
2138 case '/':
2139 if ('*' == fPrev) {
2140 if (!fInCharCommentString) {
2141 return reportError<bool>("malformed closing comment");
2142 }
2143 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002144 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002145 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002146 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002147 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002148 }
2149 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002150 }
Cary Clark8032b982017-07-28 11:04:54 -04002151 if (!fInCharCommentString && '/' == fPrev) {
2152 this->pushBracket(Bracket::kSlashSlash);
2153 break;
2154 }
2155 if (!this->checkForWord()) {
2156 return false;
2157 }
2158 break;
2159 case '\'':
2160 if (Bracket::kChar == this->topBracket()) {
2161 this->popBracket();
2162 } else if (!fInComment && !fInString) {
2163 if (fIncludeWord) {
2164 return this->reportError<bool>("word then single-quote");
2165 }
2166 this->pushBracket(Bracket::kChar);
2167 }
2168 break;
2169 case '\"':
2170 if (Bracket::kString == this->topBracket()) {
2171 this->popBracket();
2172 } else if (!fInComment && !fInChar) {
2173 if (fIncludeWord) {
2174 return this->reportError<bool>("word then double-quote");
2175 }
2176 this->pushBracket(Bracket::kString);
2177 }
2178 break;
Cary Clark8032b982017-07-28 11:04:54 -04002179 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002180 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002181 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2182 this->pushBracket(Bracket::kDebugCode);
2183 break;
2184 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002185 case ':':
2186 case '[':
2187 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002188 if (fInCharCommentString) {
2189 break;
2190 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002191 if (fInDefine && fInBrace) {
2192 break;
2193 }
Cary Clark8032b982017-07-28 11:04:54 -04002194 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2195 break;
2196 }
2197 if (!fInBrace) {
2198 if (!this->checkForWord()) {
2199 return false;
2200 }
2201 if (':' == test && !fInFunction) {
2202 break;
2203 }
2204 if ('{' == test) {
2205 this->addPunctuation(Punctuation::kLeftBrace);
2206 } else if (':' == test) {
2207 this->addPunctuation(Punctuation::kColon);
2208 }
2209 }
2210 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2211 && Bracket::kColon == fInBrace->fBracket) {
2212 Definition* braceParent = fParent->fParent;
2213 braceParent->fChildren.pop_back();
2214 braceParent->fTokens.pop_back();
2215 fParent = braceParent;
2216 fInBrace = nullptr;
2217 }
2218 this->pushBracket(
2219 '(' == test ? Bracket::kParen :
2220 '[' == test ? Bracket::kSquare :
2221 '{' == test ? Bracket::kBrace :
2222 Bracket::kColon);
2223 if (!fInBrace
2224 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2225 && fInFunction) {
2226 fInBrace = fParent;
2227 }
2228 } break;
2229 case '<':
2230 if (fInCharCommentString || fInBrace) {
2231 break;
2232 }
2233 if (!this->checkForWord()) {
2234 return false;
2235 }
2236 if (fInEnum) {
2237 break;
2238 }
2239 this->pushBracket(Bracket::kAngle);
2240 break;
2241 case ')':
2242 case ']':
2243 case '}': {
2244 if (fInCharCommentString) {
2245 break;
2246 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002247 if (fInDefine && fInBrace) {
2248 break;
2249 }
Cary Clark8032b982017-07-28 11:04:54 -04002250 if (!fInBrace) {
2251 if (!this->checkForWord()) {
2252 return false;
2253 }
2254 }
2255 bool popBraceParent = fInBrace == fParent;
2256 if ((')' == test ? Bracket::kParen :
2257 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2258 this->popBracket();
2259 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002260 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002261 } else {
2262 fInFunction = '}' != test;
2263 }
Cary Clark73fa9722017-08-29 17:36:51 -04002264 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2265 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002266 } else {
2267 return reportError<bool>("malformed close bracket");
2268 }
2269 if (popBraceParent) {
2270 Definition* braceParent = fInBrace->fParent;
2271 braceParent->fChildren.pop_back();
2272 braceParent->fTokens.pop_back();
2273 fInBrace = nullptr;
2274 }
2275 } break;
2276 case '>':
2277 if (fInCharCommentString || fInBrace) {
2278 break;
2279 }
2280 if (!this->checkForWord()) {
2281 return false;
2282 }
2283 if (fInEnum) {
2284 break;
2285 }
Cary Clarka560c472017-11-27 10:44:06 -05002286 if (Bracket::kPound == this->topBracket()) {
2287 break;
2288 }
Cary Clark8032b982017-07-28 11:04:54 -04002289 if (Bracket::kAngle == this->topBracket()) {
2290 this->popBracket();
2291 } else {
2292 return reportError<bool>("malformed close angle bracket");
2293 }
2294 break;
2295 case '#': {
2296 if (fInCharCommentString || fInBrace) {
2297 break;
2298 }
2299 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2300 this->pushBracket(Bracket::kPound);
2301 break;
2302 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002303 case ' ':
2304 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2305 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2306 fInBrace = fParent;
2307 // delimiting brackets are space ... unescaped-linefeed
2308 }
Cary Clark8032b982017-07-28 11:04:54 -04002309 case '&':
2310 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002311 case '+':
2312 case '=':
2313 case '-':
2314 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002315 if (fInCharCommentString || fInBrace) {
2316 break;
2317 }
2318 if (!this->checkForWord()) {
2319 return false;
2320 }
2321 break;
2322 case ';':
2323 if (fInCharCommentString || fInBrace) {
2324 break;
2325 }
2326 if (!this->checkForWord()) {
2327 return false;
2328 }
2329 if (Definition::Type::kKeyWord == fParent->fType
2330 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002331 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2332 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002333 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2334 this->popObject();
2335 }
Cary Clark8032b982017-07-28 11:04:54 -04002336 if (KeyWord::kEnum == fParent->fKeyWord) {
2337 fInEnum = false;
2338 }
2339 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002340 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2341 this->popObject();
2342 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002343 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002344 } else if (Definition::Type::kBracket == fParent->fType
2345 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2346 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2347 list<Definition>::iterator baseIter = fParent->fTokens.end();
2348 list<Definition>::iterator namedIter = fParent->fTokens.end();
2349 for (auto tokenIter = fParent->fTokens.end();
2350 fParent->fTokens.begin() != tokenIter--; ) {
2351 if (tokenIter->fLineCount == fLineCount) {
2352 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2353 if (namedIter != fParent->fTokens.end()) {
2354 return reportError<bool>("found two named member tokens");
2355 }
2356 namedIter = tokenIter;
2357 }
2358 baseIter = tokenIter;
2359 } else {
2360 break;
2361 }
2362 }
2363 // FIXME: if a member definition spans multiple lines, this won't work
2364 if (namedIter != fParent->fTokens.end()) {
2365 if (baseIter == namedIter) {
2366 return this->reportError<bool>("expected type before named token");
2367 }
2368 Definition* member = &*namedIter;
2369 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002370 if (!member->fTerminator) {
2371 member->fTerminator = member->fContentEnd;
2372 }
Cary Clark8032b982017-07-28 11:04:54 -04002373 fParent->fChildren.push_back(member);
2374 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2375 member->fChildren.push_back(&*nameType);
2376 }
Cary Clark8032b982017-07-28 11:04:54 -04002377 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002378 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002379 } else if (fParent->fChildren.size() > 0) {
2380 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002381 Definition* priorEnum = fPriorEnum;
2382 fPriorEnum = nullptr;
2383 if (!priorEnum) {
2384 while (fParent->fChildren.begin() != lastIter) {
2385 std::advance(lastIter, -1);
2386 priorEnum = *lastIter;
2387 if (Definition::Type::kBracket != priorEnum->fType ||
2388 (Bracket::kSlashSlash != priorEnum->fBracket
2389 && Bracket::kSlashStar != priorEnum->fBracket)) {
2390 break;
2391 }
Cary Clark8032b982017-07-28 11:04:54 -04002392 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002393 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002394 }
2395 if (Definition::Type::kKeyWord == priorEnum->fType
2396 && KeyWord::kEnum == priorEnum->fKeyWord) {
2397 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002398 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002399 while (tokenWalker != fParent->fTokens.end()) {
2400 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002401 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002402 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2403 break;
2404 }
2405 }
2406 while (tokenWalker != fParent->fTokens.end()) {
2407 std::advance(tokenWalker, 1);
2408 const Definition* test = &*tokenWalker;
2409 if (Definition::Type::kBracket != test->fType ||
2410 (Bracket::kSlashSlash != test->fBracket
2411 && Bracket::kSlashStar != test->fBracket)) {
2412 break;
2413 }
2414 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002415 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002416 Definition* start = &*tokenWalker;
2417 bool foundExpected = true;
2418 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2419 const Definition* test = &*tokenWalker;
2420 if (expected != test->fKeyWord) {
2421 foundExpected = false;
2422 break;
2423 }
2424 if (tokenWalker == fParent->fTokens.end()) {
2425 break;
2426 }
2427 std::advance(tokenWalker, 1);
2428 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002429 if (!foundExpected) {
2430 foundExpected = true;
2431 tokenWalker = saveTokenWalker;
2432 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2433 const Definition* test = &*tokenWalker;
2434 if (expected != test->fKeyWord) {
2435 foundExpected = false;
2436 break;
2437 }
2438 if (tokenWalker == fParent->fTokens.end()) {
2439 break;
2440 }
2441 if (KeyWord::kNone != expected) {
2442 std::advance(tokenWalker, 1);
2443 }
2444 }
2445 if (foundExpected) {
2446 auto nameToken = priorEnum->fTokens.begin();
2447 string enumName = string(nameToken->fContentStart,
2448 nameToken->fContentEnd - nameToken->fContentStart);
2449 const Definition* test = &*tokenWalker;
2450 string constType = string(test->fContentStart,
2451 test->fContentEnd - test->fContentStart);
2452 if (enumName != constType) {
2453 foundExpected = false;
2454 } else {
2455 std::advance(tokenWalker, 1);
2456 }
2457 }
2458 }
Cary Clark8032b982017-07-28 11:04:54 -04002459 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2460 const char* nameStart = tokenWalker->fStart;
2461 std::advance(tokenWalker, 1);
2462 if (tokenWalker != fParent->fTokens.end()) {
2463 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002464 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002465 start->fName = string(nameStart, tp.fChar - nameStart);
2466 start->fContentEnd = fChar;
2467 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002468 fPriorEnum = priorEnum;
2469 }
Cary Clark8032b982017-07-28 11:04:54 -04002470 }
2471 }
2472 }
2473 this->addPunctuation(Punctuation::kSemicolon);
2474 fInFunction = false;
2475 break;
2476 case '~':
2477 if (fInEnum) {
2478 break;
2479 }
2480 case '0': case '1': case '2': case '3': case '4':
2481 case '5': case '6': case '7': case '8': case '9':
2482 // TODO: don't want to parse numbers, but do need to track for enum defs
2483 // break;
2484 case 'A': case 'B': case 'C': case 'D': case 'E':
2485 case 'F': case 'G': case 'H': case 'I': case 'J':
2486 case 'K': case 'L': case 'M': case 'N': case 'O':
2487 case 'P': case 'Q': case 'R': case 'S': case 'T':
2488 case 'U': case 'V': case 'W': case 'X': case 'Y':
2489 case 'Z': case '_':
2490 case 'a': case 'b': case 'c': case 'd': case 'e':
2491 case 'f': case 'g': case 'h': case 'i': case 'j':
2492 case 'k': case 'l': case 'm': case 'n': case 'o':
2493 case 'p': case 'q': case 'r': case 's': case 't':
2494 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002495 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002496 if (fInCharCommentString || fInBrace) {
2497 break;
2498 }
2499 if (!fIncludeWord) {
2500 fIncludeWord = fChar;
2501 }
2502 break;
2503 }
2504done:
2505 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002506 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002507 return true;
2508}
2509
2510void IncludeParser::validate() const {
2511 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2512 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2513 }
2514 IncludeParser::ValidateKeyWords();
2515}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002516
Cary Clark186d08f2018-04-03 08:43:27 -04002517bool IncludeParser::references(const SkString& file) const {
2518 // if includes weren't passed one at a time, assume all references are valid
2519 if (fIncludeMap.empty()) {
2520 return true;
2521 }
2522 SkASSERT(file.endsWith(".bmh") );
2523 string root(file.c_str(), file.size() - 4);
2524 string kReference("_Reference");
2525 if (string::npos != root.find(kReference)) {
2526 root = root.substr(0, root.length() - kReference.length());
2527 }
2528 if (fIClassMap.end() != fIClassMap.find(root)) {
2529 return true;
2530 }
2531 if (fIStructMap.end() != fIStructMap.find(root)) {
2532 return true;
2533 }
2534 // TODO incomplete: probably need to look in other places for class-less includes like SkColor.h
2535 return false;
2536}
2537
Cary Clark2dc84ad2018-01-26 12:56:22 -05002538void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2539 if (!sk_isdir(includes)) {
2540 IncludeParser::RemoveOneFile(docs, includes);
2541 } else {
2542 SkOSFile::Iter it(includes, ".h");
2543 for (SkString file; it.next(&file); ) {
2544 SkString p = SkOSPath::Join(includes, file.c_str());
2545 const char* hunk = p.c_str();
2546 if (!SkStrEndsWith(hunk, ".h")) {
2547 continue;
2548 }
2549 IncludeParser::RemoveOneFile(docs, hunk);
2550 }
2551 }
2552}
2553
2554void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2555 const char* lastForward = strrchr(includesFile, '/');
2556 const char* lastBackward = strrchr(includesFile, '\\');
2557 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2558 if (!last) {
2559 last = includesFile;
2560 } else {
2561 last += 1;
2562 }
2563 SkString baseName(last);
2564 SkASSERT(baseName.endsWith(".h"));
2565 baseName.remove(baseName.size() - 2, 2);
2566 baseName.append("_Reference.bmh");
2567 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2568 remove(fullName.c_str());
2569}