blob: fc378ddb45bb6e7dbec63f73e95a8422e946d7f4 [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 Clark0d225392018-06-07 09:59:07 -040049 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040050 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040051 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040052 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
53 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040054 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040055 { "union", KeyWord::kUnion, KeyProperty::kObject },
56 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
57 { "void", KeyWord::kVoid, KeyProperty::kNumber },
58};
59
60const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
61
62KeyWord IncludeParser::FindKey(const char* start, const char* end) {
63 int ch = 0;
64 for (size_t index = 0; index < kKeyWordCount; ) {
65 if (start[ch] > kKeyWords[index].fName[ch]) {
66 ++index;
67 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
68 return KeyWord::kNone;
69 }
70 continue;
71 }
72 if (start[ch] < kKeyWords[index].fName[ch]) {
73 return KeyWord::kNone;
74 }
75 ++ch;
76 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040077 if (end - start < (int) strlen(kKeyWords[index].fName)) {
78 return KeyWord::kNone;
79 }
Cary Clark8032b982017-07-28 11:04:54 -040080 return kKeyWords[index].fKeyWord;
81 }
82 }
83 return KeyWord::kNone;
84}
85
86void IncludeParser::ValidateKeyWords() {
87 for (size_t index = 1; index < kKeyWordCount; ++index) {
88 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
89 == (int) kKeyWords[index].fKeyWord);
90 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
91 }
92}
93
94void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050095 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040096 fIncludeWord = nullptr;
97 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
98 Definition* def = &fParent->fTokens.back();
99 this->addDefinition(def);
100 if (KeyWord::kEnum == fParent->fKeyWord) {
101 fInEnum = true;
102 }
103 }
104}
105
Ben Wagner63fd7602017-10-09 15:45:33 -0400106void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400107 const vector<string>& foundParams) {
108 for (auto& methodParam : methodParams) {
109 bool found = false;
110 for (auto& foundParam : foundParams) {
111 if (methodParam == foundParam) {
112 found = true;
113 break;
114 }
115 }
116 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400117 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400118 }
119 }
120 for (auto& foundParam : foundParams) {
121 bool found = false;
122 for (auto& methodParam : methodParams) {
123 if (methodParam == foundParam) {
124 found = true;
125 break;
126 }
127 }
128 if (!found) {
129 this->reportError("doxygen param does not match method declaration");
130 }
131 }
132}
133
134bool IncludeParser::checkForWord() {
135 if (!fIncludeWord) {
136 return true;
137 }
138 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400139 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
140 Bracket bracket = this->topBracket();
141 if (Bracket::kParen == bracket) {
142 return true;
143 }
144 }
Cary Clark8032b982017-07-28 11:04:54 -0400145 if (KeyWord::kNone != keyWord) {
146 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
147 this->addKeyword(keyWord);
148 return true;
149 }
150 } else {
151 this->addWord();
152 return true;
153 }
154 Definition* poundDef = fParent;
155 if (!fParent) {
156 return reportError<bool>("expected parent");
157 }
158 if (Definition::Type::kBracket != poundDef->fType) {
159 return reportError<bool>("expected bracket");
160 }
161 if (Bracket::kPound != poundDef->fBracket) {
162 return reportError<bool>("expected preprocessor");
163 }
164 if (KeyWord::kNone != poundDef->fKeyWord) {
165 return reportError<bool>("already found keyword");
166 }
167 poundDef->fKeyWord = keyWord;
168 fIncludeWord = nullptr;
169 switch (keyWord) {
170 // these do not link to other # directives
171 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400172 if (!fInBrace) {
173 SkASSERT(!fInDefine);
174 fInDefine = true;
175 }
Cary Clark8032b982017-07-28 11:04:54 -0400176 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500177 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400178 break;
179 // these start a # directive link
180 case KeyWord::kIf:
181 case KeyWord::kIfdef:
182 case KeyWord::kIfndef:
183 break;
184 // these continue a # directive link
185 case KeyWord::kElif:
186 case KeyWord::kElse: {
187 this->popObject(); // pop elif
188 if (Bracket::kPound != fParent->fBracket) {
189 return this->reportError<bool>("expected preprocessor directive");
190 }
191 this->popBracket(); // pop if
192 poundDef->fParent = fParent;
193 this->addDefinition(poundDef); // push elif back
194 } break;
195 // this ends a # directive link
196 case KeyWord::kEndif:
197 // FIXME : should this be calling popBracket() instead?
198 this->popObject(); // pop endif
199 if (Bracket::kPound != fParent->fBracket) {
200 return this->reportError<bool>("expected preprocessor directive");
201 }
202 this->popBracket(); // pop if/else
203 break;
204 default:
205 SkASSERT(0);
206 }
207 return true;
208}
209
210string IncludeParser::className() const {
211 string name(fParent->fName);
212 size_t slash = name.find_last_of("/");
213 if (string::npos == slash) {
214 slash = name.find_last_of("\\");
215 }
216 SkASSERT(string::npos != slash);
217 string result = name.substr(slash);
218 result = result.substr(1, result.size() - 3);
219 return result;
220}
221
Cary Clark884dd7d2017-10-11 10:37:52 -0400222#include <sstream>
223#include <iostream>
224
Cary Clark8032b982017-07-28 11:04:54 -0400225bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400226 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400227 string className = classMapper.first;
228 auto finder = bmhParser.fClassMap.find(className);
229 if (bmhParser.fClassMap.end() == finder) {
230 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400231 continue;
232 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400233 RootDefinition* root = &finder->second;
234 root->clearVisited();
235 }
236 for (auto& classMapper : fIClassMap) {
237 string className = classMapper.first;
238 std::istringstream iss(className);
239 string classStr;
240 string classBase;
241 RootDefinition* root = nullptr;
242 while (std::getline(iss, classStr, ':')) {
243 if (root) {
244 if (!classStr.length()) {
245 continue;
246 }
247 classBase += "::" + classStr;
248 auto finder = root->fBranches.find(classBase);
249 if (root->fBranches.end() != finder) {
250 root = finder->second;
251 } else {
252 SkASSERT(0);
253 }
254 } else {
255 classBase = classStr;
256 auto finder = bmhParser.fClassMap.find(classBase);
257 if (bmhParser.fClassMap.end() != finder) {
258 root = &finder->second;
259 } else {
260 SkASSERT(0);
261 }
262 }
263 }
Cary Clark8032b982017-07-28 11:04:54 -0400264 auto& classMap = classMapper.second;
265 auto& tokens = classMap.fTokens;
266 for (const auto& token : tokens) {
267 if (token.fPrivate) {
268 continue;
269 }
270 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400271 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400272 switch (token.fMarkType) {
273 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400274 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400275 continue;
276 }
Cary Clark8032b982017-07-28 11:04:54 -0400277 if (!def) {
278 string paramName = className + "::";
279 paramName += string(token.fContentStart,
280 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400281 if (string::npos != paramName.find('\n')) {
282 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
283 paramName.end());
284 }
Cary Clarkce101242017-09-01 15:51:02 -0400285 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400286 if (!def && 0 == token.fName.find("operator")) {
287 string operatorName = className + "::";
288 TextParser oper("", token.fStart, token.fContentEnd, 0);
289 const char* start = oper.strnstr("operator", token.fContentEnd);
290 SkASSERT(start);
291 oper.skipTo(start);
292 oper.skipToEndBracket('(');
293 int parens = 0;
294 do {
295 if ('(' == oper.peek()) {
296 ++parens;
297 } else if (')' == oper.peek()) {
298 --parens;
299 }
300 } while (!oper.eof() && oper.next() && parens > 0);
301 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400302 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400303 }
304 }
305 if (!def) {
306 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
307 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400308 const char* tokenEnd = token.methodEnd();
Cary Clark8032b982017-07-28 11:04:54 -0400309 string constructorName = className + "::";
310 constructorName += string(token.fContentStart + skip,
Cary Clarkab5c9af2018-07-12 16:24:53 -0400311 tokenEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400312 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400313 }
314 if (!def && 0 == token.fName.find("SK_")) {
315 string incName = token.fName + "()";
316 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400317 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400318 if (def) {
319 if (def->fName == incName) {
320 def->fVisited = true;
321 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400322 def = root->find(className + "::toString",
323 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400324 if (def) {
325 def->fVisited = true;
326 } else {
327 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500328 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400329 }
330 }
331 break;
332 } else {
333 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500334 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400335 }
336 }
337 }
338 if (!def) {
339 bool allLower = true;
340 for (size_t index = 0; index < token.fName.length(); ++index) {
341 if (!islower(token.fName[index])) {
342 allLower = false;
343 break;
344 }
345 }
346 if (allLower) {
347 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400348 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400349 }
350 }
351 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400352 if (gAttrDeprecated == token.fName) {
353 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400354 break;
355 }
356 if (0 == token.fName.find("SkDEBUGCODE")) {
357 break;
358 }
359 }
360 if (!def) {
361 // simple method names inside nested classes have a bug and are missing trailing parens
362 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400363 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400364 }
365 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500366 if (!root->fDeprecated) {
367 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
368 fFailed = true;
369 }
Cary Clark8032b982017-07-28 11:04:54 -0400370 break;
371 }
Cary Clark73fa9722017-08-29 17:36:51 -0400372 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400373 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400374 if (token.fDeprecated && !def->fDeprecated) {
375 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
376 }
Cary Clark8032b982017-07-28 11:04:54 -0400377 } else {
378 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500379 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400380 }
381 } break;
382 case MarkType::kComment:
383 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400384 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400385 case MarkType::kEnum: {
386 if (!def) {
387 // work backwards from first word to deduce #Enum name
388 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
389 SkAssertResult(firstMember.skipName("enum"));
390 SkAssertResult(firstMember.skipToEndBracket('{'));
391 firstMember.next();
392 firstMember.skipWhiteSpace();
393 SkASSERT('k' == firstMember.peek());
394 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400395 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400396 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400397 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400398 const char* lastUnderscore = nullptr;
399 do {
400 if (!firstMember.skipToEndBracket('_')) {
401 break;
402 }
403 if (firstMember.fChar > wordEnd) {
404 break;
405 }
406 lastUnderscore = firstMember.fChar;
407 } while (firstMember.next());
408 if (lastUnderscore) {
409 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400410 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400411 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400412 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400413 }
414 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500415 if (!root->fDeprecated) {
416 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
417 fFailed = true;
418 }
Cary Clark8032b982017-07-28 11:04:54 -0400419 break;
420 }
421 }
422 def->fVisited = true;
423 for (auto& child : def->fChildren) {
424 if (MarkType::kCode == child->fMarkType) {
425 def = child;
426 break;
427 }
428 }
429 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500430 if (!root->fDeprecated) {
431 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
432 fFailed = true;
433 }
Cary Clark8032b982017-07-28 11:04:54 -0400434 break;
435 }
436 if (def->crossCheck(token)) {
437 def->fVisited = true;
438 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500439 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
440 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400441 }
442 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400443 string constName = MarkType::kEnumClass == token.fMarkType ?
444 fullName : className;
445 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400446 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400447 if (!def) {
448 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400449 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400450 }
451 if (!def) {
452 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500453 if (!root->fDeprecated) {
454 SkDebugf("const missing from bmh: %s\n", constName.c_str());
455 fFailed = true;
456 }
Cary Clark8032b982017-07-28 11:04:54 -0400457 }
458 } else {
459 def->fVisited = true;
460 }
461 }
462 } break;
463 case MarkType::kMember:
464 if (def) {
465 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500466 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400467 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500468 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400469 }
470 break;
Cary Clark2f466242017-12-11 16:03:17 -0500471 case MarkType::kTypedef:
472 if (def) {
473 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500474 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500475 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500476 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500477 }
478 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400479 case MarkType::kConst:
480 if (def) {
481 def->fVisited = true;
482 } else if (!root->fDeprecated) {
483 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
484 fFailed = true;
485 }
486 break;
Cary Clark8032b982017-07-28 11:04:54 -0400487 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400488 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400489 break;
490 }
491 }
492 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500493 int crossChecks = 0;
494 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400495 for (auto& classMapper : fIClassMap) {
496 string className = classMapper.first;
497 auto finder = bmhParser.fClassMap.find(className);
498 if (bmhParser.fClassMap.end() == finder) {
499 continue;
500 }
501 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500502 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500503 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400504 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500505 if (crossChecks) {
506 SkDebugf(".");
507 } else {
508 SkDebugf("cross-check");
509 firstCheck = className;
510 }
511 ++crossChecks;
512 }
513 if (crossChecks) {
514 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500515 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500516 }
517 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400518 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400519 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500520 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400521}
522
523IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400524 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400525 string className;
526 const Definition* test = fParent;
527 while (Definition::Type::kFileType != test->fType) {
528 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
529 className = test->fName + "::";
530 break;
531 }
532 test = test->fParent;
533 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400534 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400535 unordered_map<string, IClassDefinition>& map = fIClassMap;
536 IClassDefinition& markupDef = map[className];
537 if (markupDef.fStart) {
538 typedef IClassDefinition* IClassDefPtr;
539 return INHERITED::reportError<IClassDefPtr>("class already defined");
540 }
541 markupDef.fFileName = fFileName;
542 markupDef.fStart = includeDef.fStart;
543 markupDef.fContentStart = includeDef.fStart;
544 markupDef.fName = className;
545 markupDef.fContentEnd = includeDef.fContentEnd;
546 markupDef.fTerminator = includeDef.fTerminator;
547 markupDef.fParent = fParent;
548 markupDef.fLineCount = fLineCount;
549 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
550 MarkType::kStruct : MarkType::kClass;
551 markupDef.fKeyWord = includeDef.fKeyWord;
552 markupDef.fType = Definition::Type::kMark;
553 fParent = &markupDef;
554 return &markupDef;
555}
556
557void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
558 auto& tokens = classDef.fTokens;
559 for (auto& token : tokens) {
560 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
561 continue;
562 }
563 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400564 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400565 }
566 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400567 case MarkType::kConst:
568 this->dumpConst(token, classDef.fName);
569 break;
Cary Clark8032b982017-07-28 11:04:54 -0400570 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500571 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500572 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400573 break;
574 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400575 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400576 break;
577 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400578 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400579 continue;
580 break;
581 default:
582 SkASSERT(0);
583 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400584 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400585 }
586}
Cary Clark9174bda2017-09-19 17:39:32 -0400587void IncludeParser::dumpComment(const Definition& token) {
588 fLineCount = token.fLineCount;
589 fChar = fLine = token.fContentStart;
590 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400591 bool sawParam = false;
592 bool multiline = false;
593 bool sawReturn = false;
594 bool sawComment = false;
595 bool methodHasReturn = false;
596 vector<string> methodParams;
597 vector<string> foundParams;
598 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400599 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
600 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500601 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400602 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500603 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400604 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400605 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500606 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400607 && !methodParser.strnchr('~', methodParser.fEnd);
608 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
609 const char* nextEnd = paren;
610 do {
611 string paramName;
612 methodParser.fChar = nextEnd + 1;
613 methodParser.skipSpace();
614 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
615 continue;
616 }
617 methodParams.push_back(paramName);
618 } while (')' != nextEnd[0]);
619 }
Cary Clark9174bda2017-09-19 17:39:32 -0400620 for (const auto& child : token.fTokens) {
621 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
622 break;
623 }
Cary Clark8032b982017-07-28 11:04:54 -0400624 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400625 if (child.fPrivate) {
626 break;
627 }
Cary Clark8032b982017-07-28 11:04:54 -0400628 if ('@' == child.fContentStart[0]) {
629 TextParser parser(&child);
630 do {
631 parser.next();
632 if (parser.startsWith("param ")) {
633 parser.skipWord("param");
634 const char* parmStart = parser.fChar;
635 parser.skipToSpace();
636 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
637 parser.skipWhiteSpace();
638 do {
639 size_t nextComma = parmName.find(',');
640 string piece;
641 if (string::npos == nextComma) {
642 piece = parmName;
643 parmName = "";
644 } else {
645 piece = parmName.substr(0, nextComma);
646 parmName = parmName.substr(nextComma + 1);
647 }
648 if (sawParam) {
649 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400650 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400651 }
Cary Clark9174bda2017-09-19 17:39:32 -0400652 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400653 } else {
654 if (sawComment) {
655 this->nl();
656 }
657 this->lf(2);
658 }
659 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400660 this->writeTag("Param", piece);
661 this->writeSpace(2);
662 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
663 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400664 sawParam = true;
665 sawComment = false;
666 } while (parmName.length());
667 parser.skipTo(parser.fEnd);
668 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
669 parser.skipWord("return");
670 if ('s' == parser.peek()) {
671 parser.next();
672 }
673 if (sawParam) {
674 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400675 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400676 }
Cary Clark9174bda2017-09-19 17:39:32 -0400677 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400678 }
679 this->checkForMissingParams(methodParams, foundParams);
680 sawParam = false;
681 sawComment = false;
682 multiline = false;
683 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400684 this->writeTag("Return");
685 this->writeSpace(2);
686 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
687 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400688 sawReturn = true;
689 parser.skipTo(parser.fEnd);
690 } else {
691 this->reportError("unexpected doxygen directive");
692 }
693 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400694 } else if (child.length() > 1) {
695 const char* start = child.fContentStart;
696 ptrdiff_t length = child.fContentEnd - start;
697 SkASSERT(length >= 0);
698 while (length && '/' == start[0]) {
699 start += 1;
700 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400701 }
Cary Clark9174bda2017-09-19 17:39:32 -0400702 while (length && '/' == start[length - 1]) {
703 length -= 1;
704 if (length && '*' == start[length - 1]) {
705 length -= 1;
706 }
707 }
708 if (length) {
709 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
710 if (sawParam || sawReturn) {
711 this->indentToColumn(8);
712 }
713 this->writeBlock(length, start);
714 this->writeSpace();
715 sawComment = true;
716 if (sawParam || sawReturn) {
717 multiline = true;
718 }
Cary Clark8032b982017-07-28 11:04:54 -0400719 }
720 }
721 }
722 }
723 if (sawParam || sawReturn) {
724 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400725 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400726 }
Cary Clark9174bda2017-09-19 17:39:32 -0400727 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400728 }
729 if (!sawReturn) {
730 if (!sawParam) {
731 if (sawComment) {
732 this->nl();
733 }
734 this->lf(2);
735 }
736 this->checkForMissingParams(methodParams, foundParams);
737 }
738 if (methodHasReturn != sawReturn) {
739 if (!methodHasReturn) {
740 this->reportError("unexpected doxygen return");
741 } else {
742 if (sawComment) {
743 this->nl();
744 }
745 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400746 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400747 }
748 }
749}
750
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400751void IncludeParser::dumpCommonTail(const Definition& token) {
752 this->lf(2);
753 this->writeTag("Example");
754 this->lf(1);
755 this->writeString("// incomplete");
756 this->lf(1);
757 this->writeEndTag();
758 this->lf(2);
759 this->writeTag("SeeAlso");
760 this->writeSpace();
761 this->writeString("incomplete");
762 this->lf(2);
763 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
764 this->lf(2);
765}
766
Cary Clark224c7002018-06-27 11:00:21 -0400767void IncludeParser::dumpConst(const Definition& token, string className) {
768 this->writeTag("Const");
769 this->writeSpace();
770 this->writeString(token.fName);
771 this->writeTagTable("Line", "incomplete");
772 this->lf(2);
773 this->dumpComment(token);
774}
775
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400776void IncludeParser::dumpDefine(const Definition& token) {
777 this->writeTag("Define", token.fName);
778 this->lf(2);
779 this->writeTag("Code");
780 this->lfAlways(1);
781 this->writeString("###$");
782 this->lfAlways(1);
783 this->indentToColumn(4);
784 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
785 this->lf(1);
786 this->indentToColumn(0);
787 this->writeString("$$$#");
788
789 this->writeEndTag();
790 this->lf(2);
791 this->dumpComment(token);
792 for (auto& child : token.fTokens) {
793 if (MarkType::kComment == child.fMarkType) {
794 continue;
795 }
796 this->writeTag("Param", child.fName);
797 this->writeSpace();
798 this->writeString("incomplete");
799 this->writeSpace();
800 this->writeString("##");
801 this->lf(1);
802 }
803}
804
805void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500806 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400807 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400808 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400809 this->lfAlways(1);
810 this->indentToColumn(4);
811 this->writeString("enum");
812 this->writeSpace();
813 if ("_anonymous" != token.fName.substr(0, 10)) {
814 this->writeString(token.fName);
815 this->writeSpace();
816 }
817 this->writeString("{");
818 this->lfAlways(1);
819 for (auto& child : token.fChildren) {
820 this->indentToColumn(8);
821 this->writeString(child->fName);
822 if (child->length()) {
823 this->writeSpace();
824 this->writeBlock(child->length(), child->fContentStart);
825 }
826 if (',' != fLastChar) {
827 this->writeString(",");
828 }
829 this->lfAlways(1);
830 }
831 this->indentToColumn(4);
832 this->writeString("};");
833 this->lf(1);
834 this->writeString("##");
835 this->lf(2);
836 this->dumpComment(token);
837 for (auto& child : token.fChildren) {
838 // start here;
839 // get comments before
840 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400841 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400842 this->writeSpace();
843 this->writeString(child->fName);
844 TextParser val(child);
845 if (!val.eof()) {
846 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
847 val.next();
848 val.skipSpace();
849 const char* valEnd = val.anyOf(",\n");
850 if (!valEnd) {
851 valEnd = val.fEnd;
852 }
853 this->writeSpace();
854 this->writeBlock(valEnd - val.fStart, val.fStart);
855 } else {
856 this->writeSpace();
857 this->writeDefinition(*child);
858 }
859 }
860 this->lf(1);
861 for (auto comment : child->fChildren) {
862 if (MarkType::kComment == comment->fMarkType) {
863 TextParser parser(comment);
864 parser.skipExact("*");
865 parser.skipExact("*");
866 while (!parser.eof() && parser.skipWhiteSpace()) {
867 parser.skipExact("*");
868 parser.skipWhiteSpace();
869 const char* start = parser.fChar;
870 parser.skipToEndBracket('\n');
871 this->lf(1);
872 this->writeBlock(parser.fChar - start, start);
873 }
874 }
875 }
876 this->writeEndTag();
877 }
878 this->lf(2);
879}
880
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400881bool IncludeParser::dumpGlobals() {
Cary Clark224c7002018-06-27 11:00:21 -0400882 if (fIDefineMap.empty() && fIFunctionMap.empty() && fIEnumMap.empty() && fITemplateMap.empty()
883 && fITypedefMap.empty() && fIUnionMap.empty()) {
884 return true;
885 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400886 size_t lastBSlash = fFileName.rfind('\\');
887 size_t lastSlash = fFileName.rfind('/');
888 size_t lastDotH = fFileName.rfind(".h");
889 SkASSERT(string::npos != lastDotH);
890 if (string::npos != lastBSlash && (string::npos == lastSlash
891 || lastBSlash < lastSlash)) {
892 lastSlash = lastBSlash;
893 } else if (string::npos == lastSlash) {
894 lastSlash = -1;
895 }
896 lastSlash += 1;
897 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
898 string fileName = globalsName + "_Reference.bmh";
899 fOut = fopen(fileName.c_str(), "wb");
900 if (!fOut) {
901 SkDebugf("could not open output file %s\n", globalsName.c_str());
902 return false;
903 }
904 string prefixName = globalsName.substr(0, 2);
905 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
906 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
907 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -0400908 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400909 this->lf(2);
910 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400911 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400912 this->writeEndTag();
913 this->lf(2);
914 if (!fIDefineMap.empty()) {
915 this->writeTag("Subtopic", "Define");
916 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400917 this->writeEndTag();
918 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400919 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400920 if (!fIFunctionMap.empty()) {
921 this->writeTag("Subtopic", "Function");
922 this->writeTag("Populate");
923 this->writeEndTag();
924 this->lf(2);
925 }
926 if (!fIEnumMap.empty()) {
927 this->writeTag("Subtopic", "Enum");
928 this->writeTag("Populate");
929 this->writeEndTag();
930 this->lf(2);
931 }
932 if (!fITemplateMap.empty()) {
933 this->writeTag("Subtopic", "Template");
934 this->writeTag("Populate");
935 this->writeEndTag();
936 this->lf(2);
937 }
938 if (!fITypedefMap.empty()) {
939 this->writeTag("Subtopic", "Typedef");
940 this->writeTag("Populate");
941 this->writeEndTag();
942 this->lf(2);
943 }
944 if (!fIUnionMap.empty()) {
945 this->writeTag("Subtopic", "Union");
946 this->writeTag("Populate");
947 this->writeEndTag();
948 this->lf(2);
949 }
950 std::map<int, Definition*> sortedDefs;
951 for (const auto& entry : fIDefineMap) {
952 sortedDefs[entry.second->fLineCount] = entry.second;
953 }
954 for (const auto& entry : fIFunctionMap) {
955 sortedDefs[entry.second->fLineCount] = entry.second;
956 }
957 for (const auto& entry : fIEnumMap) {
958 sortedDefs[entry.second->fLineCount] = entry.second;
959 }
960 for (const auto& entry : fITemplateMap) {
961 sortedDefs[entry.second->fLineCount] = entry.second;
962 }
963 for (const auto& entry : fITypedefMap) {
964 sortedDefs[entry.second->fLineCount] = entry.second;
965 }
966 for (const auto& entry : fIUnionMap) {
967 sortedDefs[entry.second->fLineCount] = entry.second;
968 }
969 for (const auto& entry : sortedDefs) {
970 const Definition* def = entry.second;
971 this->writeBlockSeparator();
972 switch (def->fMarkType) {
973 case MarkType::kDefine:
974 this->dumpDefine(*def);
975 break;
976 case MarkType::kMethod:
977 this->dumpMethod(*def, globalsName);
978 break;
979 case MarkType::kEnum:
980 case MarkType::kEnumClass:
981 this->dumpEnum(*def, globalsName);
982 break;
983 case MarkType::kTemplate:
984 SkASSERT(0); // incomplete
985 break;
986 case MarkType::kTypedef: {
987 this->writeTag("Typedef");
988 this->writeSpace();
989 TextParser parser(def);
990 if (!parser.skipExact("typedef")) {
991 return false;
992 }
993 if (!parser.skipSpace()) {
994 return false;
995 }
996 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
997 this->lf(2);
998 this->dumpComment(*def);
999 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1000 this->lf(2);
1001 } continue;
1002 case MarkType::kUnion:
1003 SkASSERT(0); // incomplete
1004 break;
1005 default:
1006 SkASSERT(0);
1007 }
1008 this->dumpCommonTail(*def);
1009 }
1010 this->writeEndTag("Topic", topicName);
1011 this->lfAlways(1);
1012 fclose(fOut);
1013 SkDebugf("wrote %s\n", fileName.c_str());
1014 return true;
1015}
1016
1017bool IncludeParser::isClone(const Definition& token) {
1018 string name = token.fName;
1019 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1020}
1021
1022bool IncludeParser::isConstructor(const Definition& token, string className) {
1023 string name = token.fName;
1024 return 0 == name.find(className) || '~' == name[0];
1025}
1026
1027bool IncludeParser::isInternalName(const Definition& token) {
1028 string name = token.fName;
1029 // exception for this SkCanvas function .. for now
1030 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1031 return false;
1032 }
1033 return name.substr(0, 7) == "android"
1034 || 0 == token.fName.find("internal_")
1035 || 0 == token.fName.find("Internal_")
1036 || 0 == token.fName.find("legacy_")
1037 || 0 == token.fName.find("temporary_")
1038 || 0 == token.fName.find("private_");
1039}
1040
1041bool IncludeParser::isOperator(const Definition& token) {
1042 return "operator" == token.fName.substr(0, 8);
1043}
1044
1045void IncludeParser::dumpMethod(const Definition& token, string className) {
1046 this->writeTag("Method");
1047 this->writeSpace();
1048
1049 string name = string(token.fStart ? token.fStart : token.fContentStart,
1050 token.length());
1051 if (this->isOperator(token)) {
1052 string spaceConst(" const");
1053 size_t constPos = name.rfind(spaceConst);
1054 if (name.length() - spaceConst.length() == constPos) {
1055 name = name.substr(0, constPos) + "_const";
1056 }
1057 }
Cary Clark224c7002018-06-27 11:00:21 -04001058 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001059 string inType;
1060 if (this->isConstructor(token, className)) {
1061 inType = "Constructor";
1062 } else if (this->isOperator(token)) {
1063 inType = "Operator";
1064 } else {
1065 inType = "incomplete";
1066 }
1067 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001068 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001069 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001070 this->dumpComment(token);
1071}
1072
1073void IncludeParser::dumpMember(const Definition& token) {
1074 this->writeTag("Member");
1075 this->writeSpace();
1076 this->writeDefinition(token, token.fName, 2);
1077 lf(1);
1078 for (auto child : token.fChildren) {
1079 this->writeDefinition(*child);
1080 }
1081 this->writeEndTag();
1082 lf(2);
1083}
1084
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001085bool IncludeParser::dumpTokens() {
1086 if (!this->dumpGlobals()) {
1087 return false;
1088 }
Cary Clark9174bda2017-09-19 17:39:32 -04001089 for (const auto& member : fIClassMap) {
1090 if (string::npos != member.first.find("::")) {
1091 continue;
1092 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001093 if (!this->dumpTokens(member.first)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001094 return false;
1095 }
1096 }
1097 return true;
1098}
1099
Ben Wagner63fd7602017-10-09 15:45:33 -04001100 // dump equivalent markup
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001101bool IncludeParser::dumpTokens(string skClassName) {
1102 string fileName = skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -04001103 fOut = fopen(fileName.c_str(), "wb");
1104 if (!fOut) {
1105 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -04001106 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001107 }
1108 string prefixName = skClassName.substr(0, 2);
1109 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1110 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -04001111 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001112 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark9174bda2017-09-19 17:39:32 -04001113 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001114 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001115 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1116 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001117 this->writeTag(containerType, skClassName);
1118 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001119 auto& tokens = classMap.fTokens;
1120 for (auto& token : tokens) {
1121 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1122 continue;
1123 }
Cary Clark9174bda2017-09-19 17:39:32 -04001124 this->writeDefinition(token);
1125 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001126 }
1127 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001128 bool hasClass = false;
1129 bool hasConst = !fIEnumMap.empty();
1130 bool hasConstructor = false;
1131 bool hasMember = false;
1132 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001133 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001134 for (const auto& oneClass : fIClassMap) {
1135 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1136 continue;
1137 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001138 hasClass = true;
1139 break;
Cary Clark8032b982017-07-28 11:04:54 -04001140 }
Cary Clark224c7002018-06-27 11:00:21 -04001141 for (const auto& oneStruct : fIStructMap) {
1142 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1143 continue;
1144 }
1145 hasStruct = true;
1146 break;
1147 }
Cary Clark8032b982017-07-28 11:04:54 -04001148 for (const auto& token : classMap.fTokens) {
1149 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1150 continue;
1151 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001152 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001153 continue;
1154 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001155 if (this->isConstructor(token, skClassName)) {
1156 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001157 continue;
1158 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001159 if (this->isOperator(token)) {
1160 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001161 continue;
1162 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001163 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001164 continue;
1165 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001166 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001167 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001168 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001169 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001170 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001171 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001172
1173 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001174 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001175 this->writeTag("Populate");
1176 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001177 this->lf(2);
1178 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001179 if (hasConst) {
1180 this->writeTag("Subtopic", "Constant");
1181 this->writeTag("Populate");
1182 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001183 this->lf(2);
1184 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001185 if (hasConstructor) {
1186 this->writeTag("Subtopic", "Constructor");
1187 this->writeTag("Populate");
1188 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001189 this->lf(2);
1190 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001191 if (hasOperator) {
1192 this->writeTag("Subtopic", "Operator");
1193 this->writeTag("Populate");
1194 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001195 this->lf(2);
1196 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001197 if (hasMember) {
1198 this->writeTag("Subtopic", "Member_Function");
1199 this->writeTag("Populate");
1200 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001201 this->lf(2);
1202 }
Cary Clark224c7002018-06-27 11:00:21 -04001203 if (hasStruct) {
1204 this->writeTag("Subtopic", "Struct");
1205 this->writeTag("Populate");
1206 this->writeEndTag();
1207 this->lf(2);
1208 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001209 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001210 this->writeBlockSeparator();
1211 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001212 this->lf(2);
1213 this->writeTag("Example");
1214 this->lfcr();
1215 this->writeString("// incomplete");
1216 this->writeEndTag();
1217 this->lf(2);
1218 this->writeTag("SeeAlso", "incomplete");
1219 this->lf(2);
1220 this->writeEndTag("Enum", oneEnum.first);
1221 this->lf(2);
1222 }
Cary Clark8032b982017-07-28 11:04:54 -04001223 for (auto& oneClass : fIClassMap) {
1224 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1225 continue;
1226 }
1227 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001228 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001229 KeyWord keyword = oneClass.second.fKeyWord;
1230 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1231 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001232 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001233 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001234 this->lf(2);
1235 this->writeTag("Code");
1236 this->writeEndTag("ToDo", "fill this in manually");
1237 this->writeEndTag();
1238 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001239 for (auto& token : oneClass.second.fTokens) {
1240 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1241 continue;
1242 }
Cary Clark9174bda2017-09-19 17:39:32 -04001243 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001244 }
1245 this->lf(2);
1246 this->dumpClassTokens(oneClass.second);
1247 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001248 this->writeEndTag(containerType, innerName);
1249 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001250 }
1251 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001252 this->writeEndTag(containerType, skClassName);
1253 this->lf(2);
1254 this->writeEndTag("Topic", topicName);
1255 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001256 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001257 SkDebugf("wrote %s\n", fileName.c_str());
1258 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001259}
1260
1261bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1262 // add comment preceding class, if any
1263 const Definition* parent = includeDef.fParent;
1264 int index = includeDef.fParentIndex;
1265 auto wordIter = parent->fTokens.begin();
1266 std::advance(wordIter, index);
1267 SkASSERT(&*wordIter == &includeDef);
1268 while (parent->fTokens.begin() != wordIter) {
1269 auto testIter = std::prev(wordIter);
1270 if (Definition::Type::kWord != testIter->fType
1271 && Definition::Type::kKeyWord != testIter->fType
1272 && (Definition::Type::kBracket != testIter->fType
1273 || Bracket::kAngle != testIter->fBracket)
1274 && (Definition::Type::kPunctuation != testIter->fType
1275 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1276 break;
1277 }
1278 wordIter = testIter;
1279 }
1280 auto commentIter = wordIter;
1281 while (parent->fTokens.begin() != commentIter) {
1282 auto testIter = std::prev(commentIter);
1283 bool isComment = Definition::Type::kBracket == testIter->fType
1284 && (Bracket::kSlashSlash == testIter->fBracket
1285 || Bracket::kSlashStar == testIter->fBracket);
1286 if (!isComment) {
1287 break;
1288 }
1289 commentIter = testIter;
1290 }
1291 while (commentIter != wordIter) {
1292 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1293 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1294 return false;
1295 }
1296 commentIter = std::next(commentIter);
1297 }
1298 return true;
1299}
1300
Cary Clark0d225392018-06-07 09:59:07 -04001301Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1302 string typeName) {
1303 typedef Definition* DefinitionPtr;
1304 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1305 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1306 if (mapIter == fMaps.end()) {
1307 return nullptr;
1308 }
1309 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1310 return reportError<DefinitionPtr>("invalid mark type");
1311 }
1312 string name = this->uniqueName(*mapIter->fInclude, typeName);
1313 Definition& markupDef = *(*mapIter->fInclude)[name];
1314 if (markupDef.fStart) {
1315 return reportError<DefinitionPtr>("definition already defined");
1316 }
1317 markupDef.fFileName = fFileName;
1318 markupDef.fStart = includeDef.fStart;
1319 markupDef.fContentStart = includeDef.fStart;
1320 markupDef.fName = name;
1321 markupDef.fContentEnd = includeDef.fContentEnd;
1322 markupDef.fTerminator = includeDef.fTerminator;
1323 markupDef.fParent = fParent;
1324 markupDef.fLineCount = includeDef.fLineCount;
1325 markupDef.fMarkType = markType;
1326 markupDef.fKeyWord = includeDef.fKeyWord;
1327 markupDef.fType = Definition::Type::kMark;
1328 return &markupDef;
1329}
1330
Cary Clark224c7002018-06-27 11:00:21 -04001331Definition* IncludeParser::parentBracket(Definition* parent) const {
1332 while (parent && Definition::Type::kBracket != parent->fType) {
1333 parent = parent->fParent;
1334 }
1335 return parent;
1336}
1337
1338Bracket IncludeParser::grandParentBracket() const {
1339 Definition* parent = parentBracket(fParent);
1340 parent = parentBracket(parent ? parent->fParent : nullptr);
1341 return parent ? parent->fBracket : Bracket::kNone;
1342}
1343
Cary Clark137b8742018-05-30 09:21:49 -04001344// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001345bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1346 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001347 // parse class header
1348 auto iter = includeDef->fTokens.begin();
1349 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1350 // todo : documentation is ignoring this for now
1351 iter = std::next(iter);
1352 }
1353 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1354 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001355 iter = std::next(iter);
1356 if (iter == includeDef->fTokens.end()) {
1357 return true; // forward declaration only
1358 }
Cary Clark8032b982017-07-28 11:04:54 -04001359 do {
1360 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001361 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001362 }
1363 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1364 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001365 }
Cary Clark8032b982017-07-28 11:04:54 -04001366 } while (static_cast<void>(iter = std::next(iter)), true);
1367 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001368 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001369 }
1370 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1371 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001372 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001373 }
1374 markupDef->fStart = iter->fStart;
1375 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001376 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001377 }
1378// if (1 != includeDef->fChildren.size()) {
1379// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1380// }
1381 includeDef = includeDef->fChildren.front();
1382 iter = includeDef->fTokens.begin();
1383 // skip until public
1384 int publicIndex = 0;
1385 if (IsStruct::kNo == isStruct) {
1386 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1387 size_t publicLen = strlen(publicName);
1388 while (iter != includeDef->fTokens.end()
1389 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1390 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001391 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001392 iter = std::next(iter);
1393 ++publicIndex;
1394 }
1395 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001396 int keyIndex = publicIndex;
1397 KeyWord currentKey = KeyWord::kPublic;
1398 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1399 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001400 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1401 size_t protectedLen = strlen(protectedName);
1402 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1403 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001404 auto childIter = includeDef->fChildren.begin();
Cary Clarkb7a72a52018-06-15 05:11:24 -04001405 while ((*childIter)->fPrivate) {
1406 std::advance(childIter, 1);
1407 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001408 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001409 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001410 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001411 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001412 const char* testStart = iter->fStart;
1413 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1414 iter = std::next(iter);
1415 ++keyIndex;
1416 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1417 currentKey = KeyWord::kPublic;
1418 break;
1419 }
1420 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1421 currentKey = KeyWord::kProtected;
1422 break;
1423 }
1424 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1425 currentKey = KeyWord::kPrivate;
1426 break;
1427 }
1428 }
1429 fLastObject = nullptr;
1430 if (KeyWord::kPublic == currentKey) {
1431 if (!this->parseObject(child, markupDef)) {
1432 return false;
1433 }
Cary Clark8032b982017-07-28 11:04:54 -04001434 }
Cary Clark73fa9722017-08-29 17:36:51 -04001435 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001436 childIter = std::next(childIter);
1437 }
Cary Clark137b8742018-05-30 09:21:49 -04001438 while (iter != includeDef->fTokens.end()) {
1439 iter->fPrivate = KeyWord::kPublic != currentKey;
1440 iter = std::next(iter);
1441 }
Cary Clark8032b982017-07-28 11:04:54 -04001442 SkASSERT(fParent->fParent);
1443 fParent = fParent->fParent;
1444 return true;
1445}
1446
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001447bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001448 int lineCount, Definition* markupDef) {
1449 TextParser parser(filename, start, end, lineCount);
1450 // parse doxygen if present
1451 if (parser.startsWith("**")) {
1452 parser.next();
1453 parser.next();
1454 parser.skipWhiteSpace();
1455 if ('\\' == parser.peek()) {
1456 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001457 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1458 if (parser.skipExact("file")) {
1459 if (Definition::Type::kFileType != fParent->fType) {
1460 return reportError<bool>("expected parent is file");
1461 }
1462 string filename = markupDef->fileName();
1463 if (!parser.skipWord(filename.c_str())) {
1464 return reportError<bool>("missing object type");
1465 }
1466 } else if (parser.skipExact("fn")) {
1467 SkASSERT(0); // incomplete
1468 } else {
1469 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1470 return reportError<bool>("missing object type");
1471 }
1472 if (!parser.skipWord(markupDef->fName.c_str()) &&
1473 KeyWord::kEnum != markupDef->fKeyWord) {
1474 return reportError<bool>("missing object name");
1475 }
Cary Clark8032b982017-07-28 11:04:54 -04001476 }
Cary Clark8032b982017-07-28 11:04:54 -04001477 }
1478 }
1479 // remove leading '*' if present
1480 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1481 while (!parser.eof() && parser.skipWhiteSpace()) {
1482 while ('*' == parser.peek()) {
1483 parser.next();
1484 if (parser.eof()) {
1485 break;
1486 }
1487 parser.skipWhiteSpace();
1488 }
1489 if (parser.eof()) {
1490 break;
1491 }
1492 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001493 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001494 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001495 parser.skipToEndBracket('\n');
1496 }
1497 return true;
1498}
1499
Cary Clarkd98f78c2018-04-26 08:32:37 -04001500bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04001501 if (!markupDef) {
1502 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1503 child->fLineCount, fParent, '\0');
1504 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04001505 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04001506 globalMarkupChild->fName = globalUniqueName;
1507 if (!this->findComments(*child, globalMarkupChild)) {
1508 return false;
1509 }
1510 fIConstMap[globalUniqueName] = globalMarkupChild;
1511 return true;
1512 }
1513 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1514 child->fLineCount, markupDef, '\0');
1515 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04001516 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001517 markupChild->fTerminator = markupChild->fContentEnd;
1518 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04001519 classDef.fConsts[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001520 return true;
1521}
1522
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001523bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1524 TextParser parser(child);
1525 if (!parser.skipExact("#define")) {
1526 return false;
1527 }
1528 if (!parser.skipSpace()) {
1529 return false;
1530 }
1531 const char* nameStart = parser.fChar;
1532 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1533 if (parser.eof()) {
1534 return true; // do nothing if #define doesn't define anything
1535 }
1536 string nameStr(nameStart, parser.fChar - nameStart);
1537 struct Param {
1538 const char* fStart;
1539 const char* fEnd;
1540 };
1541 vector<Param> params;
1542 if ('(' == parser.peek()) {
1543 parser.next();
1544 if (!parser.skipSpace()) {
1545 return false;
1546 }
1547 do {
1548 const char* paramStart = parser.fChar;
1549 if (!parser.skipExact("...")) {
1550 parser.skipToNonAlphaNum();
1551 }
1552 if (parser.eof()) {
1553 return false;
1554 }
1555 params.push_back({paramStart, parser.fChar});
1556 if (!parser.skipSpace()) {
1557 return false;
1558 }
1559 if (')' == parser.peek()) {
1560 parser.next();
1561 break;
1562 }
1563 if (',' != parser.next()) {
1564 return false;
1565 }
1566 if (!parser.skipSpace()) {
1567 return false;
1568 }
1569 } while (true);
1570 }
1571 if (!parser.skipSpace()) {
1572 return false;
1573 }
1574 if (!markupDef) {
1575 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1576 child->fLineCount, fParent, '\0');
1577 Definition* globalMarkupChild = &fGlobals.back();
1578 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1579 globalMarkupChild->fName = globalUniqueName;
1580 globalMarkupChild->fTerminator = child->fContentEnd;
1581 if (!this->findComments(*child, globalMarkupChild)) {
1582 return false;
1583 }
1584 fIDefineMap[globalUniqueName] = globalMarkupChild;
1585 for (Param param : params) {
1586 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1587 child->fLineCount, globalMarkupChild, '\0');
1588 Definition* paramChild = &globalMarkupChild->fTokens.back();
1589 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1590 paramChild->fTerminator = param.fEnd;
1591 }
1592 return true;
1593 }
1594 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1595 child->fLineCount, markupDef, '\0');
1596 Definition* markupChild = &markupDef->fTokens.back();
1597 markupChild->fName = nameStr;
1598 markupChild->fTerminator = markupChild->fContentEnd;
1599 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1600 if (!this->findComments(*child, markupChild)) {
1601 return false;
1602 }
1603 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001604 return true;
1605}
1606
1607bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001608 TextParser parser(child);
1609 parser.skipToEndBracket('{');
1610 if (parser.eof()) {
1611 return true; // if enum is a forward declaration, do nothing
1612 }
1613 parser.next();
1614 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001615 if (child->fTokens.size() > 0) {
1616 auto token = child->fTokens.begin();
1617 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1618 token = token->fTokens.begin();
1619 }
1620 if (Definition::Type::kWord == token->fType) {
1621 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1622 }
1623 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001624 Definition* markupChild;
1625 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001626 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1627 child->fLineCount, fParent, '\0');
1628 markupChild = &fGlobals.back();
1629 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1630 markupChild->fName = globalUniqueName;
1631 markupChild->fTerminator = child->fContentEnd;
1632 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001633 } else {
1634 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001635 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001636 markupChild = &markupDef->fTokens.back();
1637 }
Cary Clark8032b982017-07-28 11:04:54 -04001638 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1639 markupChild->fKeyWord = KeyWord::kEnum;
1640 TextParser enumName(child);
1641 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001642 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001643 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001644 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001645 markupChild->fMarkType = MarkType::kEnumClass;
1646 }
Cary Clark8032b982017-07-28 11:04:54 -04001647 const char* nameStart = enumName.fChar;
1648 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001649 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001650 markupChild->fName = markupDef->fName + "::" +
1651 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001652 }
Cary Clark8032b982017-07-28 11:04:54 -04001653 if (!this->findComments(*child, markupChild)) {
1654 return false;
1655 }
Cary Clark8032b982017-07-28 11:04:54 -04001656 const char* dataEnd;
1657 do {
Cary Clark8032b982017-07-28 11:04:54 -04001658 parser.skipWhiteSpace();
1659 if ('}' == parser.peek()) {
1660 break;
1661 }
1662 Definition* comment = nullptr;
1663 // note that comment, if any, can be before or after (on the same line, though) as member
1664 if ('#' == parser.peek()) {
1665 // fixme: handle preprecessor, but just skip it for now
1666 parser.skipToLineStart();
1667 }
1668 while (parser.startsWith("/*") || parser.startsWith("//")) {
1669 parser.next();
1670 const char* start = parser.fChar;
1671 const char* end;
1672 if ('*' == parser.peek()) {
1673 end = parser.strnstr("*/", parser.fEnd);
1674 parser.fChar = end;
1675 parser.next();
1676 parser.next();
1677 } else {
1678 end = parser.trimmedLineEnd();
1679 parser.skipToLineStart();
1680 }
1681 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001682 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001683 comment = &markupChild->fTokens.back();
1684 comment->fTerminator = end;
1685 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1686 return false;
1687 }
1688 parser.skipWhiteSpace();
1689 }
1690 parser.skipWhiteSpace();
1691 const char* memberStart = parser.fChar;
1692 if ('}' == memberStart[0]) {
1693 break;
1694 }
Cary Clark9174bda2017-09-19 17:39:32 -04001695 // if there's comment on same the line as member def, output first as if it was before
1696
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001697 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001698 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001699 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04001700 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04001701 }
Cary Clark8032b982017-07-28 11:04:54 -04001702 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001703 if ('=' == parser.peek()) {
1704 parser.skipToEndBracket(',');
1705 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001706 if (!parser.eof() && '#' == parser.peek()) {
1707 // fixme: handle preprecessor, but just skip it for now
1708 continue;
1709 }
Cary Clark9174bda2017-09-19 17:39:32 -04001710 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04001711 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04001712 }
1713 dataEnd = parser.fChar;
1714 const char* start = parser.anyOf("/\n");
1715 SkASSERT(start);
1716 parser.skipTo(start);
1717 if ('/' == parser.next()) {
1718 char slashStar = parser.next();
1719 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001720 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001721 char doxCheck = parser.next();
1722 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1723 save.restore();
1724 }
1725 }
1726 parser.skipWhiteSpace();
1727 const char* commentStart = parser.fChar;
1728 if ('/' == slashStar) {
1729 parser.skipToEndBracket('\n');
1730 } else {
1731 parser.skipToEndBracket("*/");
1732 }
1733 SkASSERT(!parser.eof());
1734 const char* commentEnd = parser.fChar;
1735 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001736 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001737 comment = &markupChild->fTokens.back();
1738 comment->fTerminator = commentEnd;
1739 }
Cary Clark8032b982017-07-28 11:04:54 -04001740 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001741 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001742 Definition* member = &markupChild->fTokens.back();
1743 member->fName = memberName;
1744 if (comment) {
1745 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001746 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001747 }
1748 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001749 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001750 for (auto outsideMember : child->fChildren) {
1751 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001752 continue;
1753 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001754 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1755 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001756 continue;
1757 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001758 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1759 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001760 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001761 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001762 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001763 // FIXME: ? add comment as well ?
1764 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001765 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001766 if (markupDef) {
1767 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1768 SkASSERT(classDef.fStart);
1769 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1770 markupChild->fName = uniqueName;
1771 classDef.fEnums[uniqueName] = markupChild;
1772 }
Cary Clark8032b982017-07-28 11:04:54 -04001773 return true;
1774}
1775
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001776bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001777 fParent = &fIncludeMap[name];
1778 fParent->fName = name;
1779 fParent->fFileName = fFileName;
1780 fParent->fType = Definition::Type::kFileType;
1781 fParent->fContentStart = fChar;
1782 fParent->fContentEnd = fEnd;
1783 // parse include file into tree
1784 while (fChar < fEnd) {
1785 if (!this->parseChar()) {
1786 return false;
1787 }
1788 }
1789 // parse tree and add named objects to maps
1790 fParent = &fIncludeMap[name];
1791 if (!this->parseObjects(fParent, nullptr)) {
1792 return false;
1793 }
1794 return true;
1795}
1796
1797bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1798 const char* typeStart = child->fChildren[0]->fContentStart;
1799 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001800 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001801 Definition* markupChild = &markupDef->fTokens.back();
1802 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001803 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001804 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1805 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1806 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1807 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001808 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001809 classDef.fMembers[uniqueName] = markupChild;
1810 if (child->fParentIndex >= 2) {
1811 auto comment = child->fParent->fTokens.begin();
1812 std::advance(comment, child->fParentIndex - 2);
1813 if (Definition::Type::kBracket == comment->fType
1814 && (Bracket::kSlashStar == comment->fBracket
1815 || Bracket::kSlashSlash == comment->fBracket)) {
1816 TextParser parser(&*comment);
1817 do {
1818 parser.skipToAlpha();
1819 if (parser.eof()) {
1820 break;
1821 }
1822 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001823 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001824 if (Bracket::kSlashStar == comment->fBracket) {
1825 const char* commentEnd = parser.strnstr("*/", end);
1826 if (commentEnd) {
1827 end = commentEnd;
1828 }
1829 }
1830 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001831 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001832 Definition* commentChild = &markupDef->fTokens.back();
1833 markupChild->fChildren.emplace_back(commentChild);
1834 parser.skipTo(end);
1835 } while (!parser.eof());
1836 }
1837 }
1838 return true;
1839}
1840
1841bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1842 auto tokenIter = child->fParent->fTokens.begin();
1843 std::advance(tokenIter, child->fParentIndex);
1844 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001845 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001846 bool addConst = false;
1847 auto operatorCheck = tokenIter;
1848 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1849 operatorCheck = std::prev(tokenIter);
1850 }
1851 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001852 auto closeParen = std::next(tokenIter);
1853 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1854 '(' == closeParen->fContentStart[0]);
1855 nameEnd = closeParen->fContentEnd + 1;
1856 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001857 if (Definition::Type::kKeyWord == closeParen->fType &&
1858 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001859 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001860 }
Cary Clarka560c472017-11-27 10:44:06 -05001861 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001862 }
1863 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarkab5c9af2018-07-12 16:24:53 -04001864 if (string::npos != nameStr.find("sizeof")) {
1865 SkDebugf("");
1866 }
Cary Clarka560c472017-11-27 10:44:06 -05001867 if (addConst) {
1868 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001869 }
Cary Clark8032b982017-07-28 11:04:54 -04001870 while (tokenIter != child->fParent->fTokens.begin()) {
1871 auto testIter = std::prev(tokenIter);
1872 switch (testIter->fType) {
1873 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001874 if (testIter == child->fParent->fTokens.begin() &&
1875 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1876 KeyWord::kIfndef == child->fParent->fKeyWord ||
1877 KeyWord::kIf == child->fParent->fKeyWord)) {
1878 std::next(tokenIter);
1879 break;
1880 }
Cary Clark8032b982017-07-28 11:04:54 -04001881 goto keepGoing;
1882 case Definition::Type::kKeyWord: {
1883 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1884 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1885 goto keepGoing;
1886 }
1887 } break;
1888 case Definition::Type::kBracket:
1889 if (Bracket::kAngle == testIter->fBracket) {
1890 goto keepGoing;
1891 }
1892 break;
1893 case Definition::Type::kPunctuation:
1894 if (Punctuation::kSemicolon == testIter->fPunctuation
1895 || Punctuation::kLeftBrace == testIter->fPunctuation
1896 || Punctuation::kColon == testIter->fPunctuation) {
1897 break;
1898 }
1899 keepGoing:
1900 tokenIter = testIter;
1901 continue;
1902 default:
1903 break;
1904 }
1905 break;
1906 }
Cary Clark224c7002018-06-27 11:00:21 -04001907 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04001908 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001909 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001910 auto testIter = child->fParent->fTokens.begin();
1911 SkASSERT(child->fParentIndex > 0);
1912 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001913 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1914 0 == tokenIter->fParentIndex) {
1915 tokenIter = std::next(tokenIter);
1916 }
Cary Clark8032b982017-07-28 11:04:54 -04001917 const char* start = tokenIter->fContentStart;
1918 const char* end = tokenIter->fContentEnd;
1919 const char kDebugCodeStr[] = "SkDEBUGCODE";
1920 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1921 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1922 std::advance(testIter, 1);
1923 start = testIter->fContentStart + 1;
1924 end = testIter->fContentEnd - 1;
1925 } else {
1926 end = testIter->fContentEnd;
1927 while (testIter != child->fParent->fTokens.end()) {
1928 testIter = std::next(testIter);
1929 switch (testIter->fType) {
1930 case Definition::Type::kPunctuation:
1931 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1932 || Punctuation::kLeftBrace == testIter->fPunctuation
1933 || Punctuation::kColon == testIter->fPunctuation);
1934 end = testIter->fStart;
1935 break;
1936 case Definition::Type::kKeyWord: {
1937 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1938 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1939 continue;
1940 }
1941 } break;
1942 default:
1943 continue;
1944 }
1945 break;
1946 }
1947 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001948 while (end > start && ' ' >= end[-1]) {
1949 --end;
1950 }
Cary Clark9174bda2017-09-19 17:39:32 -04001951 if (!markupDef) {
1952 auto parentIter = child->fParent->fTokens.begin();
1953 SkASSERT(child->fParentIndex > 0);
1954 std::advance(parentIter, child->fParentIndex - 1);
1955 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001956 TextParser nameParser(methodName);
1957 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001958 return true; // expect this is inline class definition outside of class
1959 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001960 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1961 fParent, '\0');
1962 Definition* globalMarkupChild = &fGlobals.back();
1963 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1964 globalMarkupChild->fName = globalUniqueName;
1965 if (!this->findComments(*child, globalMarkupChild)) {
1966 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001967 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001968 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001969 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001970 }
Cary Clark8032b982017-07-28 11:04:54 -04001971 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001972 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001973 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04001974 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
1975 {
1976 auto mapIter = fIClassMap.find(markupDef->fName);
1977 SkASSERT(fIClassMap.end() != mapIter);
1978 IClassDefinition& classDef = mapIter->second;
1979 SkASSERT(classDef.fStart);
1980 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1981 markupChild->fName = uniqueName;
1982 if (!this->findComments(*child, markupChild)) {
1983 return false;
1984 }
1985 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001986 }
Cary Clark8032b982017-07-28 11:04:54 -04001987 return true;
1988}
1989
Cary Clark8032b982017-07-28 11:04:54 -04001990bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04001991 fPriorObject = nullptr;
1992 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04001993 if (!this->parseObject(child, markupDef)) {
1994 return false;
1995 }
Cary Clark0d225392018-06-07 09:59:07 -04001996 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001997 }
1998 return true;
1999}
2000
2001bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2002 // set up for error reporting
2003 fLine = fChar = child->fStart;
2004 fEnd = child->fContentEnd;
2005 // todo: put original line number in child as well
2006 switch (child->fType) {
2007 case Definition::Type::kKeyWord:
2008 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002009 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002010 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002011 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002012 }
2013 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002014 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002015 case KeyWord::kConst:
2016 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002017 if (!this->parseConst(child, markupDef)) {
2018 return child->reportError<bool>("failed to parse const or constexpr");
2019 }
2020 break;
Cary Clark8032b982017-07-28 11:04:54 -04002021 case KeyWord::kEnum:
2022 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002023 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002024 }
2025 break;
2026 case KeyWord::kStruct:
2027 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002028 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002029 }
2030 break;
2031 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002032 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002033 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002034 }
2035 break;
2036 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002037 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002038 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002039 }
2040 break;
2041 case KeyWord::kUnion:
2042 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002043 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002044 }
2045 break;
2046 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002047 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002048 }
2049 break;
2050 case Definition::Type::kBracket:
2051 switch (child->fBracket) {
2052 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04002053 if (fLastObject) {
2054 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
2055 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04002056 if (!checkDeprecated.eof()) {
2057 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002058 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2059 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002060 break;
2061 }
2062 }
2063 }
2064 {
2065 auto tokenIter = child->fParent->fTokens.begin();
2066 std::advance(tokenIter, child->fParentIndex);
2067 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002068 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002069 if (previousToken.startsWith(gAttrDeprecated)) {
2070 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002071 break;
2072 }
Cary Clarka7401902018-06-14 15:34:14 -04002073 if ('f' == previousToken.fStart[0] && isupper(previousToken.fStart[1])) {
2074 break;
2075 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002076 if (Bracket::kPound == child->fParent->fBracket &&
2077 KeyWord::kIf == child->fParent->fKeyWord) {
2078 // TODO: this will skip methods named defined() -- for the
2079 // moment there aren't any
2080 if (previousToken.startsWith("defined")) {
2081 break;
2082 }
2083 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002084 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2085 break;
2086 }
Cary Clark73fa9722017-08-29 17:36:51 -04002087 }
Cary Clark0d225392018-06-07 09:59:07 -04002088 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2089 break;
2090 }
Cary Clark8032b982017-07-28 11:04:54 -04002091 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002092 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002093 }
Cary Clark89b14562018-03-19 09:04:10 -04002094 if (fAttrDeprecated) {
2095 Definition* lastMethod = &markupDef->fTokens.back();
2096 lastMethod->fDeprecated = true;
2097 fAttrDeprecated = nullptr;
2098 }
Cary Clark73fa9722017-08-29 17:36:51 -04002099 break;
Cary Clark8032b982017-07-28 11:04:54 -04002100 case Bracket::kSlashSlash:
2101 case Bracket::kSlashStar:
2102 // comments are picked up by parsing objects first
2103 break;
2104 case Bracket::kPound:
2105 // special-case the #xxx xxx_DEFINED entries
2106 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002107 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002108 case KeyWord::kIfndef:
2109 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002110 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002111 if (!this->parseObjects(child, markupDef)) {
2112 return false;
2113 }
2114 break;
2115 }
2116 goto preproError;
2117 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002118 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002119 break;
2120 }
2121 goto preproError;
2122 case KeyWord::kEndif:
2123 if (child->boilerplateEndIf()) {
2124 break;
2125 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002126 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002127 case KeyWord::kInclude:
2128 // ignored for now
2129 break;
2130 case KeyWord::kElse:
2131 case KeyWord::kElif:
2132 // todo: handle these
2133 break;
2134 default:
2135 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002136 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002137 }
2138 break;
2139 case Bracket::kAngle:
2140 // pick up templated function pieces when method is found
2141 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002142 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002143 if (!this->parseObjects(child, markupDef)) {
2144 return false;
2145 }
Cary Clark73fa9722017-08-29 17:36:51 -04002146 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002147 case Bracket::kSquare: {
2148 // check to see if parent is operator, the only case we handle so far
2149 auto prev = child->fParent->fTokens.begin();
2150 std::advance(prev, child->fParentIndex - 1);
2151 if (KeyWord::kOperator != prev->fKeyWord) {
2152 return child->reportError<bool>("expected operator overload");
2153 }
2154 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002155 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002156 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002157 }
2158 break;
2159 case Definition::Type::kWord:
2160 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002161 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002162 }
2163 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002164 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002165 }
2166 break;
2167 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002168 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002169 break;
2170 }
2171 return true;
2172}
2173
Cary Clarkbbfda252018-03-09 15:32:01 -05002174bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2175 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002176}
2177
Cary Clark2f466242017-12-11 16:03:17 -05002178bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2179 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002180 typedefParser.skipExact("typedef");
2181 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002182 string nameStr = typedefParser.typedefName();
2183 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002184 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2185 child->fLineCount, fParent, '\0');
2186 Definition* globalMarkupChild = &fGlobals.back();
2187 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2188 globalMarkupChild->fName = globalUniqueName;
2189 if (!this->findComments(*child, globalMarkupChild)) {
2190 return false;
2191 }
2192 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002193 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002194 return true;
2195 }
2196 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002197 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002198 Definition* markupChild = &markupDef->fTokens.back();
2199 markupChild->fName = nameStr;
2200 markupChild->fTerminator = markupChild->fContentEnd;
2201 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2202 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002203 child->fName = markupDef->fName + "::" + nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002204 return true;
2205}
2206
2207bool IncludeParser::parseUnion() {
2208
2209 return true;
2210}
2211
2212bool IncludeParser::parseChar() {
2213 char test = *fChar;
2214 if ('\\' == fPrev) {
2215 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002216// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002217 fLine = fChar + 1;
2218 }
2219 goto done;
2220 }
2221 switch (test) {
2222 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002223// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002224 fLine = fChar + 1;
2225 if (fInChar) {
2226 return reportError<bool>("malformed char");
2227 }
2228 if (fInString) {
2229 return reportError<bool>("malformed string");
2230 }
2231 if (!this->checkForWord()) {
2232 return false;
2233 }
2234 if (Bracket::kPound == this->topBracket()) {
2235 KeyWord keyWord = fParent->fKeyWord;
2236 if (KeyWord::kNone == keyWord) {
2237 return this->reportError<bool>("unhandled preprocessor directive");
2238 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002239 if (fInDefine) {
2240 SkASSERT(KeyWord::kDefine == keyWord);
2241 fInDefine = false;
2242 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002243 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002244 this->popBracket();
2245 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002246 if (fInBrace) {
2247 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2248 fInBrace = nullptr;
2249 }
Cary Clark8032b982017-07-28 11:04:54 -04002250 } else if (Bracket::kSlashSlash == this->topBracket()) {
2251 this->popBracket();
2252 }
2253 break;
2254 case '*':
2255 if (!fInCharCommentString && '/' == fPrev) {
2256 this->pushBracket(Bracket::kSlashStar);
2257 }
2258 if (!this->checkForWord()) {
2259 return false;
2260 }
2261 if (!fInCharCommentString) {
2262 this->addPunctuation(Punctuation::kAsterisk);
2263 }
2264 break;
2265 case '/':
2266 if ('*' == fPrev) {
2267 if (!fInCharCommentString) {
2268 return reportError<bool>("malformed closing comment");
2269 }
2270 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002271 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002272 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002273 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002274 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002275 }
2276 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002277 }
Cary Clark8032b982017-07-28 11:04:54 -04002278 if (!fInCharCommentString && '/' == fPrev) {
2279 this->pushBracket(Bracket::kSlashSlash);
2280 break;
2281 }
2282 if (!this->checkForWord()) {
2283 return false;
2284 }
2285 break;
2286 case '\'':
2287 if (Bracket::kChar == this->topBracket()) {
2288 this->popBracket();
2289 } else if (!fInComment && !fInString) {
2290 if (fIncludeWord) {
2291 return this->reportError<bool>("word then single-quote");
2292 }
2293 this->pushBracket(Bracket::kChar);
2294 }
2295 break;
2296 case '\"':
2297 if (Bracket::kString == this->topBracket()) {
2298 this->popBracket();
2299 } else if (!fInComment && !fInChar) {
2300 if (fIncludeWord) {
2301 return this->reportError<bool>("word then double-quote");
2302 }
2303 this->pushBracket(Bracket::kString);
2304 }
2305 break;
Cary Clark8032b982017-07-28 11:04:54 -04002306 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002307 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002308 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2309 this->pushBracket(Bracket::kDebugCode);
2310 break;
2311 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002312 case ':':
2313 case '[':
2314 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002315 if (fInCharCommentString) {
2316 break;
2317 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002318 if (fInDefine && fInBrace) {
2319 break;
2320 }
Cary Clark8032b982017-07-28 11:04:54 -04002321 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2322 break;
2323 }
Cary Clark0d225392018-06-07 09:59:07 -04002324 if (fConstExpr) {
2325 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2326 fConstExpr = nullptr;
2327 }
Cary Clark8032b982017-07-28 11:04:54 -04002328 if (!fInBrace) {
2329 if (!this->checkForWord()) {
2330 return false;
2331 }
2332 if (':' == test && !fInFunction) {
2333 break;
2334 }
2335 if ('{' == test) {
2336 this->addPunctuation(Punctuation::kLeftBrace);
2337 } else if (':' == test) {
2338 this->addPunctuation(Punctuation::kColon);
2339 }
2340 }
2341 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2342 && Bracket::kColon == fInBrace->fBracket) {
2343 Definition* braceParent = fParent->fParent;
2344 braceParent->fChildren.pop_back();
2345 braceParent->fTokens.pop_back();
2346 fParent = braceParent;
2347 fInBrace = nullptr;
2348 }
2349 this->pushBracket(
2350 '(' == test ? Bracket::kParen :
2351 '[' == test ? Bracket::kSquare :
2352 '{' == test ? Bracket::kBrace :
2353 Bracket::kColon);
2354 if (!fInBrace
2355 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2356 && fInFunction) {
2357 fInBrace = fParent;
2358 }
2359 } break;
2360 case '<':
2361 if (fInCharCommentString || fInBrace) {
2362 break;
2363 }
2364 if (!this->checkForWord()) {
2365 return false;
2366 }
2367 if (fInEnum) {
2368 break;
2369 }
2370 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002371 // this angle bracket may be an operator or may be a bracket
2372 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002373 break;
2374 case ')':
2375 case ']':
2376 case '}': {
2377 if (fInCharCommentString) {
2378 break;
2379 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002380 if (fInDefine && fInBrace) {
2381 break;
2382 }
Cary Clark8032b982017-07-28 11:04:54 -04002383 if (!fInBrace) {
2384 if (!this->checkForWord()) {
2385 return false;
2386 }
2387 }
2388 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002389 Bracket match = ')' == test ? Bracket::kParen :
2390 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2391 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002392 this->popBracket();
2393 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002394 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002395 } else {
2396 fInFunction = '}' != test;
2397 }
Cary Clark73fa9722017-08-29 17:36:51 -04002398 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2399 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002400 } else if (Bracket::kAngle == this->topBracket()
2401 && match == this->grandParentBracket()) {
2402 this->popBracket();
2403 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002404 } else {
2405 return reportError<bool>("malformed close bracket");
2406 }
2407 if (popBraceParent) {
2408 Definition* braceParent = fInBrace->fParent;
2409 braceParent->fChildren.pop_back();
2410 braceParent->fTokens.pop_back();
2411 fInBrace = nullptr;
2412 }
2413 } break;
2414 case '>':
2415 if (fInCharCommentString || fInBrace) {
2416 break;
2417 }
2418 if (!this->checkForWord()) {
2419 return false;
2420 }
2421 if (fInEnum) {
2422 break;
2423 }
Cary Clarka560c472017-11-27 10:44:06 -05002424 if (Bracket::kPound == this->topBracket()) {
2425 break;
2426 }
Cary Clark8032b982017-07-28 11:04:54 -04002427 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04002428 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04002429 this->popBracket();
2430 } else {
2431 return reportError<bool>("malformed close angle bracket");
2432 }
2433 break;
2434 case '#': {
2435 if (fInCharCommentString || fInBrace) {
2436 break;
2437 }
2438 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2439 this->pushBracket(Bracket::kPound);
2440 break;
2441 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002442 case ' ':
2443 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2444 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2445 fInBrace = fParent;
2446 // delimiting brackets are space ... unescaped-linefeed
2447 }
Cary Clark8032b982017-07-28 11:04:54 -04002448 case '&':
2449 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002450 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05002451 case '-':
2452 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002453 if (fInCharCommentString || fInBrace) {
2454 break;
2455 }
2456 if (!this->checkForWord()) {
2457 return false;
2458 }
2459 break;
Cary Clark0d225392018-06-07 09:59:07 -04002460 case '=':
2461 if (fInCharCommentString || fInBrace) {
2462 break;
2463 }
2464 if (!this->checkForWord()) {
2465 return false;
2466 }
2467 {
2468 const Definition& lastToken = fParent->fTokens.back();
2469 if (lastToken.fType != Definition::Type::kWord) {
2470 break;
2471 }
2472 string name(lastToken.fContentStart, lastToken.length());
2473 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
2474 break;
2475 }
2476 // find token on start of line
2477 auto lineIter = fParent->fTokens.end();
2478 do {
Cary Clark80247e52018-07-11 16:18:41 -04002479 if (fParent->fTokens.begin() == lineIter) {
2480 break;
2481 }
Cary Clark0d225392018-06-07 09:59:07 -04002482 --lineIter;
2483 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04002484 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04002485 ++lineIter;
2486 }
2487 Definition* lineStart = &*lineIter;
2488 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
2489 bool sawConst = false;
2490 bool sawStatic = false;
2491 bool sawTemplate = false;
2492 bool sawType = false;
2493 while (&lastToken != &*lineIter) {
2494 if (KeyWord::kTemplate == lineIter->fKeyWord) {
2495 if (sawConst || sawStatic || sawTemplate) {
2496 sawConst = false;
2497 break;
2498 }
2499 if (&lastToken == &*++lineIter) {
2500 break;
2501 }
2502 if (KeyWord::kTypename != lineIter->fKeyWord) {
2503 break;
2504 }
2505 if (&lastToken == &*++lineIter) {
2506 break;
2507 }
2508 if (Definition::Type::kWord != lineIter->fType) {
2509 break;
2510 }
2511 sawTemplate = true;
2512 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
2513 if (sawConst || sawStatic) {
2514 sawConst = false;
2515 break;
2516 }
2517 sawStatic = true;
2518 } else if (KeyWord::kConst == lineIter->fKeyWord
2519 || KeyWord::kConstExpr == lineIter->fKeyWord) {
2520 if (sawConst) {
2521 sawConst = false;
2522 break;
2523 }
2524 sawConst = true;
2525 } else {
2526 if (sawType) {
2527 sawType = false;
2528 break;
2529 }
2530 if (Definition::Type::kKeyWord == lineIter->fType
2531 && KeyProperty::kNumber
2532 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
2533 sawType = true;
2534 } else if (Definition::Type::kWord == lineIter->fType) {
2535 string typeName(lineIter->fContentStart, lineIter->length());
2536 if ("Sk" != name.substr(0, 2)) {
2537 sawType = true;
2538 }
2539 }
2540 }
2541 ++lineIter;
2542 }
2543 if (sawType && sawConst) {
2544 // if found, name first
2545 lineStart->fName = name;
2546 lineStart->fMarkType = MarkType::kConst;
2547 fParent->fChildren.emplace_back(lineStart);
2548 fConstExpr = lineStart;
2549 }
2550 }
2551 break;
Cary Clark8032b982017-07-28 11:04:54 -04002552 case ';':
2553 if (fInCharCommentString || fInBrace) {
2554 break;
2555 }
2556 if (!this->checkForWord()) {
2557 return false;
2558 }
Cary Clark0d225392018-06-07 09:59:07 -04002559 if (fConstExpr) {
2560 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2561 fConstExpr = nullptr;
2562 }
Cary Clark8032b982017-07-28 11:04:54 -04002563 if (Definition::Type::kKeyWord == fParent->fType
2564 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002565 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2566 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002567 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2568 this->popObject();
2569 }
Cary Clark8032b982017-07-28 11:04:54 -04002570 if (KeyWord::kEnum == fParent->fKeyWord) {
2571 fInEnum = false;
2572 }
2573 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002574 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2575 this->popObject();
2576 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002577 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002578 } else if (Definition::Type::kBracket == fParent->fType
2579 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2580 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2581 list<Definition>::iterator baseIter = fParent->fTokens.end();
2582 list<Definition>::iterator namedIter = fParent->fTokens.end();
2583 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04002584 fParent->fTokens.begin() != tokenIter; ) {
2585 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04002586 if (tokenIter->fLineCount == fLineCount) {
2587 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2588 if (namedIter != fParent->fTokens.end()) {
2589 return reportError<bool>("found two named member tokens");
2590 }
2591 namedIter = tokenIter;
2592 }
2593 baseIter = tokenIter;
2594 } else {
2595 break;
2596 }
2597 }
2598 // FIXME: if a member definition spans multiple lines, this won't work
2599 if (namedIter != fParent->fTokens.end()) {
2600 if (baseIter == namedIter) {
2601 return this->reportError<bool>("expected type before named token");
2602 }
2603 Definition* member = &*namedIter;
2604 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002605 if (!member->fTerminator) {
2606 member->fTerminator = member->fContentEnd;
2607 }
Cary Clark8032b982017-07-28 11:04:54 -04002608 fParent->fChildren.push_back(member);
2609 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2610 member->fChildren.push_back(&*nameType);
2611 }
Cary Clark8032b982017-07-28 11:04:54 -04002612 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002613 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002614 } else if (fParent->fChildren.size() > 0) {
2615 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002616 Definition* priorEnum = fPriorEnum;
2617 fPriorEnum = nullptr;
2618 if (!priorEnum) {
2619 while (fParent->fChildren.begin() != lastIter) {
2620 std::advance(lastIter, -1);
2621 priorEnum = *lastIter;
2622 if (Definition::Type::kBracket != priorEnum->fType ||
2623 (Bracket::kSlashSlash != priorEnum->fBracket
2624 && Bracket::kSlashStar != priorEnum->fBracket)) {
2625 break;
2626 }
Cary Clark8032b982017-07-28 11:04:54 -04002627 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002628 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002629 }
2630 if (Definition::Type::kKeyWord == priorEnum->fType
2631 && KeyWord::kEnum == priorEnum->fKeyWord) {
2632 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002633 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002634 while (tokenWalker != fParent->fTokens.end()) {
2635 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002636 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002637 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2638 break;
2639 }
2640 }
2641 while (tokenWalker != fParent->fTokens.end()) {
2642 std::advance(tokenWalker, 1);
2643 const Definition* test = &*tokenWalker;
2644 if (Definition::Type::kBracket != test->fType ||
2645 (Bracket::kSlashSlash != test->fBracket
2646 && Bracket::kSlashStar != test->fBracket)) {
2647 break;
2648 }
2649 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002650 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002651 Definition* start = &*tokenWalker;
2652 bool foundExpected = true;
2653 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2654 const Definition* test = &*tokenWalker;
2655 if (expected != test->fKeyWord) {
2656 foundExpected = false;
2657 break;
2658 }
2659 if (tokenWalker == fParent->fTokens.end()) {
2660 break;
2661 }
2662 std::advance(tokenWalker, 1);
2663 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002664 if (!foundExpected) {
2665 foundExpected = true;
2666 tokenWalker = saveTokenWalker;
2667 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2668 const Definition* test = &*tokenWalker;
2669 if (expected != test->fKeyWord) {
2670 foundExpected = false;
2671 break;
2672 }
2673 if (tokenWalker == fParent->fTokens.end()) {
2674 break;
2675 }
2676 if (KeyWord::kNone != expected) {
2677 std::advance(tokenWalker, 1);
2678 }
2679 }
2680 if (foundExpected) {
2681 auto nameToken = priorEnum->fTokens.begin();
2682 string enumName = string(nameToken->fContentStart,
2683 nameToken->fContentEnd - nameToken->fContentStart);
2684 const Definition* test = &*tokenWalker;
2685 string constType = string(test->fContentStart,
2686 test->fContentEnd - test->fContentStart);
2687 if (enumName != constType) {
2688 foundExpected = false;
2689 } else {
2690 std::advance(tokenWalker, 1);
2691 }
2692 }
2693 }
Cary Clark8032b982017-07-28 11:04:54 -04002694 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2695 const char* nameStart = tokenWalker->fStart;
2696 std::advance(tokenWalker, 1);
2697 if (tokenWalker != fParent->fTokens.end()) {
2698 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002699 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002700 start->fName = string(nameStart, tp.fChar - nameStart);
2701 start->fContentEnd = fChar;
2702 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002703 fPriorEnum = priorEnum;
2704 }
Cary Clark8032b982017-07-28 11:04:54 -04002705 }
2706 }
2707 }
2708 this->addPunctuation(Punctuation::kSemicolon);
2709 fInFunction = false;
2710 break;
2711 case '~':
2712 if (fInEnum) {
2713 break;
2714 }
2715 case '0': case '1': case '2': case '3': case '4':
2716 case '5': case '6': case '7': case '8': case '9':
2717 // TODO: don't want to parse numbers, but do need to track for enum defs
2718 // break;
2719 case 'A': case 'B': case 'C': case 'D': case 'E':
2720 case 'F': case 'G': case 'H': case 'I': case 'J':
2721 case 'K': case 'L': case 'M': case 'N': case 'O':
2722 case 'P': case 'Q': case 'R': case 'S': case 'T':
2723 case 'U': case 'V': case 'W': case 'X': case 'Y':
2724 case 'Z': case '_':
2725 case 'a': case 'b': case 'c': case 'd': case 'e':
2726 case 'f': case 'g': case 'h': case 'i': case 'j':
2727 case 'k': case 'l': case 'm': case 'n': case 'o':
2728 case 'p': case 'q': case 'r': case 's': case 't':
2729 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002730 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002731 if (fInCharCommentString || fInBrace) {
2732 break;
2733 }
2734 if (!fIncludeWord) {
2735 fIncludeWord = fChar;
2736 }
2737 break;
2738 }
2739done:
2740 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002741 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002742 return true;
2743}
2744
2745void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04002746 IncludeParser::ValidateKeyWords();
2747}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002748
Cary Clark186d08f2018-04-03 08:43:27 -04002749bool IncludeParser::references(const SkString& file) const {
2750 // if includes weren't passed one at a time, assume all references are valid
2751 if (fIncludeMap.empty()) {
2752 return true;
2753 }
2754 SkASSERT(file.endsWith(".bmh") );
2755 string root(file.c_str(), file.size() - 4);
2756 string kReference("_Reference");
2757 if (string::npos != root.find(kReference)) {
2758 root = root.substr(0, root.length() - kReference.length());
2759 }
2760 if (fIClassMap.end() != fIClassMap.find(root)) {
2761 return true;
2762 }
2763 if (fIStructMap.end() != fIStructMap.find(root)) {
2764 return true;
2765 }
Cary Clark224c7002018-06-27 11:00:21 -04002766 if (fIEnumMap.end() != fIEnumMap.find(root)) {
2767 return true;
2768 }
2769 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
2770 return true;
2771 }
Cary Clark186d08f2018-04-03 08:43:27 -04002772 return false;
2773}
2774
Cary Clark2dc84ad2018-01-26 12:56:22 -05002775void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2776 if (!sk_isdir(includes)) {
2777 IncludeParser::RemoveOneFile(docs, includes);
2778 } else {
2779 SkOSFile::Iter it(includes, ".h");
2780 for (SkString file; it.next(&file); ) {
2781 SkString p = SkOSPath::Join(includes, file.c_str());
2782 const char* hunk = p.c_str();
2783 if (!SkStrEndsWith(hunk, ".h")) {
2784 continue;
2785 }
2786 IncludeParser::RemoveOneFile(docs, hunk);
2787 }
2788 }
2789}
2790
2791void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2792 const char* lastForward = strrchr(includesFile, '/');
2793 const char* lastBackward = strrchr(includesFile, '\\');
2794 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2795 if (!last) {
2796 last = includesFile;
2797 } else {
2798 last += 1;
2799 }
2800 SkString baseName(last);
2801 SkASSERT(baseName.endsWith(".h"));
2802 baseName.remove(baseName.size() - 2, 2);
2803 baseName.append("_Reference.bmh");
2804 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2805 remove(fullName.c_str());
2806}
Cary Clark224c7002018-06-27 11:00:21 -04002807
2808Bracket IncludeParser::topBracket() const {
2809 Definition* parent = this->parentBracket(fParent);
2810 return parent ? parent->fBracket : Bracket::kNone;
2811}