blob: 12bd787eb8bd65fc651377f2bdc405c85a65d08f [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 Clark4dc5a452018-05-21 11:56:57 -040053 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040054 { "union", KeyWord::kUnion, KeyProperty::kObject },
55 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
56 { "void", KeyWord::kVoid, KeyProperty::kNumber },
57};
58
59const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
60
61KeyWord IncludeParser::FindKey(const char* start, const char* end) {
62 int ch = 0;
63 for (size_t index = 0; index < kKeyWordCount; ) {
64 if (start[ch] > kKeyWords[index].fName[ch]) {
65 ++index;
66 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
67 return KeyWord::kNone;
68 }
69 continue;
70 }
71 if (start[ch] < kKeyWords[index].fName[ch]) {
72 return KeyWord::kNone;
73 }
74 ++ch;
75 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040076 if (end - start < (int) strlen(kKeyWords[index].fName)) {
77 return KeyWord::kNone;
78 }
Cary Clark8032b982017-07-28 11:04:54 -040079 return kKeyWords[index].fKeyWord;
80 }
81 }
82 return KeyWord::kNone;
83}
84
85void IncludeParser::ValidateKeyWords() {
86 for (size_t index = 1; index < kKeyWordCount; ++index) {
87 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
88 == (int) kKeyWords[index].fKeyWord);
89 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
90 }
91}
92
93void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050094 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040095 fIncludeWord = nullptr;
96 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
97 Definition* def = &fParent->fTokens.back();
98 this->addDefinition(def);
99 if (KeyWord::kEnum == fParent->fKeyWord) {
100 fInEnum = true;
101 }
102 }
103}
104
Ben Wagner63fd7602017-10-09 15:45:33 -0400105void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400106 const vector<string>& foundParams) {
107 for (auto& methodParam : methodParams) {
108 bool found = false;
109 for (auto& foundParam : foundParams) {
110 if (methodParam == foundParam) {
111 found = true;
112 break;
113 }
114 }
115 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400116 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400117 }
118 }
119 for (auto& foundParam : foundParams) {
120 bool found = false;
121 for (auto& methodParam : methodParams) {
122 if (methodParam == foundParam) {
123 found = true;
124 break;
125 }
126 }
127 if (!found) {
128 this->reportError("doxygen param does not match method declaration");
129 }
130 }
131}
132
133bool IncludeParser::checkForWord() {
134 if (!fIncludeWord) {
135 return true;
136 }
137 KeyWord keyWord = FindKey(fIncludeWord, fChar);
138 if (KeyWord::kNone != keyWord) {
139 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
140 this->addKeyword(keyWord);
141 return true;
142 }
143 } else {
144 this->addWord();
145 return true;
146 }
147 Definition* poundDef = fParent;
148 if (!fParent) {
149 return reportError<bool>("expected parent");
150 }
151 if (Definition::Type::kBracket != poundDef->fType) {
152 return reportError<bool>("expected bracket");
153 }
154 if (Bracket::kPound != poundDef->fBracket) {
155 return reportError<bool>("expected preprocessor");
156 }
157 if (KeyWord::kNone != poundDef->fKeyWord) {
158 return reportError<bool>("already found keyword");
159 }
160 poundDef->fKeyWord = keyWord;
161 fIncludeWord = nullptr;
162 switch (keyWord) {
163 // these do not link to other # directives
164 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400165 if (!fInBrace) {
166 SkASSERT(!fInDefine);
167 fInDefine = true;
168 }
Cary Clark8032b982017-07-28 11:04:54 -0400169 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500170 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400171 break;
172 // these start a # directive link
173 case KeyWord::kIf:
174 case KeyWord::kIfdef:
175 case KeyWord::kIfndef:
176 break;
177 // these continue a # directive link
178 case KeyWord::kElif:
179 case KeyWord::kElse: {
180 this->popObject(); // pop elif
181 if (Bracket::kPound != fParent->fBracket) {
182 return this->reportError<bool>("expected preprocessor directive");
183 }
184 this->popBracket(); // pop if
185 poundDef->fParent = fParent;
186 this->addDefinition(poundDef); // push elif back
187 } break;
188 // this ends a # directive link
189 case KeyWord::kEndif:
190 // FIXME : should this be calling popBracket() instead?
191 this->popObject(); // pop endif
192 if (Bracket::kPound != fParent->fBracket) {
193 return this->reportError<bool>("expected preprocessor directive");
194 }
195 this->popBracket(); // pop if/else
196 break;
197 default:
198 SkASSERT(0);
199 }
200 return true;
201}
202
203string IncludeParser::className() const {
204 string name(fParent->fName);
205 size_t slash = name.find_last_of("/");
206 if (string::npos == slash) {
207 slash = name.find_last_of("\\");
208 }
209 SkASSERT(string::npos != slash);
210 string result = name.substr(slash);
211 result = result.substr(1, result.size() - 3);
212 return result;
213}
214
Cary Clark884dd7d2017-10-11 10:37:52 -0400215#include <sstream>
216#include <iostream>
217
Cary Clark8032b982017-07-28 11:04:54 -0400218bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400219 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400220 string className = classMapper.first;
221 auto finder = bmhParser.fClassMap.find(className);
222 if (bmhParser.fClassMap.end() == finder) {
223 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400224 continue;
225 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400226 RootDefinition* root = &finder->second;
227 root->clearVisited();
228 }
229 for (auto& classMapper : fIClassMap) {
230 string className = classMapper.first;
231 std::istringstream iss(className);
232 string classStr;
233 string classBase;
234 RootDefinition* root = nullptr;
235 while (std::getline(iss, classStr, ':')) {
236 if (root) {
237 if (!classStr.length()) {
238 continue;
239 }
240 classBase += "::" + classStr;
241 auto finder = root->fBranches.find(classBase);
242 if (root->fBranches.end() != finder) {
243 root = finder->second;
244 } else {
245 SkASSERT(0);
246 }
247 } else {
248 classBase = classStr;
249 auto finder = bmhParser.fClassMap.find(classBase);
250 if (bmhParser.fClassMap.end() != finder) {
251 root = &finder->second;
252 } else {
253 SkASSERT(0);
254 }
255 }
256 }
Cary Clark8032b982017-07-28 11:04:54 -0400257 auto& classMap = classMapper.second;
258 auto& tokens = classMap.fTokens;
259 for (const auto& token : tokens) {
260 if (token.fPrivate) {
261 continue;
262 }
263 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400264 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400265 switch (token.fMarkType) {
266 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400267 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400268 continue;
269 }
Cary Clark8032b982017-07-28 11:04:54 -0400270 if (!def) {
271 string paramName = className + "::";
272 paramName += string(token.fContentStart,
273 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400274 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400275 if (!def && 0 == token.fName.find("operator")) {
276 string operatorName = className + "::";
277 TextParser oper("", token.fStart, token.fContentEnd, 0);
278 const char* start = oper.strnstr("operator", token.fContentEnd);
279 SkASSERT(start);
280 oper.skipTo(start);
281 oper.skipToEndBracket('(');
282 int parens = 0;
283 do {
284 if ('(' == oper.peek()) {
285 ++parens;
286 } else if (')' == oper.peek()) {
287 --parens;
288 }
289 } while (!oper.eof() && oper.next() && parens > 0);
290 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400291 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400292 }
293 }
294 if (!def) {
295 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
296 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
297 string constructorName = className + "::";
298 constructorName += string(token.fContentStart + skip,
299 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400300 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400301 }
302 if (!def && 0 == token.fName.find("SK_")) {
303 string incName = token.fName + "()";
304 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400305 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400306 if (def) {
307 if (def->fName == incName) {
308 def->fVisited = true;
309 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400310 def = root->find(className + "::toString",
311 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400312 if (def) {
313 def->fVisited = true;
314 } else {
315 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500316 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400317 }
318 }
319 break;
320 } else {
321 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500322 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400323 }
324 }
325 }
326 if (!def) {
327 bool allLower = true;
328 for (size_t index = 0; index < token.fName.length(); ++index) {
329 if (!islower(token.fName[index])) {
330 allLower = false;
331 break;
332 }
333 }
334 if (allLower) {
335 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400336 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400337 }
338 }
339 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400340 if (gAttrDeprecated == token.fName) {
341 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400342 break;
343 }
344 if (0 == token.fName.find("SkDEBUGCODE")) {
345 break;
346 }
347 }
348 if (!def) {
349 // simple method names inside nested classes have a bug and are missing trailing parens
350 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400351 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400352 }
353 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500354 if (!root->fDeprecated) {
355 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
356 fFailed = true;
357 }
Cary Clark8032b982017-07-28 11:04:54 -0400358 break;
359 }
Cary Clark73fa9722017-08-29 17:36:51 -0400360 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400361 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400362 if (MarkType::kDefinedBy == def->fMarkType) {
363 def->fParent->fVisited = true;
364 }
Cary Clark89b14562018-03-19 09:04:10 -0400365 if (token.fDeprecated && !def->fDeprecated) {
366 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
367 }
Cary Clark8032b982017-07-28 11:04:54 -0400368 } else {
369 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500370 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400371 }
372 } break;
373 case MarkType::kComment:
374 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400375 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400376 case MarkType::kEnum: {
377 if (!def) {
378 // work backwards from first word to deduce #Enum name
379 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
380 SkAssertResult(firstMember.skipName("enum"));
381 SkAssertResult(firstMember.skipToEndBracket('{'));
382 firstMember.next();
383 firstMember.skipWhiteSpace();
384 SkASSERT('k' == firstMember.peek());
385 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400386 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400387 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400388 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400389 const char* lastUnderscore = nullptr;
390 do {
391 if (!firstMember.skipToEndBracket('_')) {
392 break;
393 }
394 if (firstMember.fChar > wordEnd) {
395 break;
396 }
397 lastUnderscore = firstMember.fChar;
398 } while (firstMember.next());
399 if (lastUnderscore) {
400 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400401 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400402 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400403 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400404 }
405 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500406 if (!root->fDeprecated) {
407 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
408 fFailed = true;
409 }
Cary Clark8032b982017-07-28 11:04:54 -0400410 break;
411 }
412 }
413 def->fVisited = true;
414 for (auto& child : def->fChildren) {
415 if (MarkType::kCode == child->fMarkType) {
416 def = child;
417 break;
418 }
419 }
420 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500421 if (!root->fDeprecated) {
422 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
423 fFailed = true;
424 }
Cary Clark8032b982017-07-28 11:04:54 -0400425 break;
426 }
427 if (def->crossCheck(token)) {
428 def->fVisited = true;
429 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500430 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
431 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400432 }
433 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400434 string constName = MarkType::kEnumClass == token.fMarkType ?
435 fullName : className;
436 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400437 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400438 if (!def) {
439 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400440 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400441 }
442 if (!def) {
443 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500444 if (!root->fDeprecated) {
445 SkDebugf("const missing from bmh: %s\n", constName.c_str());
446 fFailed = true;
447 }
Cary Clark8032b982017-07-28 11:04:54 -0400448 }
449 } else {
450 def->fVisited = true;
451 }
452 }
453 } break;
454 case MarkType::kMember:
455 if (def) {
456 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500457 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400458 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500459 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400460 }
461 break;
Cary Clark2f466242017-12-11 16:03:17 -0500462 case MarkType::kTypedef:
463 if (def) {
464 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500465 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500466 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500467 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500468 }
469 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400470 case MarkType::kConst:
471 if (def) {
472 def->fVisited = true;
473 } else if (!root->fDeprecated) {
474 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
475 fFailed = true;
476 }
477 break;
Cary Clark8032b982017-07-28 11:04:54 -0400478 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400479 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400480 break;
481 }
482 }
483 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500484 int crossChecks = 0;
485 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400486 for (auto& classMapper : fIClassMap) {
487 string className = classMapper.first;
488 auto finder = bmhParser.fClassMap.find(className);
489 if (bmhParser.fClassMap.end() == finder) {
490 continue;
491 }
492 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500493 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500494 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400495 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500496 if (crossChecks) {
497 SkDebugf(".");
498 } else {
499 SkDebugf("cross-check");
500 firstCheck = className;
501 }
502 ++crossChecks;
503 }
504 if (crossChecks) {
505 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500506 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500507 }
508 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400509 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400510 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500511 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400512}
513
514IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400515 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400516 string className;
517 const Definition* test = fParent;
518 while (Definition::Type::kFileType != test->fType) {
519 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
520 className = test->fName + "::";
521 break;
522 }
523 test = test->fParent;
524 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400525 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400526 unordered_map<string, IClassDefinition>& map = fIClassMap;
527 IClassDefinition& markupDef = map[className];
528 if (markupDef.fStart) {
529 typedef IClassDefinition* IClassDefPtr;
530 return INHERITED::reportError<IClassDefPtr>("class already defined");
531 }
532 markupDef.fFileName = fFileName;
533 markupDef.fStart = includeDef.fStart;
534 markupDef.fContentStart = includeDef.fStart;
535 markupDef.fName = className;
536 markupDef.fContentEnd = includeDef.fContentEnd;
537 markupDef.fTerminator = includeDef.fTerminator;
538 markupDef.fParent = fParent;
539 markupDef.fLineCount = fLineCount;
540 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
541 MarkType::kStruct : MarkType::kClass;
542 markupDef.fKeyWord = includeDef.fKeyWord;
543 markupDef.fType = Definition::Type::kMark;
544 fParent = &markupDef;
545 return &markupDef;
546}
547
548void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
549 auto& tokens = classDef.fTokens;
550 for (auto& token : tokens) {
551 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
552 continue;
553 }
554 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400555 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400556 }
557 switch (token.fMarkType) {
558 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500559 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500560 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400561 break;
562 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400563 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400564 break;
565 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400566 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400567 continue;
568 break;
569 default:
570 SkASSERT(0);
571 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400572 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400573 }
574}
Cary Clark9174bda2017-09-19 17:39:32 -0400575void IncludeParser::dumpComment(const Definition& token) {
576 fLineCount = token.fLineCount;
577 fChar = fLine = token.fContentStart;
578 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400579 bool sawParam = false;
580 bool multiline = false;
581 bool sawReturn = false;
582 bool sawComment = false;
583 bool methodHasReturn = false;
584 vector<string> methodParams;
585 vector<string> foundParams;
586 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400587 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
588 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500589 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400590 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500591 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400592 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400593 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500594 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400595 && !methodParser.strnchr('~', methodParser.fEnd);
596 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
597 const char* nextEnd = paren;
598 do {
599 string paramName;
600 methodParser.fChar = nextEnd + 1;
601 methodParser.skipSpace();
602 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
603 continue;
604 }
605 methodParams.push_back(paramName);
606 } while (')' != nextEnd[0]);
607 }
Cary Clark9174bda2017-09-19 17:39:32 -0400608 for (const auto& child : token.fTokens) {
609 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
610 break;
611 }
Cary Clark8032b982017-07-28 11:04:54 -0400612 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400613 if (child.fPrivate) {
614 break;
615 }
Cary Clark8032b982017-07-28 11:04:54 -0400616 if ('@' == child.fContentStart[0]) {
617 TextParser parser(&child);
618 do {
619 parser.next();
620 if (parser.startsWith("param ")) {
621 parser.skipWord("param");
622 const char* parmStart = parser.fChar;
623 parser.skipToSpace();
624 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
625 parser.skipWhiteSpace();
626 do {
627 size_t nextComma = parmName.find(',');
628 string piece;
629 if (string::npos == nextComma) {
630 piece = parmName;
631 parmName = "";
632 } else {
633 piece = parmName.substr(0, nextComma);
634 parmName = parmName.substr(nextComma + 1);
635 }
636 if (sawParam) {
637 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400638 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400639 }
Cary Clark9174bda2017-09-19 17:39:32 -0400640 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400641 } else {
642 if (sawComment) {
643 this->nl();
644 }
645 this->lf(2);
646 }
647 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400648 this->writeTag("Param", piece);
649 this->writeSpace(2);
650 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
651 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400652 sawParam = true;
653 sawComment = false;
654 } while (parmName.length());
655 parser.skipTo(parser.fEnd);
656 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
657 parser.skipWord("return");
658 if ('s' == parser.peek()) {
659 parser.next();
660 }
661 if (sawParam) {
662 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400663 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400664 }
Cary Clark9174bda2017-09-19 17:39:32 -0400665 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400666 }
667 this->checkForMissingParams(methodParams, foundParams);
668 sawParam = false;
669 sawComment = false;
670 multiline = false;
671 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400672 this->writeTag("Return");
673 this->writeSpace(2);
674 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
675 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400676 sawReturn = true;
677 parser.skipTo(parser.fEnd);
678 } else {
679 this->reportError("unexpected doxygen directive");
680 }
681 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400682 } else if (child.length() > 1) {
683 const char* start = child.fContentStart;
684 ptrdiff_t length = child.fContentEnd - start;
685 SkASSERT(length >= 0);
686 while (length && '/' == start[0]) {
687 start += 1;
688 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400689 }
Cary Clark9174bda2017-09-19 17:39:32 -0400690 while (length && '/' == start[length - 1]) {
691 length -= 1;
692 if (length && '*' == start[length - 1]) {
693 length -= 1;
694 }
695 }
696 if (length) {
697 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
698 if (sawParam || sawReturn) {
699 this->indentToColumn(8);
700 }
701 this->writeBlock(length, start);
702 this->writeSpace();
703 sawComment = true;
704 if (sawParam || sawReturn) {
705 multiline = true;
706 }
Cary Clark8032b982017-07-28 11:04:54 -0400707 }
708 }
709 }
710 }
711 if (sawParam || sawReturn) {
712 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400713 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400714 }
Cary Clark9174bda2017-09-19 17:39:32 -0400715 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400716 }
717 if (!sawReturn) {
718 if (!sawParam) {
719 if (sawComment) {
720 this->nl();
721 }
722 this->lf(2);
723 }
724 this->checkForMissingParams(methodParams, foundParams);
725 }
726 if (methodHasReturn != sawReturn) {
727 if (!methodHasReturn) {
728 this->reportError("unexpected doxygen return");
729 } else {
730 if (sawComment) {
731 this->nl();
732 }
733 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400734 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400735 }
736 }
737}
738
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400739void IncludeParser::dumpCommonTail(const Definition& token) {
740 this->lf(2);
741 this->writeTag("Example");
742 this->lf(1);
743 this->writeString("// incomplete");
744 this->lf(1);
745 this->writeEndTag();
746 this->lf(2);
747 this->writeTag("SeeAlso");
748 this->writeSpace();
749 this->writeString("incomplete");
750 this->lf(2);
751 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
752 this->lf(2);
753}
754
755void IncludeParser::dumpDefine(const Definition& token) {
756 this->writeTag("Define", token.fName);
757 this->lf(2);
758 this->writeTag("Code");
759 this->lfAlways(1);
760 this->writeString("###$");
761 this->lfAlways(1);
762 this->indentToColumn(4);
763 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
764 this->lf(1);
765 this->indentToColumn(0);
766 this->writeString("$$$#");
767
768 this->writeEndTag();
769 this->lf(2);
770 this->dumpComment(token);
771 for (auto& child : token.fTokens) {
772 if (MarkType::kComment == child.fMarkType) {
773 continue;
774 }
775 this->writeTag("Param", child.fName);
776 this->writeSpace();
777 this->writeString("incomplete");
778 this->writeSpace();
779 this->writeString("##");
780 this->lf(1);
781 }
782}
783
784void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500785 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400786 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400787 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400788 this->lfAlways(1);
789 this->indentToColumn(4);
790 this->writeString("enum");
791 this->writeSpace();
792 if ("_anonymous" != token.fName.substr(0, 10)) {
793 this->writeString(token.fName);
794 this->writeSpace();
795 }
796 this->writeString("{");
797 this->lfAlways(1);
798 for (auto& child : token.fChildren) {
799 this->indentToColumn(8);
800 this->writeString(child->fName);
801 if (child->length()) {
802 this->writeSpace();
803 this->writeBlock(child->length(), child->fContentStart);
804 }
805 if (',' != fLastChar) {
806 this->writeString(",");
807 }
808 this->lfAlways(1);
809 }
810 this->indentToColumn(4);
811 this->writeString("};");
812 this->lf(1);
813 this->writeString("##");
814 this->lf(2);
815 this->dumpComment(token);
816 for (auto& child : token.fChildren) {
817 // start here;
818 // get comments before
819 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400820 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400821 this->writeSpace();
822 this->writeString(child->fName);
823 TextParser val(child);
824 if (!val.eof()) {
825 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
826 val.next();
827 val.skipSpace();
828 const char* valEnd = val.anyOf(",\n");
829 if (!valEnd) {
830 valEnd = val.fEnd;
831 }
832 this->writeSpace();
833 this->writeBlock(valEnd - val.fStart, val.fStart);
834 } else {
835 this->writeSpace();
836 this->writeDefinition(*child);
837 }
838 }
839 this->lf(1);
840 for (auto comment : child->fChildren) {
841 if (MarkType::kComment == comment->fMarkType) {
842 TextParser parser(comment);
843 parser.skipExact("*");
844 parser.skipExact("*");
845 while (!parser.eof() && parser.skipWhiteSpace()) {
846 parser.skipExact("*");
847 parser.skipWhiteSpace();
848 const char* start = parser.fChar;
849 parser.skipToEndBracket('\n');
850 this->lf(1);
851 this->writeBlock(parser.fChar - start, start);
852 }
853 }
854 }
855 this->writeEndTag();
856 }
857 this->lf(2);
858}
859
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400860bool IncludeParser::dumpGlobals() {
861 size_t lastBSlash = fFileName.rfind('\\');
862 size_t lastSlash = fFileName.rfind('/');
863 size_t lastDotH = fFileName.rfind(".h");
864 SkASSERT(string::npos != lastDotH);
865 if (string::npos != lastBSlash && (string::npos == lastSlash
866 || lastBSlash < lastSlash)) {
867 lastSlash = lastBSlash;
868 } else if (string::npos == lastSlash) {
869 lastSlash = -1;
870 }
871 lastSlash += 1;
872 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
873 string fileName = globalsName + "_Reference.bmh";
874 fOut = fopen(fileName.c_str(), "wb");
875 if (!fOut) {
876 SkDebugf("could not open output file %s\n", globalsName.c_str());
877 return false;
878 }
879 string prefixName = globalsName.substr(0, 2);
880 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
881 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
882 this->writeTagNoLF("Topic", topicName);
883 this->writeTag("Alias", topicName + "_Reference");
884 this->lf(2);
885 this->writeTag("Subtopic", "Overview");
886 fIndent += 4;
887 this->writeTag("Subtopic", "Subtopic");
888 fIndent += 4;
889 this->writeTag("Populate");
890 fIndent -= 4;
891 this->writeEndTag();
892 fIndent -= 4;
893 this->writeEndTag();
894 this->lf(2);
895 if (!fIDefineMap.empty()) {
896 this->writeTag("Subtopic", "Define");
897 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400898 this->writeEndTag();
899 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400900 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400901 if (!fIFunctionMap.empty()) {
902 this->writeTag("Subtopic", "Function");
903 this->writeTag("Populate");
904 this->writeEndTag();
905 this->lf(2);
906 }
907 if (!fIEnumMap.empty()) {
908 this->writeTag("Subtopic", "Enum");
909 this->writeTag("Populate");
910 this->writeEndTag();
911 this->lf(2);
912 }
913 if (!fITemplateMap.empty()) {
914 this->writeTag("Subtopic", "Template");
915 this->writeTag("Populate");
916 this->writeEndTag();
917 this->lf(2);
918 }
919 if (!fITypedefMap.empty()) {
920 this->writeTag("Subtopic", "Typedef");
921 this->writeTag("Populate");
922 this->writeEndTag();
923 this->lf(2);
924 }
925 if (!fIUnionMap.empty()) {
926 this->writeTag("Subtopic", "Union");
927 this->writeTag("Populate");
928 this->writeEndTag();
929 this->lf(2);
930 }
931 std::map<int, Definition*> sortedDefs;
932 for (const auto& entry : fIDefineMap) {
933 sortedDefs[entry.second->fLineCount] = entry.second;
934 }
935 for (const auto& entry : fIFunctionMap) {
936 sortedDefs[entry.second->fLineCount] = entry.second;
937 }
938 for (const auto& entry : fIEnumMap) {
939 sortedDefs[entry.second->fLineCount] = entry.second;
940 }
941 for (const auto& entry : fITemplateMap) {
942 sortedDefs[entry.second->fLineCount] = entry.second;
943 }
944 for (const auto& entry : fITypedefMap) {
945 sortedDefs[entry.second->fLineCount] = entry.second;
946 }
947 for (const auto& entry : fIUnionMap) {
948 sortedDefs[entry.second->fLineCount] = entry.second;
949 }
950 for (const auto& entry : sortedDefs) {
951 const Definition* def = entry.second;
952 this->writeBlockSeparator();
953 switch (def->fMarkType) {
954 case MarkType::kDefine:
955 this->dumpDefine(*def);
956 break;
957 case MarkType::kMethod:
958 this->dumpMethod(*def, globalsName);
959 break;
960 case MarkType::kEnum:
961 case MarkType::kEnumClass:
962 this->dumpEnum(*def, globalsName);
963 break;
964 case MarkType::kTemplate:
965 SkASSERT(0); // incomplete
966 break;
967 case MarkType::kTypedef: {
968 this->writeTag("Typedef");
969 this->writeSpace();
970 TextParser parser(def);
971 if (!parser.skipExact("typedef")) {
972 return false;
973 }
974 if (!parser.skipSpace()) {
975 return false;
976 }
977 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
978 this->lf(2);
979 this->dumpComment(*def);
980 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
981 this->lf(2);
982 } continue;
983 case MarkType::kUnion:
984 SkASSERT(0); // incomplete
985 break;
986 default:
987 SkASSERT(0);
988 }
989 this->dumpCommonTail(*def);
990 }
991 this->writeEndTag("Topic", topicName);
992 this->lfAlways(1);
993 fclose(fOut);
994 SkDebugf("wrote %s\n", fileName.c_str());
995 return true;
996}
997
998bool IncludeParser::isClone(const Definition& token) {
999 string name = token.fName;
1000 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1001}
1002
1003bool IncludeParser::isConstructor(const Definition& token, string className) {
1004 string name = token.fName;
1005 return 0 == name.find(className) || '~' == name[0];
1006}
1007
1008bool IncludeParser::isInternalName(const Definition& token) {
1009 string name = token.fName;
1010 // exception for this SkCanvas function .. for now
1011 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1012 return false;
1013 }
1014 return name.substr(0, 7) == "android"
1015 || 0 == token.fName.find("internal_")
1016 || 0 == token.fName.find("Internal_")
1017 || 0 == token.fName.find("legacy_")
1018 || 0 == token.fName.find("temporary_")
1019 || 0 == token.fName.find("private_");
1020}
1021
1022bool IncludeParser::isOperator(const Definition& token) {
1023 return "operator" == token.fName.substr(0, 8);
1024}
1025
1026void IncludeParser::dumpMethod(const Definition& token, string className) {
1027 this->writeTag("Method");
1028 this->writeSpace();
1029
1030 string name = string(token.fStart ? token.fStart : token.fContentStart,
1031 token.length());
1032 if (this->isOperator(token)) {
1033 string spaceConst(" const");
1034 size_t constPos = name.rfind(spaceConst);
1035 if (name.length() - spaceConst.length() == constPos) {
1036 name = name.substr(0, constPos) + "_const";
1037 }
1038 }
1039 this->writeString(name);
1040 string inType;
1041 if (this->isConstructor(token, className)) {
1042 inType = "Constructor";
1043 } else if (this->isOperator(token)) {
1044 inType = "Operator";
1045 } else {
1046 inType = "incomplete";
1047 }
1048 this->writeTag("In", inType);
1049 this->writeTag("Line");
1050 this->writeSpace(1);
1051 this->writeString("#");
1052 this->writeSpace(1);
1053 this->writeString("incomplete");
1054 this->writeSpace(1);
1055 this->writeString("##");
1056 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001057 this->dumpComment(token);
1058}
1059
1060void IncludeParser::dumpMember(const Definition& token) {
1061 this->writeTag("Member");
1062 this->writeSpace();
1063 this->writeDefinition(token, token.fName, 2);
1064 lf(1);
1065 for (auto child : token.fChildren) {
1066 this->writeDefinition(*child);
1067 }
1068 this->writeEndTag();
1069 lf(2);
1070}
1071
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001072bool IncludeParser::dumpTokens() {
1073 if (!this->dumpGlobals()) {
1074 return false;
1075 }
Cary Clark9174bda2017-09-19 17:39:32 -04001076 for (const auto& member : fIClassMap) {
1077 if (string::npos != member.first.find("::")) {
1078 continue;
1079 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001080 if (!this->dumpTokens(member.first)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001081 return false;
1082 }
1083 }
1084 return true;
1085}
1086
Ben Wagner63fd7602017-10-09 15:45:33 -04001087 // dump equivalent markup
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001088bool IncludeParser::dumpTokens(string skClassName) {
1089 string fileName = skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -04001090 fOut = fopen(fileName.c_str(), "wb");
1091 if (!fOut) {
1092 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -04001093 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001094 }
1095 string prefixName = skClassName.substr(0, 2);
1096 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1097 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -04001098 this->writeTagNoLF("Topic", topicName);
1099 this->writeTag("Alias", topicName + "_Reference");
1100 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001101 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001102 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1103 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001104 this->writeTag(containerType, skClassName);
1105 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001106 auto& tokens = classMap.fTokens;
1107 for (auto& token : tokens) {
1108 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1109 continue;
1110 }
Cary Clark9174bda2017-09-19 17:39:32 -04001111 this->writeDefinition(token);
1112 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001113 }
1114 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001115 bool hasClass = false;
1116 bool hasConst = !fIEnumMap.empty();
1117 bool hasConstructor = false;
1118 bool hasMember = false;
1119 bool hasOperator = false;
Cary Clark8032b982017-07-28 11:04:54 -04001120 for (const auto& oneClass : fIClassMap) {
1121 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1122 continue;
1123 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001124 hasClass = true;
1125 break;
Cary Clark8032b982017-07-28 11:04:54 -04001126 }
Cary Clark8032b982017-07-28 11:04:54 -04001127 for (const auto& token : classMap.fTokens) {
1128 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1129 continue;
1130 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001131 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001132 continue;
1133 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001134 if (this->isConstructor(token, skClassName)) {
1135 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001136 continue;
1137 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001138 if (this->isOperator(token)) {
1139 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001140 continue;
1141 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001142 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001143 continue;
1144 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001145 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001146 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001147 this->writeTag("Subtopic", "Overview");
1148 fIndent += 4;
1149 this->writeTag("Subtopic", "Subtopic");
1150 fIndent += 4;
1151 this->writeTag("Populate");
1152 fIndent -= 4;
1153 this->writeEndTag();
1154 fIndent -= 4;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001155 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001156 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001157
1158 if (hasClass) {
1159 this->writeTag("Subtopic", "Class_or_Struct");
1160 this->writeTag("Populate");
1161 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001162 this->lf(2);
1163 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001164 if (hasConst) {
1165 this->writeTag("Subtopic", "Constant");
1166 this->writeTag("Populate");
1167 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001168 this->lf(2);
1169 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001170 if (hasConstructor) {
1171 this->writeTag("Subtopic", "Constructor");
1172 this->writeTag("Populate");
1173 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001174 this->lf(2);
1175 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001176 if (hasOperator) {
1177 this->writeTag("Subtopic", "Operator");
1178 this->writeTag("Populate");
1179 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001180 this->lf(2);
1181 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001182 if (hasMember) {
1183 this->writeTag("Subtopic", "Member_Function");
1184 this->writeTag("Populate");
1185 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001186 this->lf(2);
1187 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001188 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001189 this->writeBlockSeparator();
1190 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001191 this->lf(2);
1192 this->writeTag("Example");
1193 this->lfcr();
1194 this->writeString("// incomplete");
1195 this->writeEndTag();
1196 this->lf(2);
1197 this->writeTag("SeeAlso", "incomplete");
1198 this->lf(2);
1199 this->writeEndTag("Enum", oneEnum.first);
1200 this->lf(2);
1201 }
Cary Clark8032b982017-07-28 11:04:54 -04001202 for (auto& oneClass : fIClassMap) {
1203 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1204 continue;
1205 }
1206 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001207 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001208 KeyWord keyword = oneClass.second.fKeyWord;
1209 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1210 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001211 this->writeTag(containerType, innerName);
1212 this->lf(2);
1213 this->writeTag("Code");
1214 this->writeEndTag("ToDo", "fill this in manually");
1215 this->writeEndTag();
1216 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001217 for (auto& token : oneClass.second.fTokens) {
1218 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1219 continue;
1220 }
Cary Clark9174bda2017-09-19 17:39:32 -04001221 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001222 }
1223 this->lf(2);
1224 this->dumpClassTokens(oneClass.second);
1225 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001226 this->writeEndTag(containerType, innerName);
1227 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001228 }
1229 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001230 this->writeEndTag(containerType, skClassName);
1231 this->lf(2);
1232 this->writeEndTag("Topic", topicName);
1233 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001234 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001235 SkDebugf("wrote %s\n", fileName.c_str());
1236 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001237}
1238
1239bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1240 // add comment preceding class, if any
1241 const Definition* parent = includeDef.fParent;
1242 int index = includeDef.fParentIndex;
1243 auto wordIter = parent->fTokens.begin();
1244 std::advance(wordIter, index);
1245 SkASSERT(&*wordIter == &includeDef);
1246 while (parent->fTokens.begin() != wordIter) {
1247 auto testIter = std::prev(wordIter);
1248 if (Definition::Type::kWord != testIter->fType
1249 && Definition::Type::kKeyWord != testIter->fType
1250 && (Definition::Type::kBracket != testIter->fType
1251 || Bracket::kAngle != testIter->fBracket)
1252 && (Definition::Type::kPunctuation != testIter->fType
1253 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1254 break;
1255 }
1256 wordIter = testIter;
1257 }
1258 auto commentIter = wordIter;
1259 while (parent->fTokens.begin() != commentIter) {
1260 auto testIter = std::prev(commentIter);
1261 bool isComment = Definition::Type::kBracket == testIter->fType
1262 && (Bracket::kSlashSlash == testIter->fBracket
1263 || Bracket::kSlashStar == testIter->fBracket);
1264 if (!isComment) {
1265 break;
1266 }
1267 commentIter = testIter;
1268 }
1269 while (commentIter != wordIter) {
1270 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1271 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1272 return false;
1273 }
1274 commentIter = std::next(commentIter);
1275 }
1276 return true;
1277}
1278
1279// caller calls reportError, so just return false here
1280bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1281 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001282 // parse class header
1283 auto iter = includeDef->fTokens.begin();
1284 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1285 // todo : documentation is ignoring this for now
1286 iter = std::next(iter);
1287 }
1288 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1289 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001290 iter = std::next(iter);
1291 if (iter == includeDef->fTokens.end()) {
1292 return true; // forward declaration only
1293 }
Cary Clark8032b982017-07-28 11:04:54 -04001294 do {
1295 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001296 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001297 }
1298 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1299 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001300 }
Cary Clark8032b982017-07-28 11:04:54 -04001301 } while (static_cast<void>(iter = std::next(iter)), true);
1302 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001303 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001304 }
1305 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1306 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001307 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001308 }
1309 markupDef->fStart = iter->fStart;
1310 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001311 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001312 }
1313// if (1 != includeDef->fChildren.size()) {
1314// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1315// }
1316 includeDef = includeDef->fChildren.front();
1317 iter = includeDef->fTokens.begin();
1318 // skip until public
1319 int publicIndex = 0;
1320 if (IsStruct::kNo == isStruct) {
1321 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1322 size_t publicLen = strlen(publicName);
1323 while (iter != includeDef->fTokens.end()
1324 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1325 || strncmp(iter->fStart, publicName, publicLen))) {
1326 iter = std::next(iter);
1327 ++publicIndex;
1328 }
1329 }
1330 auto childIter = includeDef->fChildren.begin();
1331 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1332 (*childIter)->fPrivate = true;
1333 childIter = std::next(childIter);
1334 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001335 int keyIndex = publicIndex;
1336 KeyWord currentKey = KeyWord::kPublic;
1337 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1338 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001339 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1340 size_t protectedLen = strlen(protectedName);
1341 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1342 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001343 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001344 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001345 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1346 const char* testStart = iter->fStart;
1347 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1348 iter = std::next(iter);
1349 ++keyIndex;
1350 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1351 currentKey = KeyWord::kPublic;
1352 break;
1353 }
1354 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1355 currentKey = KeyWord::kProtected;
1356 break;
1357 }
1358 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1359 currentKey = KeyWord::kPrivate;
1360 break;
1361 }
1362 }
1363 fLastObject = nullptr;
1364 if (KeyWord::kPublic == currentKey) {
1365 if (!this->parseObject(child, markupDef)) {
1366 return false;
1367 }
1368 } else {
1369 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001370 }
Cary Clark73fa9722017-08-29 17:36:51 -04001371 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001372 childIter = std::next(childIter);
1373 }
Cary Clark8032b982017-07-28 11:04:54 -04001374 SkASSERT(fParent->fParent);
1375 fParent = fParent->fParent;
1376 return true;
1377}
1378
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001379bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001380 int lineCount, Definition* markupDef) {
1381 TextParser parser(filename, start, end, lineCount);
1382 // parse doxygen if present
1383 if (parser.startsWith("**")) {
1384 parser.next();
1385 parser.next();
1386 parser.skipWhiteSpace();
1387 if ('\\' == parser.peek()) {
1388 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001389 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1390 if (parser.skipExact("file")) {
1391 if (Definition::Type::kFileType != fParent->fType) {
1392 return reportError<bool>("expected parent is file");
1393 }
1394 string filename = markupDef->fileName();
1395 if (!parser.skipWord(filename.c_str())) {
1396 return reportError<bool>("missing object type");
1397 }
1398 } else if (parser.skipExact("fn")) {
1399 SkASSERT(0); // incomplete
1400 } else {
1401 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1402 return reportError<bool>("missing object type");
1403 }
1404 if (!parser.skipWord(markupDef->fName.c_str()) &&
1405 KeyWord::kEnum != markupDef->fKeyWord) {
1406 return reportError<bool>("missing object name");
1407 }
Cary Clark8032b982017-07-28 11:04:54 -04001408 }
Cary Clark8032b982017-07-28 11:04:54 -04001409 }
1410 }
1411 // remove leading '*' if present
1412 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1413 while (!parser.eof() && parser.skipWhiteSpace()) {
1414 while ('*' == parser.peek()) {
1415 parser.next();
1416 if (parser.eof()) {
1417 break;
1418 }
1419 parser.skipWhiteSpace();
1420 }
1421 if (parser.eof()) {
1422 break;
1423 }
1424 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001425 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001426 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001427 parser.skipToEndBracket('\n');
1428 }
1429 return true;
1430}
1431
Cary Clarkd98f78c2018-04-26 08:32:37 -04001432bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
1433 // todo: hard code to constexpr for now
1434 TextParser constParser(child);
1435 if (!constParser.skipExact("static")) {
1436 return false;
1437 }
1438 constParser.skipWhiteSpace();
1439 if (!constParser.skipExact("constexpr")) {
1440 return false;
1441 }
1442 constParser.skipWhiteSpace();
1443 const char* typeStart = constParser.fChar;
1444 constParser.skipToSpace();
1445 KeyWord constType = FindKey(typeStart, constParser.fChar);
1446 if (KeyWord::kNone == constType) {
1447 // todo: this could be a non-keyword, ... do we need to look for type?
1448 return false;
1449 }
1450 constParser.skipWhiteSpace();
1451 const char* nameStart = constParser.fChar;
1452 constParser.skipToSpace();
1453 string nameStr = string(nameStart, constParser.fChar - nameStart);
1454 if (!markupDef) {
1455 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1456 child->fLineCount, fParent, '\0');
1457 Definition* globalMarkupChild = &fGlobals.back();
1458 string globalUniqueName = this->uniqueName(fIConstMap, nameStr);
1459 globalMarkupChild->fName = globalUniqueName;
1460 if (!this->findComments(*child, globalMarkupChild)) {
1461 return false;
1462 }
1463 fIConstMap[globalUniqueName] = globalMarkupChild;
1464 return true;
1465 }
1466 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1467 child->fLineCount, markupDef, '\0');
1468 Definition* markupChild = &markupDef->fTokens.back();
1469 markupChild->fName = nameStr;
1470 markupChild->fTerminator = markupChild->fContentEnd;
1471 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1472 classDef.fConsts[nameStr] = markupChild;
1473 return true;
1474}
1475
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001476bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1477 TextParser parser(child);
1478 if (!parser.skipExact("#define")) {
1479 return false;
1480 }
1481 if (!parser.skipSpace()) {
1482 return false;
1483 }
1484 const char* nameStart = parser.fChar;
1485 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1486 if (parser.eof()) {
1487 return true; // do nothing if #define doesn't define anything
1488 }
1489 string nameStr(nameStart, parser.fChar - nameStart);
1490 struct Param {
1491 const char* fStart;
1492 const char* fEnd;
1493 };
1494 vector<Param> params;
1495 if ('(' == parser.peek()) {
1496 parser.next();
1497 if (!parser.skipSpace()) {
1498 return false;
1499 }
1500 do {
1501 const char* paramStart = parser.fChar;
1502 if (!parser.skipExact("...")) {
1503 parser.skipToNonAlphaNum();
1504 }
1505 if (parser.eof()) {
1506 return false;
1507 }
1508 params.push_back({paramStart, parser.fChar});
1509 if (!parser.skipSpace()) {
1510 return false;
1511 }
1512 if (')' == parser.peek()) {
1513 parser.next();
1514 break;
1515 }
1516 if (',' != parser.next()) {
1517 return false;
1518 }
1519 if (!parser.skipSpace()) {
1520 return false;
1521 }
1522 } while (true);
1523 }
1524 if (!parser.skipSpace()) {
1525 return false;
1526 }
1527 if (!markupDef) {
1528 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1529 child->fLineCount, fParent, '\0');
1530 Definition* globalMarkupChild = &fGlobals.back();
1531 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1532 globalMarkupChild->fName = globalUniqueName;
1533 globalMarkupChild->fTerminator = child->fContentEnd;
1534 if (!this->findComments(*child, globalMarkupChild)) {
1535 return false;
1536 }
1537 fIDefineMap[globalUniqueName] = globalMarkupChild;
1538 for (Param param : params) {
1539 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1540 child->fLineCount, globalMarkupChild, '\0');
1541 Definition* paramChild = &globalMarkupChild->fTokens.back();
1542 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1543 paramChild->fTerminator = param.fEnd;
1544 }
1545 return true;
1546 }
1547 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1548 child->fLineCount, markupDef, '\0');
1549 Definition* markupChild = &markupDef->fTokens.back();
1550 markupChild->fName = nameStr;
1551 markupChild->fTerminator = markupChild->fContentEnd;
1552 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1553 if (!this->findComments(*child, markupChild)) {
1554 return false;
1555 }
1556 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001557 return true;
1558}
1559
1560bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001561 TextParser parser(child);
1562 parser.skipToEndBracket('{');
1563 if (parser.eof()) {
1564 return true; // if enum is a forward declaration, do nothing
1565 }
1566 parser.next();
1567 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001568 if (child->fTokens.size() > 0) {
1569 auto token = child->fTokens.begin();
1570 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1571 token = token->fTokens.begin();
1572 }
1573 if (Definition::Type::kWord == token->fType) {
1574 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1575 }
1576 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001577 Definition* markupChild;
1578 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001579 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1580 child->fLineCount, fParent, '\0');
1581 markupChild = &fGlobals.back();
1582 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1583 markupChild->fName = globalUniqueName;
1584 markupChild->fTerminator = child->fContentEnd;
1585 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001586 } else {
1587 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001588 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001589 markupChild = &markupDef->fTokens.back();
1590 }
Cary Clark8032b982017-07-28 11:04:54 -04001591 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1592 markupChild->fKeyWord = KeyWord::kEnum;
1593 TextParser enumName(child);
1594 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001595 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001596 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001597 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001598 markupChild->fMarkType = MarkType::kEnumClass;
1599 }
Cary Clark8032b982017-07-28 11:04:54 -04001600 const char* nameStart = enumName.fChar;
1601 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001602 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001603 markupChild->fName = markupDef->fName + "::" +
1604 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001605 }
Cary Clark8032b982017-07-28 11:04:54 -04001606 if (!this->findComments(*child, markupChild)) {
1607 return false;
1608 }
Cary Clark8032b982017-07-28 11:04:54 -04001609 const char* dataEnd;
1610 do {
Cary Clark8032b982017-07-28 11:04:54 -04001611 parser.skipWhiteSpace();
1612 if ('}' == parser.peek()) {
1613 break;
1614 }
1615 Definition* comment = nullptr;
1616 // note that comment, if any, can be before or after (on the same line, though) as member
1617 if ('#' == parser.peek()) {
1618 // fixme: handle preprecessor, but just skip it for now
1619 parser.skipToLineStart();
1620 }
1621 while (parser.startsWith("/*") || parser.startsWith("//")) {
1622 parser.next();
1623 const char* start = parser.fChar;
1624 const char* end;
1625 if ('*' == parser.peek()) {
1626 end = parser.strnstr("*/", parser.fEnd);
1627 parser.fChar = end;
1628 parser.next();
1629 parser.next();
1630 } else {
1631 end = parser.trimmedLineEnd();
1632 parser.skipToLineStart();
1633 }
1634 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001635 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001636 comment = &markupChild->fTokens.back();
1637 comment->fTerminator = end;
1638 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1639 return false;
1640 }
1641 parser.skipWhiteSpace();
1642 }
1643 parser.skipWhiteSpace();
1644 const char* memberStart = parser.fChar;
1645 if ('}' == memberStart[0]) {
1646 break;
1647 }
Cary Clark9174bda2017-09-19 17:39:32 -04001648 // if there's comment on same the line as member def, output first as if it was before
1649
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001650 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001651 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001652 if (parser.eof() || !parser.skipWhiteSpace()) {
1653 return this->reportError<bool>("enum member must end with comma 1");
1654 }
Cary Clark8032b982017-07-28 11:04:54 -04001655 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001656 if ('=' == parser.peek()) {
1657 parser.skipToEndBracket(',');
1658 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001659 if (!parser.eof() && '#' == parser.peek()) {
1660 // fixme: handle preprecessor, but just skip it for now
1661 continue;
1662 }
Cary Clark9174bda2017-09-19 17:39:32 -04001663 if (parser.eof() || ',' != parser.peek()) {
1664 return this->reportError<bool>("enum member must end with comma 2");
1665 }
1666 dataEnd = parser.fChar;
1667 const char* start = parser.anyOf("/\n");
1668 SkASSERT(start);
1669 parser.skipTo(start);
1670 if ('/' == parser.next()) {
1671 char slashStar = parser.next();
1672 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001673 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001674 char doxCheck = parser.next();
1675 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1676 save.restore();
1677 }
1678 }
1679 parser.skipWhiteSpace();
1680 const char* commentStart = parser.fChar;
1681 if ('/' == slashStar) {
1682 parser.skipToEndBracket('\n');
1683 } else {
1684 parser.skipToEndBracket("*/");
1685 }
1686 SkASSERT(!parser.eof());
1687 const char* commentEnd = parser.fChar;
1688 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001689 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001690 comment = &markupChild->fTokens.back();
1691 comment->fTerminator = commentEnd;
1692 }
Cary Clark8032b982017-07-28 11:04:54 -04001693 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001694 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001695 Definition* member = &markupChild->fTokens.back();
1696 member->fName = memberName;
1697 if (comment) {
1698 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001699 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001700 }
1701 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001702 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001703 for (auto outsideMember : child->fChildren) {
1704 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001705 continue;
1706 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001707 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1708 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001709 continue;
1710 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001711 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1712 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001713 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001714 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001715 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001716 // FIXME: ? add comment as well ?
1717 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001718 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001719 if (markupDef) {
1720 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1721 SkASSERT(classDef.fStart);
1722 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1723 markupChild->fName = uniqueName;
1724 classDef.fEnums[uniqueName] = markupChild;
1725 }
Cary Clark8032b982017-07-28 11:04:54 -04001726 return true;
1727}
1728
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001729bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001730 fParent = &fIncludeMap[name];
1731 fParent->fName = name;
1732 fParent->fFileName = fFileName;
1733 fParent->fType = Definition::Type::kFileType;
1734 fParent->fContentStart = fChar;
1735 fParent->fContentEnd = fEnd;
1736 // parse include file into tree
1737 while (fChar < fEnd) {
1738 if (!this->parseChar()) {
1739 return false;
1740 }
1741 }
1742 // parse tree and add named objects to maps
1743 fParent = &fIncludeMap[name];
1744 if (!this->parseObjects(fParent, nullptr)) {
1745 return false;
1746 }
1747 return true;
1748}
1749
1750bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1751 const char* typeStart = child->fChildren[0]->fContentStart;
1752 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001753 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001754 Definition* markupChild = &markupDef->fTokens.back();
1755 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001756 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001757 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1758 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1759 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1760 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001761 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001762 classDef.fMembers[uniqueName] = markupChild;
1763 if (child->fParentIndex >= 2) {
1764 auto comment = child->fParent->fTokens.begin();
1765 std::advance(comment, child->fParentIndex - 2);
1766 if (Definition::Type::kBracket == comment->fType
1767 && (Bracket::kSlashStar == comment->fBracket
1768 || Bracket::kSlashSlash == comment->fBracket)) {
1769 TextParser parser(&*comment);
1770 do {
1771 parser.skipToAlpha();
1772 if (parser.eof()) {
1773 break;
1774 }
1775 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001776 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001777 if (Bracket::kSlashStar == comment->fBracket) {
1778 const char* commentEnd = parser.strnstr("*/", end);
1779 if (commentEnd) {
1780 end = commentEnd;
1781 }
1782 }
1783 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001784 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001785 Definition* commentChild = &markupDef->fTokens.back();
1786 markupChild->fChildren.emplace_back(commentChild);
1787 parser.skipTo(end);
1788 } while (!parser.eof());
1789 }
1790 }
1791 return true;
1792}
1793
1794bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1795 auto tokenIter = child->fParent->fTokens.begin();
1796 std::advance(tokenIter, child->fParentIndex);
1797 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001798 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001799 bool addConst = false;
1800 auto operatorCheck = tokenIter;
1801 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1802 operatorCheck = std::prev(tokenIter);
1803 }
1804 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001805 auto closeParen = std::next(tokenIter);
1806 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1807 '(' == closeParen->fContentStart[0]);
1808 nameEnd = closeParen->fContentEnd + 1;
1809 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001810 if (Definition::Type::kKeyWord == closeParen->fType &&
1811 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001812 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001813 }
Cary Clarka560c472017-11-27 10:44:06 -05001814 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001815 }
1816 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001817 if (addConst) {
1818 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001819 }
Cary Clark8032b982017-07-28 11:04:54 -04001820 while (tokenIter != child->fParent->fTokens.begin()) {
1821 auto testIter = std::prev(tokenIter);
1822 switch (testIter->fType) {
1823 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001824 if (testIter == child->fParent->fTokens.begin() &&
1825 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1826 KeyWord::kIfndef == child->fParent->fKeyWord ||
1827 KeyWord::kIf == child->fParent->fKeyWord)) {
1828 std::next(tokenIter);
1829 break;
1830 }
Cary Clark8032b982017-07-28 11:04:54 -04001831 goto keepGoing;
1832 case Definition::Type::kKeyWord: {
1833 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1834 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1835 goto keepGoing;
1836 }
1837 } break;
1838 case Definition::Type::kBracket:
1839 if (Bracket::kAngle == testIter->fBracket) {
1840 goto keepGoing;
1841 }
1842 break;
1843 case Definition::Type::kPunctuation:
1844 if (Punctuation::kSemicolon == testIter->fPunctuation
1845 || Punctuation::kLeftBrace == testIter->fPunctuation
1846 || Punctuation::kColon == testIter->fPunctuation) {
1847 break;
1848 }
1849 keepGoing:
1850 tokenIter = testIter;
1851 continue;
1852 default:
1853 break;
1854 }
1855 break;
1856 }
1857 tokenIter->fName = nameStr;
1858 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001859 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001860 auto testIter = child->fParent->fTokens.begin();
1861 SkASSERT(child->fParentIndex > 0);
1862 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001863 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1864 0 == tokenIter->fParentIndex) {
1865 tokenIter = std::next(tokenIter);
1866 }
Cary Clark8032b982017-07-28 11:04:54 -04001867 const char* start = tokenIter->fContentStart;
1868 const char* end = tokenIter->fContentEnd;
1869 const char kDebugCodeStr[] = "SkDEBUGCODE";
1870 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1871 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1872 std::advance(testIter, 1);
1873 start = testIter->fContentStart + 1;
1874 end = testIter->fContentEnd - 1;
1875 } else {
1876 end = testIter->fContentEnd;
1877 while (testIter != child->fParent->fTokens.end()) {
1878 testIter = std::next(testIter);
1879 switch (testIter->fType) {
1880 case Definition::Type::kPunctuation:
1881 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1882 || Punctuation::kLeftBrace == testIter->fPunctuation
1883 || Punctuation::kColon == testIter->fPunctuation);
1884 end = testIter->fStart;
1885 break;
1886 case Definition::Type::kKeyWord: {
1887 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1888 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1889 continue;
1890 }
1891 } break;
1892 default:
1893 continue;
1894 }
1895 break;
1896 }
1897 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001898 while (end > start && ' ' >= end[-1]) {
1899 --end;
1900 }
Cary Clark9174bda2017-09-19 17:39:32 -04001901 if (!markupDef) {
1902 auto parentIter = child->fParent->fTokens.begin();
1903 SkASSERT(child->fParentIndex > 0);
1904 std::advance(parentIter, child->fParentIndex - 1);
1905 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001906 TextParser nameParser(methodName);
1907 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001908 return true; // expect this is inline class definition outside of class
1909 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001910 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1911 fParent, '\0');
1912 Definition* globalMarkupChild = &fGlobals.back();
1913 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1914 globalMarkupChild->fName = globalUniqueName;
1915 if (!this->findComments(*child, globalMarkupChild)) {
1916 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001917 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001918 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001919 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001920 }
Cary Clark8032b982017-07-28 11:04:54 -04001921 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001922 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001923 Definition* markupChild = &markupDef->fTokens.back();
1924 // do find instead -- I wonder if there is a way to prevent this in c++
1925 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1926 SkASSERT(classDef.fStart);
1927 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1928 markupChild->fName = uniqueName;
1929 if (!this->findComments(*child, markupChild)) {
1930 return false;
1931 }
1932 classDef.fMethods[uniqueName] = markupChild;
1933 return true;
1934}
1935
Cary Clark8032b982017-07-28 11:04:54 -04001936bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1937 for (auto& child : parent->fChildren) {
1938 if (!this->parseObject(child, markupDef)) {
1939 return false;
1940 }
1941 }
1942 return true;
1943}
1944
1945bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1946 // set up for error reporting
1947 fLine = fChar = child->fStart;
1948 fEnd = child->fContentEnd;
1949 // todo: put original line number in child as well
1950 switch (child->fType) {
1951 case Definition::Type::kKeyWord:
1952 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001953 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001954 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001955 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001956 }
1957 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001958 case KeyWord::kStatic:
1959 if (!this->parseConst(child, markupDef)) {
1960 return child->reportError<bool>("failed to parse const or constexpr");
1961 }
1962 break;
Cary Clark8032b982017-07-28 11:04:54 -04001963 case KeyWord::kEnum:
1964 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001965 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001966 }
1967 break;
1968 case KeyWord::kStruct:
1969 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001970 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001971 }
1972 break;
1973 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001974 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001975 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001976 }
1977 break;
1978 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001979 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001980 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001981 }
1982 break;
1983 case KeyWord::kUnion:
1984 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001985 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001986 }
1987 break;
1988 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001989 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001990 }
1991 break;
1992 case Definition::Type::kBracket:
1993 switch (child->fBracket) {
1994 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001995 if (fLastObject) {
1996 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1997 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001998 if (!checkDeprecated.eof()) {
1999 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002000 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2001 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002002 break;
2003 }
2004 }
2005 }
2006 {
2007 auto tokenIter = child->fParent->fTokens.begin();
2008 std::advance(tokenIter, child->fParentIndex);
2009 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002010 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002011 if (previousToken.startsWith(gAttrDeprecated)) {
2012 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002013 break;
2014 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002015 if (Bracket::kPound == child->fParent->fBracket &&
2016 KeyWord::kIf == child->fParent->fKeyWord) {
2017 // TODO: this will skip methods named defined() -- for the
2018 // moment there aren't any
2019 if (previousToken.startsWith("defined")) {
2020 break;
2021 }
2022 }
Cary Clark73fa9722017-08-29 17:36:51 -04002023 }
Cary Clark8032b982017-07-28 11:04:54 -04002024 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002025 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002026 }
Cary Clark89b14562018-03-19 09:04:10 -04002027 if (fAttrDeprecated) {
2028 Definition* lastMethod = &markupDef->fTokens.back();
2029 lastMethod->fDeprecated = true;
2030 fAttrDeprecated = nullptr;
2031 }
Cary Clark73fa9722017-08-29 17:36:51 -04002032 break;
Cary Clark8032b982017-07-28 11:04:54 -04002033 case Bracket::kSlashSlash:
2034 case Bracket::kSlashStar:
2035 // comments are picked up by parsing objects first
2036 break;
2037 case Bracket::kPound:
2038 // special-case the #xxx xxx_DEFINED entries
2039 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002040 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002041 case KeyWord::kIfndef:
2042 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002043 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002044 if (!this->parseObjects(child, markupDef)) {
2045 return false;
2046 }
2047 break;
2048 }
2049 goto preproError;
2050 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002051 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002052 break;
2053 }
2054 goto preproError;
2055 case KeyWord::kEndif:
2056 if (child->boilerplateEndIf()) {
2057 break;
2058 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002059 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002060 case KeyWord::kInclude:
2061 // ignored for now
2062 break;
2063 case KeyWord::kElse:
2064 case KeyWord::kElif:
2065 // todo: handle these
2066 break;
2067 default:
2068 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002069 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002070 }
2071 break;
2072 case Bracket::kAngle:
2073 // pick up templated function pieces when method is found
2074 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002075 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002076 if (!this->parseObjects(child, markupDef)) {
2077 return false;
2078 }
Cary Clark73fa9722017-08-29 17:36:51 -04002079 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002080 case Bracket::kSquare: {
2081 // check to see if parent is operator, the only case we handle so far
2082 auto prev = child->fParent->fTokens.begin();
2083 std::advance(prev, child->fParentIndex - 1);
2084 if (KeyWord::kOperator != prev->fKeyWord) {
2085 return child->reportError<bool>("expected operator overload");
2086 }
2087 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002088 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002089 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002090 }
2091 break;
2092 case Definition::Type::kWord:
2093 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002094 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002095 }
2096 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002097 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002098 }
2099 break;
2100 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002101 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002102 break;
2103 }
2104 return true;
2105}
2106
Cary Clarkbbfda252018-03-09 15:32:01 -05002107bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2108 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002109}
2110
Cary Clark2f466242017-12-11 16:03:17 -05002111bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2112 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002113 typedefParser.skipExact("typedef");
2114 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002115 string nameStr = typedefParser.typedefName();
2116 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002117 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2118 child->fLineCount, fParent, '\0');
2119 Definition* globalMarkupChild = &fGlobals.back();
2120 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2121 globalMarkupChild->fName = globalUniqueName;
2122 if (!this->findComments(*child, globalMarkupChild)) {
2123 return false;
2124 }
2125 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark2f466242017-12-11 16:03:17 -05002126 return true;
2127 }
2128 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002129 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002130 Definition* markupChild = &markupDef->fTokens.back();
2131 markupChild->fName = nameStr;
2132 markupChild->fTerminator = markupChild->fContentEnd;
2133 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2134 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002135 return true;
2136}
2137
2138bool IncludeParser::parseUnion() {
2139
2140 return true;
2141}
2142
2143bool IncludeParser::parseChar() {
2144 char test = *fChar;
2145 if ('\\' == fPrev) {
2146 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002147// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002148 fLine = fChar + 1;
2149 }
2150 goto done;
2151 }
2152 switch (test) {
2153 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002154// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002155 fLine = fChar + 1;
2156 if (fInChar) {
2157 return reportError<bool>("malformed char");
2158 }
2159 if (fInString) {
2160 return reportError<bool>("malformed string");
2161 }
2162 if (!this->checkForWord()) {
2163 return false;
2164 }
2165 if (Bracket::kPound == this->topBracket()) {
2166 KeyWord keyWord = fParent->fKeyWord;
2167 if (KeyWord::kNone == keyWord) {
2168 return this->reportError<bool>("unhandled preprocessor directive");
2169 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002170 if (fInDefine) {
2171 SkASSERT(KeyWord::kDefine == keyWord);
2172 fInDefine = false;
2173 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002174 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002175 this->popBracket();
2176 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002177 if (fInBrace) {
2178 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2179 fInBrace = nullptr;
2180 }
Cary Clark8032b982017-07-28 11:04:54 -04002181 } else if (Bracket::kSlashSlash == this->topBracket()) {
2182 this->popBracket();
2183 }
2184 break;
2185 case '*':
2186 if (!fInCharCommentString && '/' == fPrev) {
2187 this->pushBracket(Bracket::kSlashStar);
2188 }
2189 if (!this->checkForWord()) {
2190 return false;
2191 }
2192 if (!fInCharCommentString) {
2193 this->addPunctuation(Punctuation::kAsterisk);
2194 }
2195 break;
2196 case '/':
2197 if ('*' == fPrev) {
2198 if (!fInCharCommentString) {
2199 return reportError<bool>("malformed closing comment");
2200 }
2201 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002202 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002203 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002204 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002205 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002206 }
2207 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002208 }
Cary Clark8032b982017-07-28 11:04:54 -04002209 if (!fInCharCommentString && '/' == fPrev) {
2210 this->pushBracket(Bracket::kSlashSlash);
2211 break;
2212 }
2213 if (!this->checkForWord()) {
2214 return false;
2215 }
2216 break;
2217 case '\'':
2218 if (Bracket::kChar == this->topBracket()) {
2219 this->popBracket();
2220 } else if (!fInComment && !fInString) {
2221 if (fIncludeWord) {
2222 return this->reportError<bool>("word then single-quote");
2223 }
2224 this->pushBracket(Bracket::kChar);
2225 }
2226 break;
2227 case '\"':
2228 if (Bracket::kString == this->topBracket()) {
2229 this->popBracket();
2230 } else if (!fInComment && !fInChar) {
2231 if (fIncludeWord) {
2232 return this->reportError<bool>("word then double-quote");
2233 }
2234 this->pushBracket(Bracket::kString);
2235 }
2236 break;
Cary Clark8032b982017-07-28 11:04:54 -04002237 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002238 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002239 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2240 this->pushBracket(Bracket::kDebugCode);
2241 break;
2242 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002243 case ':':
2244 case '[':
2245 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002246 if (fInCharCommentString) {
2247 break;
2248 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002249 if (fInDefine && fInBrace) {
2250 break;
2251 }
Cary Clark8032b982017-07-28 11:04:54 -04002252 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2253 break;
2254 }
2255 if (!fInBrace) {
2256 if (!this->checkForWord()) {
2257 return false;
2258 }
2259 if (':' == test && !fInFunction) {
2260 break;
2261 }
2262 if ('{' == test) {
2263 this->addPunctuation(Punctuation::kLeftBrace);
2264 } else if (':' == test) {
2265 this->addPunctuation(Punctuation::kColon);
2266 }
2267 }
2268 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2269 && Bracket::kColon == fInBrace->fBracket) {
2270 Definition* braceParent = fParent->fParent;
2271 braceParent->fChildren.pop_back();
2272 braceParent->fTokens.pop_back();
2273 fParent = braceParent;
2274 fInBrace = nullptr;
2275 }
2276 this->pushBracket(
2277 '(' == test ? Bracket::kParen :
2278 '[' == test ? Bracket::kSquare :
2279 '{' == test ? Bracket::kBrace :
2280 Bracket::kColon);
2281 if (!fInBrace
2282 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2283 && fInFunction) {
2284 fInBrace = fParent;
2285 }
2286 } break;
2287 case '<':
2288 if (fInCharCommentString || fInBrace) {
2289 break;
2290 }
2291 if (!this->checkForWord()) {
2292 return false;
2293 }
2294 if (fInEnum) {
2295 break;
2296 }
2297 this->pushBracket(Bracket::kAngle);
2298 break;
2299 case ')':
2300 case ']':
2301 case '}': {
2302 if (fInCharCommentString) {
2303 break;
2304 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002305 if (fInDefine && fInBrace) {
2306 break;
2307 }
Cary Clark8032b982017-07-28 11:04:54 -04002308 if (!fInBrace) {
2309 if (!this->checkForWord()) {
2310 return false;
2311 }
2312 }
2313 bool popBraceParent = fInBrace == fParent;
2314 if ((')' == test ? Bracket::kParen :
2315 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2316 this->popBracket();
2317 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002318 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002319 } else {
2320 fInFunction = '}' != test;
2321 }
Cary Clark73fa9722017-08-29 17:36:51 -04002322 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2323 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002324 } else {
2325 return reportError<bool>("malformed close bracket");
2326 }
2327 if (popBraceParent) {
2328 Definition* braceParent = fInBrace->fParent;
2329 braceParent->fChildren.pop_back();
2330 braceParent->fTokens.pop_back();
2331 fInBrace = nullptr;
2332 }
2333 } break;
2334 case '>':
2335 if (fInCharCommentString || fInBrace) {
2336 break;
2337 }
2338 if (!this->checkForWord()) {
2339 return false;
2340 }
2341 if (fInEnum) {
2342 break;
2343 }
Cary Clarka560c472017-11-27 10:44:06 -05002344 if (Bracket::kPound == this->topBracket()) {
2345 break;
2346 }
Cary Clark8032b982017-07-28 11:04:54 -04002347 if (Bracket::kAngle == this->topBracket()) {
2348 this->popBracket();
2349 } else {
2350 return reportError<bool>("malformed close angle bracket");
2351 }
2352 break;
2353 case '#': {
2354 if (fInCharCommentString || fInBrace) {
2355 break;
2356 }
2357 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2358 this->pushBracket(Bracket::kPound);
2359 break;
2360 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002361 case ' ':
2362 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2363 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2364 fInBrace = fParent;
2365 // delimiting brackets are space ... unescaped-linefeed
2366 }
Cary Clark8032b982017-07-28 11:04:54 -04002367 case '&':
2368 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002369 case '+':
2370 case '=':
2371 case '-':
2372 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002373 if (fInCharCommentString || fInBrace) {
2374 break;
2375 }
2376 if (!this->checkForWord()) {
2377 return false;
2378 }
2379 break;
2380 case ';':
2381 if (fInCharCommentString || fInBrace) {
2382 break;
2383 }
2384 if (!this->checkForWord()) {
2385 return false;
2386 }
2387 if (Definition::Type::kKeyWord == fParent->fType
2388 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002389 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2390 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002391 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2392 this->popObject();
2393 }
Cary Clark8032b982017-07-28 11:04:54 -04002394 if (KeyWord::kEnum == fParent->fKeyWord) {
2395 fInEnum = false;
2396 }
2397 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002398 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2399 this->popObject();
2400 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002401 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002402 } else if (Definition::Type::kBracket == fParent->fType
2403 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2404 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2405 list<Definition>::iterator baseIter = fParent->fTokens.end();
2406 list<Definition>::iterator namedIter = fParent->fTokens.end();
2407 for (auto tokenIter = fParent->fTokens.end();
2408 fParent->fTokens.begin() != tokenIter--; ) {
2409 if (tokenIter->fLineCount == fLineCount) {
2410 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2411 if (namedIter != fParent->fTokens.end()) {
2412 return reportError<bool>("found two named member tokens");
2413 }
2414 namedIter = tokenIter;
2415 }
2416 baseIter = tokenIter;
2417 } else {
2418 break;
2419 }
2420 }
2421 // FIXME: if a member definition spans multiple lines, this won't work
2422 if (namedIter != fParent->fTokens.end()) {
2423 if (baseIter == namedIter) {
2424 return this->reportError<bool>("expected type before named token");
2425 }
2426 Definition* member = &*namedIter;
2427 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002428 if (!member->fTerminator) {
2429 member->fTerminator = member->fContentEnd;
2430 }
Cary Clark8032b982017-07-28 11:04:54 -04002431 fParent->fChildren.push_back(member);
2432 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2433 member->fChildren.push_back(&*nameType);
2434 }
Cary Clark8032b982017-07-28 11:04:54 -04002435 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002436 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002437 } else if (fParent->fChildren.size() > 0) {
2438 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002439 Definition* priorEnum = fPriorEnum;
2440 fPriorEnum = nullptr;
2441 if (!priorEnum) {
2442 while (fParent->fChildren.begin() != lastIter) {
2443 std::advance(lastIter, -1);
2444 priorEnum = *lastIter;
2445 if (Definition::Type::kBracket != priorEnum->fType ||
2446 (Bracket::kSlashSlash != priorEnum->fBracket
2447 && Bracket::kSlashStar != priorEnum->fBracket)) {
2448 break;
2449 }
Cary Clark8032b982017-07-28 11:04:54 -04002450 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002451 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002452 }
2453 if (Definition::Type::kKeyWord == priorEnum->fType
2454 && KeyWord::kEnum == priorEnum->fKeyWord) {
2455 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002456 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002457 while (tokenWalker != fParent->fTokens.end()) {
2458 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002459 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002460 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2461 break;
2462 }
2463 }
2464 while (tokenWalker != fParent->fTokens.end()) {
2465 std::advance(tokenWalker, 1);
2466 const Definition* test = &*tokenWalker;
2467 if (Definition::Type::kBracket != test->fType ||
2468 (Bracket::kSlashSlash != test->fBracket
2469 && Bracket::kSlashStar != test->fBracket)) {
2470 break;
2471 }
2472 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002473 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002474 Definition* start = &*tokenWalker;
2475 bool foundExpected = true;
2476 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2477 const Definition* test = &*tokenWalker;
2478 if (expected != test->fKeyWord) {
2479 foundExpected = false;
2480 break;
2481 }
2482 if (tokenWalker == fParent->fTokens.end()) {
2483 break;
2484 }
2485 std::advance(tokenWalker, 1);
2486 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002487 if (!foundExpected) {
2488 foundExpected = true;
2489 tokenWalker = saveTokenWalker;
2490 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2491 const Definition* test = &*tokenWalker;
2492 if (expected != test->fKeyWord) {
2493 foundExpected = false;
2494 break;
2495 }
2496 if (tokenWalker == fParent->fTokens.end()) {
2497 break;
2498 }
2499 if (KeyWord::kNone != expected) {
2500 std::advance(tokenWalker, 1);
2501 }
2502 }
2503 if (foundExpected) {
2504 auto nameToken = priorEnum->fTokens.begin();
2505 string enumName = string(nameToken->fContentStart,
2506 nameToken->fContentEnd - nameToken->fContentStart);
2507 const Definition* test = &*tokenWalker;
2508 string constType = string(test->fContentStart,
2509 test->fContentEnd - test->fContentStart);
2510 if (enumName != constType) {
2511 foundExpected = false;
2512 } else {
2513 std::advance(tokenWalker, 1);
2514 }
2515 }
2516 }
Cary Clark8032b982017-07-28 11:04:54 -04002517 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2518 const char* nameStart = tokenWalker->fStart;
2519 std::advance(tokenWalker, 1);
2520 if (tokenWalker != fParent->fTokens.end()) {
2521 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002522 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002523 start->fName = string(nameStart, tp.fChar - nameStart);
2524 start->fContentEnd = fChar;
2525 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002526 fPriorEnum = priorEnum;
2527 }
Cary Clark8032b982017-07-28 11:04:54 -04002528 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04002529 } else { // check for static constexpr not following an enum
2530 // find first token on line
2531 auto backTokenWalker = fParent->fTokens.end();
2532 while (fParent->fTokens.begin() != backTokenWalker
2533 && (fParent->fTokens.end() == backTokenWalker
2534 || backTokenWalker->fStart > fLine)) {
2535 std::advance(backTokenWalker, -1);
2536 }
2537 if (fParent->fTokens.end() != backTokenWalker
2538 && backTokenWalker->fStart < fLine) {
2539 std::advance(backTokenWalker, 1);
2540 }
2541 // look for static constexpr
2542 Definition* start = &*backTokenWalker;
2543 bool foundExpected = true;
2544 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr}){
2545 const Definition* test = &*backTokenWalker;
2546 if (expected != test->fKeyWord) {
2547 foundExpected = false;
2548 break;
2549 }
2550 if (backTokenWalker == fParent->fTokens.end()) {
2551 break;
2552 }
2553 std::advance(backTokenWalker, 1);
2554 }
2555 if (foundExpected) {
2556 std::advance(backTokenWalker, 1);
2557 const char* nameStart = backTokenWalker->fStart;
2558 std::advance(backTokenWalker, 1);
2559 TextParser parser(fFileName, nameStart, backTokenWalker->fStart, fLineCount);
2560 parser.skipToNonAlphaNum();
2561 start->fMarkType = MarkType::kConst;
2562 start->fName = string(nameStart, parser.fChar - nameStart);
2563 start->fContentEnd = backTokenWalker->fContentEnd;
2564 fParent->fChildren.emplace_back(start);
2565 }
Cary Clark8032b982017-07-28 11:04:54 -04002566 }
2567 }
2568 this->addPunctuation(Punctuation::kSemicolon);
2569 fInFunction = false;
2570 break;
2571 case '~':
2572 if (fInEnum) {
2573 break;
2574 }
2575 case '0': case '1': case '2': case '3': case '4':
2576 case '5': case '6': case '7': case '8': case '9':
2577 // TODO: don't want to parse numbers, but do need to track for enum defs
2578 // break;
2579 case 'A': case 'B': case 'C': case 'D': case 'E':
2580 case 'F': case 'G': case 'H': case 'I': case 'J':
2581 case 'K': case 'L': case 'M': case 'N': case 'O':
2582 case 'P': case 'Q': case 'R': case 'S': case 'T':
2583 case 'U': case 'V': case 'W': case 'X': case 'Y':
2584 case 'Z': case '_':
2585 case 'a': case 'b': case 'c': case 'd': case 'e':
2586 case 'f': case 'g': case 'h': case 'i': case 'j':
2587 case 'k': case 'l': case 'm': case 'n': case 'o':
2588 case 'p': case 'q': case 'r': case 's': case 't':
2589 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002590 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002591 if (fInCharCommentString || fInBrace) {
2592 break;
2593 }
2594 if (!fIncludeWord) {
2595 fIncludeWord = fChar;
2596 }
2597 break;
2598 }
2599done:
2600 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002601 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002602 return true;
2603}
2604
2605void IncludeParser::validate() const {
2606 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2607 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2608 }
2609 IncludeParser::ValidateKeyWords();
2610}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002611
Cary Clark186d08f2018-04-03 08:43:27 -04002612bool IncludeParser::references(const SkString& file) const {
2613 // if includes weren't passed one at a time, assume all references are valid
2614 if (fIncludeMap.empty()) {
2615 return true;
2616 }
2617 SkASSERT(file.endsWith(".bmh") );
2618 string root(file.c_str(), file.size() - 4);
2619 string kReference("_Reference");
2620 if (string::npos != root.find(kReference)) {
2621 root = root.substr(0, root.length() - kReference.length());
2622 }
2623 if (fIClassMap.end() != fIClassMap.find(root)) {
2624 return true;
2625 }
2626 if (fIStructMap.end() != fIStructMap.find(root)) {
2627 return true;
2628 }
2629 // TODO incomplete: probably need to look in other places for class-less includes like SkColor.h
2630 return false;
2631}
2632
Cary Clark2dc84ad2018-01-26 12:56:22 -05002633void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2634 if (!sk_isdir(includes)) {
2635 IncludeParser::RemoveOneFile(docs, includes);
2636 } else {
2637 SkOSFile::Iter it(includes, ".h");
2638 for (SkString file; it.next(&file); ) {
2639 SkString p = SkOSPath::Join(includes, file.c_str());
2640 const char* hunk = p.c_str();
2641 if (!SkStrEndsWith(hunk, ".h")) {
2642 continue;
2643 }
2644 IncludeParser::RemoveOneFile(docs, hunk);
2645 }
2646 }
2647}
2648
2649void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2650 const char* lastForward = strrchr(includesFile, '/');
2651 const char* lastBackward = strrchr(includesFile, '\\');
2652 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2653 if (!last) {
2654 last = includesFile;
2655 } else {
2656 last += 1;
2657 }
2658 SkString baseName(last);
2659 SkASSERT(baseName.endsWith(".h"));
2660 baseName.remove(baseName.size() - 2, 2);
2661 baseName.append("_Reference.bmh");
2662 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2663 remove(fullName.c_str());
2664}