blob: eca72963b09d1d991178f60f34af80fe2ff6855b [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 Clarkd2ca79c2018-08-10 13:09:13 -040019 { "alignas", KeyWord::kAlignAs, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040020 { "bool", KeyWord::kBool, KeyProperty::kNumber },
21 { "char", KeyWord::kChar, KeyProperty::kNumber },
22 { "class", KeyWord::kClass, KeyProperty::kObject },
23 { "const", KeyWord::kConst, KeyProperty::kModifier },
24 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
25 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
26 { "double", KeyWord::kDouble, KeyProperty::kNumber },
27 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
28 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
29 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
30 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050031 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040032 { "float", KeyWord::kFloat, KeyProperty::kNumber },
33 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
34 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
35 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
36 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
37 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
38 { "inline", KeyWord::kInline, KeyProperty::kModifier },
39 { "int", KeyWord::kInt, KeyProperty::kNumber },
40 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
41 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
42 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
43 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
44 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
45 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
46 { "static", KeyWord::kStatic, KeyProperty::kModifier },
47 { "struct", KeyWord::kStruct, KeyProperty::kObject },
48 { "template", KeyWord::kTemplate, KeyProperty::kObject },
49 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clark0d225392018-06-07 09:59:07 -040050 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040051 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040052 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040053 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
54 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040055 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040056 { "union", KeyWord::kUnion, KeyProperty::kObject },
57 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
58 { "void", KeyWord::kVoid, KeyProperty::kNumber },
59};
60
61const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
62
63KeyWord IncludeParser::FindKey(const char* start, const char* end) {
64 int ch = 0;
65 for (size_t index = 0; index < kKeyWordCount; ) {
66 if (start[ch] > kKeyWords[index].fName[ch]) {
67 ++index;
68 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
69 return KeyWord::kNone;
70 }
71 continue;
72 }
73 if (start[ch] < kKeyWords[index].fName[ch]) {
74 return KeyWord::kNone;
75 }
76 ++ch;
77 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040078 if (end - start < (int) strlen(kKeyWords[index].fName)) {
79 return KeyWord::kNone;
80 }
Cary Clark8032b982017-07-28 11:04:54 -040081 return kKeyWords[index].fKeyWord;
82 }
83 }
84 return KeyWord::kNone;
85}
86
87void IncludeParser::ValidateKeyWords() {
88 for (size_t index = 1; index < kKeyWordCount; ++index) {
89 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
90 == (int) kKeyWords[index].fKeyWord);
91 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
92 }
93}
94
95void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050096 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040097 fIncludeWord = nullptr;
98 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
99 Definition* def = &fParent->fTokens.back();
100 this->addDefinition(def);
101 if (KeyWord::kEnum == fParent->fKeyWord) {
102 fInEnum = true;
103 }
104 }
105}
106
Ben Wagner63fd7602017-10-09 15:45:33 -0400107void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400108 const vector<string>& foundParams) {
109 for (auto& methodParam : methodParams) {
110 bool found = false;
111 for (auto& foundParam : foundParams) {
112 if (methodParam == foundParam) {
113 found = true;
114 break;
115 }
116 }
117 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400118 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400119 }
120 }
121 for (auto& foundParam : foundParams) {
122 bool found = false;
123 for (auto& methodParam : methodParams) {
124 if (methodParam == foundParam) {
125 found = true;
126 break;
127 }
128 }
129 if (!found) {
130 this->reportError("doxygen param does not match method declaration");
131 }
132 }
133}
134
135bool IncludeParser::checkForWord() {
136 if (!fIncludeWord) {
137 return true;
138 }
139 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400140 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
141 Bracket bracket = this->topBracket();
142 if (Bracket::kParen == bracket) {
143 return true;
144 }
145 }
Cary Clark8032b982017-07-28 11:04:54 -0400146 if (KeyWord::kNone != keyWord) {
147 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
148 this->addKeyword(keyWord);
149 return true;
150 }
151 } else {
152 this->addWord();
153 return true;
154 }
155 Definition* poundDef = fParent;
156 if (!fParent) {
157 return reportError<bool>("expected parent");
158 }
159 if (Definition::Type::kBracket != poundDef->fType) {
160 return reportError<bool>("expected bracket");
161 }
162 if (Bracket::kPound != poundDef->fBracket) {
163 return reportError<bool>("expected preprocessor");
164 }
165 if (KeyWord::kNone != poundDef->fKeyWord) {
166 return reportError<bool>("already found keyword");
167 }
168 poundDef->fKeyWord = keyWord;
169 fIncludeWord = nullptr;
170 switch (keyWord) {
171 // these do not link to other # directives
172 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400173 if (!fInBrace) {
174 SkASSERT(!fInDefine);
175 fInDefine = true;
176 }
Cary Clark8032b982017-07-28 11:04:54 -0400177 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500178 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400179 break;
180 // these start a # directive link
181 case KeyWord::kIf:
182 case KeyWord::kIfdef:
183 case KeyWord::kIfndef:
184 break;
185 // these continue a # directive link
186 case KeyWord::kElif:
187 case KeyWord::kElse: {
188 this->popObject(); // pop elif
189 if (Bracket::kPound != fParent->fBracket) {
190 return this->reportError<bool>("expected preprocessor directive");
191 }
192 this->popBracket(); // pop if
193 poundDef->fParent = fParent;
194 this->addDefinition(poundDef); // push elif back
195 } break;
196 // this ends a # directive link
197 case KeyWord::kEndif:
198 // FIXME : should this be calling popBracket() instead?
199 this->popObject(); // pop endif
200 if (Bracket::kPound != fParent->fBracket) {
201 return this->reportError<bool>("expected preprocessor directive");
202 }
203 this->popBracket(); // pop if/else
204 break;
205 default:
206 SkASSERT(0);
207 }
208 return true;
209}
210
211string IncludeParser::className() const {
212 string name(fParent->fName);
213 size_t slash = name.find_last_of("/");
214 if (string::npos == slash) {
215 slash = name.find_last_of("\\");
216 }
217 SkASSERT(string::npos != slash);
218 string result = name.substr(slash);
219 result = result.substr(1, result.size() - 3);
220 return result;
221}
222
Cary Clark884dd7d2017-10-11 10:37:52 -0400223#include <sstream>
224#include <iostream>
225
Cary Clark8032b982017-07-28 11:04:54 -0400226bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400227 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400228 string className = classMapper.first;
229 auto finder = bmhParser.fClassMap.find(className);
230 if (bmhParser.fClassMap.end() == finder) {
231 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400232 continue;
233 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400234 RootDefinition* root = &finder->second;
235 root->clearVisited();
236 }
237 for (auto& classMapper : fIClassMap) {
238 string className = classMapper.first;
239 std::istringstream iss(className);
240 string classStr;
241 string classBase;
242 RootDefinition* root = nullptr;
243 while (std::getline(iss, classStr, ':')) {
244 if (root) {
245 if (!classStr.length()) {
246 continue;
247 }
248 classBase += "::" + classStr;
249 auto finder = root->fBranches.find(classBase);
250 if (root->fBranches.end() != finder) {
251 root = finder->second;
252 } else {
253 SkASSERT(0);
254 }
255 } else {
256 classBase = classStr;
257 auto finder = bmhParser.fClassMap.find(classBase);
258 if (bmhParser.fClassMap.end() != finder) {
259 root = &finder->second;
260 } else {
261 SkASSERT(0);
262 }
263 }
264 }
Cary Clark8032b982017-07-28 11:04:54 -0400265 auto& classMap = classMapper.second;
266 auto& tokens = classMap.fTokens;
267 for (const auto& token : tokens) {
268 if (token.fPrivate) {
269 continue;
270 }
271 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400272 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400273 switch (token.fMarkType) {
274 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400275 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400276 continue;
277 }
Cary Clark8032b982017-07-28 11:04:54 -0400278 if (!def) {
279 string paramName = className + "::";
280 paramName += string(token.fContentStart,
281 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400282 if (string::npos != paramName.find('\n')) {
283 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
284 paramName.end());
285 }
Cary Clarkce101242017-09-01 15:51:02 -0400286 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400287 if (!def && 0 == token.fName.find("operator")) {
288 string operatorName = className + "::";
289 TextParser oper("", token.fStart, token.fContentEnd, 0);
290 const char* start = oper.strnstr("operator", token.fContentEnd);
291 SkASSERT(start);
292 oper.skipTo(start);
293 oper.skipToEndBracket('(');
294 int parens = 0;
295 do {
296 if ('(' == oper.peek()) {
297 ++parens;
298 } else if (')' == oper.peek()) {
299 --parens;
300 }
301 } while (!oper.eof() && oper.next() && parens > 0);
302 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400303 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400304 }
305 }
306 if (!def) {
307 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
308 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
Cary Clarkab5c9af2018-07-12 16:24:53 -0400309 const char* tokenEnd = token.methodEnd();
Cary Clark8032b982017-07-28 11:04:54 -0400310 string constructorName = className + "::";
311 constructorName += string(token.fContentStart + skip,
Cary Clarkab5c9af2018-07-12 16:24:53 -0400312 tokenEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400313 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400314 }
315 if (!def && 0 == token.fName.find("SK_")) {
316 string incName = token.fName + "()";
317 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400318 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400319 if (def) {
320 if (def->fName == incName) {
321 def->fVisited = true;
322 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400323 def = root->find(className + "::toString",
324 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400325 if (def) {
326 def->fVisited = true;
327 } else {
328 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500329 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400330 }
331 }
332 break;
333 } else {
334 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500335 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400336 }
337 }
338 }
339 if (!def) {
340 bool allLower = true;
341 for (size_t index = 0; index < token.fName.length(); ++index) {
342 if (!islower(token.fName[index])) {
343 allLower = false;
344 break;
345 }
346 }
347 if (allLower) {
348 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400349 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400350 }
351 }
352 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400353 if (gAttrDeprecated == token.fName) {
354 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400355 break;
356 }
357 if (0 == token.fName.find("SkDEBUGCODE")) {
358 break;
359 }
360 }
361 if (!def) {
362 // simple method names inside nested classes have a bug and are missing trailing parens
363 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400364 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400365 }
366 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500367 if (!root->fDeprecated) {
368 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
369 fFailed = true;
370 }
Cary Clark8032b982017-07-28 11:04:54 -0400371 break;
372 }
Cary Clark73fa9722017-08-29 17:36:51 -0400373 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400374 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400375 if (token.fDeprecated && !def->fDeprecated) {
376 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
377 }
Cary Clark8032b982017-07-28 11:04:54 -0400378 } else {
379 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500380 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400381 }
382 } break;
383 case MarkType::kComment:
384 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400385 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400386 case MarkType::kEnum: {
387 if (!def) {
388 // work backwards from first word to deduce #Enum name
389 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
390 SkAssertResult(firstMember.skipName("enum"));
391 SkAssertResult(firstMember.skipToEndBracket('{'));
392 firstMember.next();
393 firstMember.skipWhiteSpace();
394 SkASSERT('k' == firstMember.peek());
395 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400396 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400397 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400398 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400399 const char* lastUnderscore = nullptr;
400 do {
401 if (!firstMember.skipToEndBracket('_')) {
402 break;
403 }
404 if (firstMember.fChar > wordEnd) {
405 break;
406 }
407 lastUnderscore = firstMember.fChar;
408 } while (firstMember.next());
409 if (lastUnderscore) {
410 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400411 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400412 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400413 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400414 }
415 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500416 if (!root->fDeprecated) {
417 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
418 fFailed = true;
419 }
Cary Clark8032b982017-07-28 11:04:54 -0400420 break;
421 }
422 }
423 def->fVisited = true;
424 for (auto& child : def->fChildren) {
425 if (MarkType::kCode == child->fMarkType) {
426 def = child;
427 break;
428 }
429 }
430 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500431 if (!root->fDeprecated) {
432 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
433 fFailed = true;
434 }
Cary Clark8032b982017-07-28 11:04:54 -0400435 break;
436 }
437 if (def->crossCheck(token)) {
438 def->fVisited = true;
439 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500440 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
441 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400442 }
443 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400444 string constName = MarkType::kEnumClass == token.fMarkType ?
445 fullName : className;
446 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400447 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400448 if (!def) {
449 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400450 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400451 }
452 if (!def) {
453 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500454 if (!root->fDeprecated) {
455 SkDebugf("const missing from bmh: %s\n", constName.c_str());
456 fFailed = true;
457 }
Cary Clark8032b982017-07-28 11:04:54 -0400458 }
459 } else {
460 def->fVisited = true;
461 }
462 }
463 } break;
464 case MarkType::kMember:
465 if (def) {
466 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500467 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400468 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500469 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400470 }
471 break;
Cary Clark2f466242017-12-11 16:03:17 -0500472 case MarkType::kTypedef:
473 if (def) {
474 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500475 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500476 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500477 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500478 }
479 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400480 case MarkType::kConst:
481 if (def) {
482 def->fVisited = true;
483 } else if (!root->fDeprecated) {
484 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
485 fFailed = true;
486 }
487 break;
Cary Clark8032b982017-07-28 11:04:54 -0400488 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400489 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400490 break;
491 }
492 }
493 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500494 int crossChecks = 0;
495 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400496 for (auto& classMapper : fIClassMap) {
497 string className = classMapper.first;
498 auto finder = bmhParser.fClassMap.find(className);
499 if (bmhParser.fClassMap.end() == finder) {
500 continue;
501 }
502 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500503 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500504 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400505 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500506 if (crossChecks) {
507 SkDebugf(".");
508 } else {
509 SkDebugf("cross-check");
510 firstCheck = className;
511 }
512 ++crossChecks;
513 }
514 if (crossChecks) {
515 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500516 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500517 }
518 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400519 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400520 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500521 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400522}
523
524IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400525 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400526 string className;
527 const Definition* test = fParent;
528 while (Definition::Type::kFileType != test->fType) {
529 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
530 className = test->fName + "::";
531 break;
532 }
533 test = test->fParent;
534 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400535 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400536 unordered_map<string, IClassDefinition>& map = fIClassMap;
537 IClassDefinition& markupDef = map[className];
538 if (markupDef.fStart) {
539 typedef IClassDefinition* IClassDefPtr;
540 return INHERITED::reportError<IClassDefPtr>("class already defined");
541 }
542 markupDef.fFileName = fFileName;
543 markupDef.fStart = includeDef.fStart;
544 markupDef.fContentStart = includeDef.fStart;
545 markupDef.fName = className;
546 markupDef.fContentEnd = includeDef.fContentEnd;
547 markupDef.fTerminator = includeDef.fTerminator;
548 markupDef.fParent = fParent;
549 markupDef.fLineCount = fLineCount;
550 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
551 MarkType::kStruct : MarkType::kClass;
552 markupDef.fKeyWord = includeDef.fKeyWord;
553 markupDef.fType = Definition::Type::kMark;
554 fParent = &markupDef;
555 return &markupDef;
556}
557
558void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
559 auto& tokens = classDef.fTokens;
560 for (auto& token : tokens) {
561 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
562 continue;
563 }
564 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400565 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400566 }
567 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400568 case MarkType::kConst:
569 this->dumpConst(token, classDef.fName);
570 break;
Cary Clark8032b982017-07-28 11:04:54 -0400571 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500572 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500573 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400574 break;
575 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400576 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400577 break;
578 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400579 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400580 continue;
581 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400582 case MarkType::kTypedef:
583 this->dumpTypedef(token, classDef.fName);
584 break;
Cary Clark8032b982017-07-28 11:04:54 -0400585 default:
586 SkASSERT(0);
587 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400588 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400589 }
590}
Cary Clark9174bda2017-09-19 17:39:32 -0400591void IncludeParser::dumpComment(const Definition& token) {
592 fLineCount = token.fLineCount;
593 fChar = fLine = token.fContentStart;
594 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400595 bool sawParam = false;
596 bool multiline = false;
597 bool sawReturn = false;
598 bool sawComment = false;
599 bool methodHasReturn = false;
600 vector<string> methodParams;
601 vector<string> foundParams;
602 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400603 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
604 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500605 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400606 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500607 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400608 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400609 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500610 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400611 && !methodParser.strnchr('~', methodParser.fEnd);
612 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
613 const char* nextEnd = paren;
614 do {
615 string paramName;
616 methodParser.fChar = nextEnd + 1;
617 methodParser.skipSpace();
618 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
619 continue;
620 }
621 methodParams.push_back(paramName);
622 } while (')' != nextEnd[0]);
623 }
Cary Clark9174bda2017-09-19 17:39:32 -0400624 for (const auto& child : token.fTokens) {
625 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
626 break;
627 }
Cary Clark8032b982017-07-28 11:04:54 -0400628 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400629 if (child.fPrivate) {
630 break;
631 }
Cary Clark8032b982017-07-28 11:04:54 -0400632 if ('@' == child.fContentStart[0]) {
633 TextParser parser(&child);
634 do {
635 parser.next();
636 if (parser.startsWith("param ")) {
637 parser.skipWord("param");
638 const char* parmStart = parser.fChar;
639 parser.skipToSpace();
640 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
641 parser.skipWhiteSpace();
642 do {
643 size_t nextComma = parmName.find(',');
644 string piece;
645 if (string::npos == nextComma) {
646 piece = parmName;
647 parmName = "";
648 } else {
649 piece = parmName.substr(0, nextComma);
650 parmName = parmName.substr(nextComma + 1);
651 }
652 if (sawParam) {
653 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400654 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400655 }
Cary Clark9174bda2017-09-19 17:39:32 -0400656 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400657 } else {
658 if (sawComment) {
659 this->nl();
660 }
661 this->lf(2);
662 }
663 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400664 this->writeTag("Param", piece);
665 this->writeSpace(2);
666 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
667 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400668 sawParam = true;
669 sawComment = false;
670 } while (parmName.length());
671 parser.skipTo(parser.fEnd);
672 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
673 parser.skipWord("return");
674 if ('s' == parser.peek()) {
675 parser.next();
676 }
677 if (sawParam) {
678 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400679 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400680 }
Cary Clark9174bda2017-09-19 17:39:32 -0400681 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400682 }
683 this->checkForMissingParams(methodParams, foundParams);
684 sawParam = false;
685 sawComment = false;
686 multiline = false;
687 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400688 this->writeTag("Return");
689 this->writeSpace(2);
690 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
691 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400692 sawReturn = true;
693 parser.skipTo(parser.fEnd);
694 } else {
695 this->reportError("unexpected doxygen directive");
696 }
697 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400698 } else if (child.length() > 1) {
699 const char* start = child.fContentStart;
700 ptrdiff_t length = child.fContentEnd - start;
701 SkASSERT(length >= 0);
702 while (length && '/' == start[0]) {
703 start += 1;
704 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400705 }
Cary Clark9174bda2017-09-19 17:39:32 -0400706 while (length && '/' == start[length - 1]) {
707 length -= 1;
708 if (length && '*' == start[length - 1]) {
709 length -= 1;
710 }
711 }
712 if (length) {
713 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
714 if (sawParam || sawReturn) {
715 this->indentToColumn(8);
716 }
717 this->writeBlock(length, start);
718 this->writeSpace();
719 sawComment = true;
720 if (sawParam || sawReturn) {
721 multiline = true;
722 }
Cary Clark8032b982017-07-28 11:04:54 -0400723 }
724 }
725 }
726 }
727 if (sawParam || sawReturn) {
728 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400729 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400730 }
Cary Clark9174bda2017-09-19 17:39:32 -0400731 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400732 }
733 if (!sawReturn) {
734 if (!sawParam) {
735 if (sawComment) {
736 this->nl();
737 }
738 this->lf(2);
739 }
740 this->checkForMissingParams(methodParams, foundParams);
741 }
742 if (methodHasReturn != sawReturn) {
743 if (!methodHasReturn) {
744 this->reportError("unexpected doxygen return");
745 } else {
746 if (sawComment) {
747 this->nl();
748 }
749 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400750 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400751 }
752 }
753}
754
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400755void IncludeParser::dumpCommonTail(const Definition& token) {
756 this->lf(2);
757 this->writeTag("Example");
758 this->lf(1);
759 this->writeString("// incomplete");
760 this->lf(1);
761 this->writeEndTag();
762 this->lf(2);
763 this->writeTag("SeeAlso");
764 this->writeSpace();
765 this->writeString("incomplete");
766 this->lf(2);
767 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
768 this->lf(2);
769}
770
Cary Clark224c7002018-06-27 11:00:21 -0400771void IncludeParser::dumpConst(const Definition& token, string className) {
772 this->writeTag("Const");
773 this->writeSpace();
774 this->writeString(token.fName);
775 this->writeTagTable("Line", "incomplete");
776 this->lf(2);
777 this->dumpComment(token);
778}
779
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400780void IncludeParser::dumpDefine(const Definition& token) {
781 this->writeTag("Define", token.fName);
782 this->lf(2);
783 this->writeTag("Code");
784 this->lfAlways(1);
785 this->writeString("###$");
786 this->lfAlways(1);
787 this->indentToColumn(4);
788 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
789 this->lf(1);
790 this->indentToColumn(0);
791 this->writeString("$$$#");
792
793 this->writeEndTag();
794 this->lf(2);
795 this->dumpComment(token);
796 for (auto& child : token.fTokens) {
797 if (MarkType::kComment == child.fMarkType) {
798 continue;
799 }
800 this->writeTag("Param", child.fName);
801 this->writeSpace();
802 this->writeString("incomplete");
803 this->writeSpace();
804 this->writeString("##");
805 this->lf(1);
806 }
807}
808
809void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500810 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400811 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400812 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400813 this->lfAlways(1);
814 this->indentToColumn(4);
815 this->writeString("enum");
816 this->writeSpace();
817 if ("_anonymous" != token.fName.substr(0, 10)) {
818 this->writeString(token.fName);
819 this->writeSpace();
820 }
821 this->writeString("{");
822 this->lfAlways(1);
823 for (auto& child : token.fChildren) {
824 this->indentToColumn(8);
825 this->writeString(child->fName);
826 if (child->length()) {
827 this->writeSpace();
828 this->writeBlock(child->length(), child->fContentStart);
829 }
830 if (',' != fLastChar) {
831 this->writeString(",");
832 }
833 this->lfAlways(1);
834 }
835 this->indentToColumn(4);
836 this->writeString("};");
837 this->lf(1);
838 this->writeString("##");
839 this->lf(2);
840 this->dumpComment(token);
841 for (auto& child : token.fChildren) {
842 // start here;
843 // get comments before
844 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400845 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400846 this->writeSpace();
847 this->writeString(child->fName);
848 TextParser val(child);
849 if (!val.eof()) {
850 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
851 val.next();
852 val.skipSpace();
853 const char* valEnd = val.anyOf(",\n");
854 if (!valEnd) {
855 valEnd = val.fEnd;
856 }
857 this->writeSpace();
858 this->writeBlock(valEnd - val.fStart, val.fStart);
859 } else {
860 this->writeSpace();
861 this->writeDefinition(*child);
862 }
863 }
864 this->lf(1);
865 for (auto comment : child->fChildren) {
866 if (MarkType::kComment == comment->fMarkType) {
867 TextParser parser(comment);
868 parser.skipExact("*");
869 parser.skipExact("*");
870 while (!parser.eof() && parser.skipWhiteSpace()) {
871 parser.skipExact("*");
872 parser.skipWhiteSpace();
873 const char* start = parser.fChar;
874 parser.skipToEndBracket('\n');
875 this->lf(1);
876 this->writeBlock(parser.fChar - start, start);
877 }
878 }
879 }
880 this->writeEndTag();
881 }
882 this->lf(2);
883}
884
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400885bool IncludeParser::dumpGlobals(string* globalFileName, long int* globalTell) {
886 bool hasGlobals = !fIDefineMap.empty() || !fIFunctionMap.empty() || !fIEnumMap.empty()
887 || !fITemplateMap.empty()|| !fITypedefMap.empty() || !fIUnionMap.empty();
888 if (!hasGlobals) {
Cary Clark224c7002018-06-27 11:00:21 -0400889 return true;
890 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400891 size_t lastBSlash = fFileName.rfind('\\');
892 size_t lastSlash = fFileName.rfind('/');
893 size_t lastDotH = fFileName.rfind(".h");
894 SkASSERT(string::npos != lastDotH);
895 if (string::npos != lastBSlash && (string::npos == lastSlash
896 || lastBSlash < lastSlash)) {
897 lastSlash = lastBSlash;
898 } else if (string::npos == lastSlash) {
899 lastSlash = -1;
900 }
901 lastSlash += 1;
902 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
903 string fileName = globalsName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400904 *globalFileName = fileName;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400905 fOut = fopen(fileName.c_str(), "wb");
906 if (!fOut) {
907 SkDebugf("could not open output file %s\n", globalsName.c_str());
908 return false;
909 }
910 string prefixName = globalsName.substr(0, 2);
911 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
912 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
913 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -0400914 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400915 this->lf(2);
916 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400917 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400918 this->writeEndTag();
919 this->lf(2);
920 if (!fIDefineMap.empty()) {
921 this->writeTag("Subtopic", "Define");
922 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400923 this->writeEndTag();
924 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400925 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400926 if (!fIFunctionMap.empty()) {
927 this->writeTag("Subtopic", "Function");
928 this->writeTag("Populate");
929 this->writeEndTag();
930 this->lf(2);
931 }
932 if (!fIEnumMap.empty()) {
933 this->writeTag("Subtopic", "Enum");
934 this->writeTag("Populate");
935 this->writeEndTag();
936 this->lf(2);
937 }
938 if (!fITemplateMap.empty()) {
939 this->writeTag("Subtopic", "Template");
940 this->writeTag("Populate");
941 this->writeEndTag();
942 this->lf(2);
943 }
944 if (!fITypedefMap.empty()) {
945 this->writeTag("Subtopic", "Typedef");
946 this->writeTag("Populate");
947 this->writeEndTag();
948 this->lf(2);
949 }
950 if (!fIUnionMap.empty()) {
951 this->writeTag("Subtopic", "Union");
952 this->writeTag("Populate");
953 this->writeEndTag();
954 this->lf(2);
955 }
956 std::map<int, Definition*> sortedDefs;
957 for (const auto& entry : fIDefineMap) {
958 sortedDefs[entry.second->fLineCount] = entry.second;
959 }
960 for (const auto& entry : fIFunctionMap) {
961 sortedDefs[entry.second->fLineCount] = entry.second;
962 }
963 for (const auto& entry : fIEnumMap) {
964 sortedDefs[entry.second->fLineCount] = entry.second;
965 }
966 for (const auto& entry : fITemplateMap) {
967 sortedDefs[entry.second->fLineCount] = entry.second;
968 }
969 for (const auto& entry : fITypedefMap) {
970 sortedDefs[entry.second->fLineCount] = entry.second;
971 }
972 for (const auto& entry : fIUnionMap) {
973 sortedDefs[entry.second->fLineCount] = entry.second;
974 }
975 for (const auto& entry : sortedDefs) {
976 const Definition* def = entry.second;
977 this->writeBlockSeparator();
978 switch (def->fMarkType) {
979 case MarkType::kDefine:
980 this->dumpDefine(*def);
981 break;
982 case MarkType::kMethod:
983 this->dumpMethod(*def, globalsName);
984 break;
985 case MarkType::kEnum:
986 case MarkType::kEnumClass:
987 this->dumpEnum(*def, globalsName);
988 break;
989 case MarkType::kTemplate:
990 SkASSERT(0); // incomplete
991 break;
992 case MarkType::kTypedef: {
993 this->writeTag("Typedef");
994 this->writeSpace();
995 TextParser parser(def);
996 if (!parser.skipExact("typedef")) {
997 return false;
998 }
999 if (!parser.skipSpace()) {
1000 return false;
1001 }
1002 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
1003 this->lf(2);
1004 this->dumpComment(*def);
1005 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
1006 this->lf(2);
1007 } continue;
1008 case MarkType::kUnion:
1009 SkASSERT(0); // incomplete
1010 break;
1011 default:
1012 SkASSERT(0);
1013 }
1014 this->dumpCommonTail(*def);
1015 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001016 *globalTell = ftell(fOut);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001017 this->writeEndTag("Topic", topicName);
1018 this->lfAlways(1);
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001019// fclose(fOut); // defer closing in case class needs to be also written here
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001020 SkDebugf("wrote %s\n", fileName.c_str());
1021 return true;
1022}
1023
1024bool IncludeParser::isClone(const Definition& token) {
1025 string name = token.fName;
1026 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1027}
1028
1029bool IncludeParser::isConstructor(const Definition& token, string className) {
1030 string name = token.fName;
1031 return 0 == name.find(className) || '~' == name[0];
1032}
1033
1034bool IncludeParser::isInternalName(const Definition& token) {
1035 string name = token.fName;
1036 // exception for this SkCanvas function .. for now
1037 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1038 return false;
1039 }
1040 return name.substr(0, 7) == "android"
1041 || 0 == token.fName.find("internal_")
1042 || 0 == token.fName.find("Internal_")
1043 || 0 == token.fName.find("legacy_")
1044 || 0 == token.fName.find("temporary_")
1045 || 0 == token.fName.find("private_");
1046}
1047
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001048bool IncludeParser::isMember(const Definition& token) const {
1049 if ('f' == token.fStart[0] && isupper(token.fStart[1])) {
1050 return true;
1051 }
1052 if (!islower(token.fStart[0])) {
1053 return false;
1054 }
1055 // make an exception for SkTextBlob::RunBuffer, sole struct with members not in fXxxx format
1056 if (string::npos != token.fFileName.find("SkTextBlob.h")) {
1057 const Definition* structToken = token.fParent;
1058 if (!structToken) {
1059 return false;
1060 }
1061 if (KeyWord::kStruct != structToken->fKeyWord) {
1062 structToken = token.fParent->fParent;
1063 if (!structToken) {
1064 return false;
1065 }
1066 if (KeyWord::kStruct != structToken->fKeyWord) {
1067 return false;
1068 }
1069 }
1070 SkASSERT(structToken->fTokens.size() > 0);
1071 const Definition& child = structToken->fTokens.front();
1072 string structName(child.fContentStart, child.length());
1073 if ("RunBuffer" != structName) {
1074 return false;
1075 }
1076 string tokenName(token.fContentStart, token.length());
1077 string allowed[] = { "glyphs", "pos", "utf8text", "clusters" };
1078 for (auto allow : allowed) {
1079 if (allow == tokenName) {
1080 return true;
1081 }
1082 }
1083 }
1084 return false;
1085}
1086
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001087bool IncludeParser::isOperator(const Definition& token) {
1088 return "operator" == token.fName.substr(0, 8);
1089}
1090
1091void IncludeParser::dumpMethod(const Definition& token, string className) {
1092 this->writeTag("Method");
1093 this->writeSpace();
1094
1095 string name = string(token.fStart ? token.fStart : token.fContentStart,
1096 token.length());
1097 if (this->isOperator(token)) {
1098 string spaceConst(" const");
1099 size_t constPos = name.rfind(spaceConst);
1100 if (name.length() - spaceConst.length() == constPos) {
1101 name = name.substr(0, constPos) + "_const";
1102 }
1103 }
Cary Clark224c7002018-06-27 11:00:21 -04001104 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001105 string inType;
1106 if (this->isConstructor(token, className)) {
1107 inType = "Constructor";
1108 } else if (this->isOperator(token)) {
1109 inType = "Operator";
1110 } else {
1111 inType = "incomplete";
1112 }
1113 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001114 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001115 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001116 this->dumpComment(token);
1117}
1118
1119void IncludeParser::dumpMember(const Definition& token) {
1120 this->writeTag("Member");
1121 this->writeSpace();
1122 this->writeDefinition(token, token.fName, 2);
1123 lf(1);
1124 for (auto child : token.fChildren) {
1125 this->writeDefinition(*child);
1126 }
1127 this->writeEndTag();
1128 lf(2);
1129}
1130
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001131bool IncludeParser::dumpTokens() {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001132 string globalFileName;
1133 long int globalTell = 0;
1134 if (!this->dumpGlobals(&globalFileName, &globalTell)) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001135 return false;
1136 }
Cary Clark9174bda2017-09-19 17:39:32 -04001137 for (const auto& member : fIClassMap) {
1138 if (string::npos != member.first.find("::")) {
1139 continue;
1140 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001141 if (!this->dumpTokens(member.first, globalFileName, &globalTell)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001142 return false;
1143 }
1144 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001145 if (globalTell) {
1146 fclose(fOut);
1147 }
Cary Clark9174bda2017-09-19 17:39:32 -04001148 return true;
1149}
1150
Ben Wagner63fd7602017-10-09 15:45:33 -04001151 // dump equivalent markup
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001152bool IncludeParser::dumpTokens(string skClassName, string globalFileName, long int* globalTell) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001153 string fileName = skClassName + "_Reference.bmh";
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001154 if (globalFileName != fileName) {
1155 fOut = fopen(fileName.c_str(), "wb");
1156 if (!fOut) {
1157 SkDebugf("could not open output file %s\n", fileName.c_str());
1158 return false;
1159 }
1160 } else {
1161 fseek(fOut, *globalTell, SEEK_SET);
1162 this->lf(2);
1163 this->writeBlockSeparator();
1164 *globalTell = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001165 }
1166 string prefixName = skClassName.substr(0, 2);
1167 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1168 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001169 if (globalFileName != fileName) {
1170 this->writeTagNoLF("Topic", topicName);
1171 this->writeEndTag("Alias", topicName + "_Reference");
1172 this->lf(2);
1173 }
Cary Clark8032b982017-07-28 11:04:54 -04001174 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001175 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1176 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001177 this->writeTag(containerType, skClassName);
1178 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001179 auto& tokens = classMap.fTokens;
1180 for (auto& token : tokens) {
1181 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1182 continue;
1183 }
Cary Clark9174bda2017-09-19 17:39:32 -04001184 this->writeDefinition(token);
1185 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001186 }
1187 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001188 bool hasClass = false;
1189 bool hasConst = !fIEnumMap.empty();
1190 bool hasConstructor = false;
1191 bool hasMember = false;
1192 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001193 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001194 for (const auto& oneClass : fIClassMap) {
1195 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1196 continue;
1197 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001198 hasClass = true;
1199 break;
Cary Clark8032b982017-07-28 11:04:54 -04001200 }
Cary Clark224c7002018-06-27 11:00:21 -04001201 for (const auto& oneStruct : fIStructMap) {
1202 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1203 continue;
1204 }
1205 hasStruct = true;
1206 break;
1207 }
Cary Clark8032b982017-07-28 11:04:54 -04001208 for (const auto& token : classMap.fTokens) {
1209 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1210 continue;
1211 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001212 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001213 continue;
1214 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001215 if (this->isConstructor(token, skClassName)) {
1216 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001217 continue;
1218 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001219 if (this->isOperator(token)) {
1220 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001221 continue;
1222 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001223 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001224 continue;
1225 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001226 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001227 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001228 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001229 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001230 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001231 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001232
1233 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001234 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001235 this->writeTag("Populate");
1236 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001237 this->lf(2);
1238 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001239 if (hasConst) {
1240 this->writeTag("Subtopic", "Constant");
1241 this->writeTag("Populate");
1242 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001243 this->lf(2);
1244 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001245 if (hasConstructor) {
1246 this->writeTag("Subtopic", "Constructor");
1247 this->writeTag("Populate");
1248 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001249 this->lf(2);
1250 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001251 if (hasOperator) {
1252 this->writeTag("Subtopic", "Operator");
1253 this->writeTag("Populate");
1254 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001255 this->lf(2);
1256 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001257 if (hasMember) {
1258 this->writeTag("Subtopic", "Member_Function");
1259 this->writeTag("Populate");
1260 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001261 this->lf(2);
1262 }
Cary Clark224c7002018-06-27 11:00:21 -04001263 if (hasStruct) {
1264 this->writeTag("Subtopic", "Struct");
1265 this->writeTag("Populate");
1266 this->writeEndTag();
1267 this->lf(2);
1268 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001269 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001270 this->writeBlockSeparator();
1271 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001272 this->lf(2);
1273 this->writeTag("Example");
1274 this->lfcr();
1275 this->writeString("// incomplete");
1276 this->writeEndTag();
1277 this->lf(2);
1278 this->writeTag("SeeAlso", "incomplete");
1279 this->lf(2);
1280 this->writeEndTag("Enum", oneEnum.first);
1281 this->lf(2);
1282 }
Cary Clark8032b982017-07-28 11:04:54 -04001283 for (auto& oneClass : fIClassMap) {
1284 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1285 continue;
1286 }
1287 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001288 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001289 KeyWord keyword = oneClass.second.fKeyWord;
1290 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1291 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001292 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001293 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001294 this->lf(2);
1295 this->writeTag("Code");
1296 this->writeEndTag("ToDo", "fill this in manually");
1297 this->writeEndTag();
1298 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001299 for (auto& token : oneClass.second.fTokens) {
1300 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1301 continue;
1302 }
Cary Clark9174bda2017-09-19 17:39:32 -04001303 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001304 }
1305 this->lf(2);
1306 this->dumpClassTokens(oneClass.second);
1307 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001308 this->writeEndTag(containerType, innerName);
1309 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001310 }
1311 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001312 this->writeEndTag(containerType, skClassName);
1313 this->lf(2);
1314 this->writeEndTag("Topic", topicName);
1315 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001316 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001317 SkDebugf("wrote %s\n", fileName.c_str());
1318 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001319}
1320
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001321void IncludeParser::dumpTypedef(const Definition& token, string className) {
1322 this->writeTag("Typedef");
1323 this->writeSpace();
1324 this->writeString(token.fName);
1325 this->writeTagTable("Line", "incomplete");
1326 this->lf(2);
1327 this->dumpComment(token);
1328}
1329
Cary Clark8032b982017-07-28 11:04:54 -04001330bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1331 // add comment preceding class, if any
1332 const Definition* parent = includeDef.fParent;
1333 int index = includeDef.fParentIndex;
1334 auto wordIter = parent->fTokens.begin();
1335 std::advance(wordIter, index);
1336 SkASSERT(&*wordIter == &includeDef);
1337 while (parent->fTokens.begin() != wordIter) {
1338 auto testIter = std::prev(wordIter);
1339 if (Definition::Type::kWord != testIter->fType
1340 && Definition::Type::kKeyWord != testIter->fType
1341 && (Definition::Type::kBracket != testIter->fType
1342 || Bracket::kAngle != testIter->fBracket)
1343 && (Definition::Type::kPunctuation != testIter->fType
1344 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1345 break;
1346 }
1347 wordIter = testIter;
1348 }
1349 auto commentIter = wordIter;
1350 while (parent->fTokens.begin() != commentIter) {
1351 auto testIter = std::prev(commentIter);
1352 bool isComment = Definition::Type::kBracket == testIter->fType
1353 && (Bracket::kSlashSlash == testIter->fBracket
1354 || Bracket::kSlashStar == testIter->fBracket);
1355 if (!isComment) {
1356 break;
1357 }
1358 commentIter = testIter;
1359 }
1360 while (commentIter != wordIter) {
1361 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1362 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1363 return false;
1364 }
1365 commentIter = std::next(commentIter);
1366 }
1367 return true;
1368}
1369
Cary Clark0d225392018-06-07 09:59:07 -04001370Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1371 string typeName) {
1372 typedef Definition* DefinitionPtr;
1373 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1374 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1375 if (mapIter == fMaps.end()) {
1376 return nullptr;
1377 }
1378 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1379 return reportError<DefinitionPtr>("invalid mark type");
1380 }
1381 string name = this->uniqueName(*mapIter->fInclude, typeName);
1382 Definition& markupDef = *(*mapIter->fInclude)[name];
1383 if (markupDef.fStart) {
1384 return reportError<DefinitionPtr>("definition already defined");
1385 }
1386 markupDef.fFileName = fFileName;
1387 markupDef.fStart = includeDef.fStart;
1388 markupDef.fContentStart = includeDef.fStart;
1389 markupDef.fName = name;
1390 markupDef.fContentEnd = includeDef.fContentEnd;
1391 markupDef.fTerminator = includeDef.fTerminator;
1392 markupDef.fParent = fParent;
1393 markupDef.fLineCount = includeDef.fLineCount;
1394 markupDef.fMarkType = markType;
1395 markupDef.fKeyWord = includeDef.fKeyWord;
1396 markupDef.fType = Definition::Type::kMark;
1397 return &markupDef;
1398}
1399
Cary Clark224c7002018-06-27 11:00:21 -04001400Definition* IncludeParser::parentBracket(Definition* parent) const {
1401 while (parent && Definition::Type::kBracket != parent->fType) {
1402 parent = parent->fParent;
1403 }
1404 return parent;
1405}
1406
1407Bracket IncludeParser::grandParentBracket() const {
1408 Definition* parent = parentBracket(fParent);
1409 parent = parentBracket(parent ? parent->fParent : nullptr);
1410 return parent ? parent->fBracket : Bracket::kNone;
1411}
1412
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001413bool IncludeParser::inAlignAs() const {
1414 if (fParent->fTokens.size() < 2) {
1415 return false;
1416 }
1417 auto reverseIter = fParent->fTokens.end();
1418 bool checkForBracket = true;
1419 while (fParent->fTokens.begin() != reverseIter) {
1420 std::advance(reverseIter, -1);
1421 if (checkForBracket) {
1422 if (Definition::Type::kBracket != reverseIter->fType) {
1423 return false;
1424 }
1425 if (Bracket::kParen != reverseIter->fBracket) {
1426 return false;
1427 }
1428 checkForBracket = false;
1429 continue;
1430 }
1431 if (Definition::Type::kKeyWord != reverseIter->fType) {
1432 return false;
1433 }
1434 return KeyWord::kAlignAs == reverseIter->fKeyWord;
1435 }
1436 return false;
1437}
1438
Cary Clark137b8742018-05-30 09:21:49 -04001439// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001440bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1441 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001442 // parse class header
1443 auto iter = includeDef->fTokens.begin();
1444 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1445 // todo : documentation is ignoring this for now
1446 iter = std::next(iter);
1447 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001448 bool hasAlignAs = iter->fKeyWord == KeyWord::kAlignAs;
1449 if (hasAlignAs) {
1450 iter = std::next(iter);
1451 if (Definition::Type::kBracket != iter->fType || Bracket::kParen != iter->fBracket) {
1452 return includeDef->reportError<bool>("expected alignas argument");
1453 }
1454 iter = std::next(iter);
1455 }
Cary Clark8032b982017-07-28 11:04:54 -04001456 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1457 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001458 iter = std::next(iter);
1459 if (iter == includeDef->fTokens.end()) {
1460 return true; // forward declaration only
1461 }
Cary Clark8032b982017-07-28 11:04:54 -04001462 do {
1463 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001464 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001465 }
1466 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1467 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001468 }
Cary Clark8032b982017-07-28 11:04:54 -04001469 } while (static_cast<void>(iter = std::next(iter)), true);
1470 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001471 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001472 }
1473 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1474 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001475 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001476 }
1477 markupDef->fStart = iter->fStart;
1478 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001479 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001480 }
1481// if (1 != includeDef->fChildren.size()) {
1482// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1483// }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001484 auto includeDefIter = includeDef->fChildren.begin();
1485 if (hasAlignAs) {
1486 SkASSERT(includeDef->fChildren.end() != includeDefIter);
1487 SkASSERT(Bracket::kParen == (*includeDefIter)->fBracket);
1488 std::advance(includeDefIter, 1);
1489 }
1490 if (includeDef->fChildren.end() != includeDefIter
1491 && Bracket::kAngle == (*includeDefIter)->fBracket) {
1492 std::advance(includeDefIter, 1);
1493 }
1494 includeDef = *includeDefIter;
1495 SkASSERT(Bracket::kBrace == includeDef->fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001496 iter = includeDef->fTokens.begin();
1497 // skip until public
1498 int publicIndex = 0;
1499 if (IsStruct::kNo == isStruct) {
1500 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1501 size_t publicLen = strlen(publicName);
1502 while (iter != includeDef->fTokens.end()
1503 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1504 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001505 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001506 iter = std::next(iter);
1507 ++publicIndex;
1508 }
1509 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001510 int keyIndex = publicIndex;
1511 KeyWord currentKey = KeyWord::kPublic;
1512 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1513 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001514 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1515 size_t protectedLen = strlen(protectedName);
1516 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1517 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001518 auto childIter = includeDef->fChildren.begin();
Cary Clarkd2ca79c2018-08-10 13:09:13 -04001519 while (includeDef->fChildren.end() != childIter && (*childIter)->fPrivate) {
Cary Clarkb7a72a52018-06-15 05:11:24 -04001520 std::advance(childIter, 1);
1521 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001522 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001523 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001524 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001525 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001526 const char* testStart = iter->fStart;
1527 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1528 iter = std::next(iter);
1529 ++keyIndex;
1530 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1531 currentKey = KeyWord::kPublic;
1532 break;
1533 }
1534 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1535 currentKey = KeyWord::kProtected;
1536 break;
1537 }
1538 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1539 currentKey = KeyWord::kPrivate;
1540 break;
1541 }
1542 }
1543 fLastObject = nullptr;
1544 if (KeyWord::kPublic == currentKey) {
1545 if (!this->parseObject(child, markupDef)) {
1546 return false;
1547 }
Cary Clark8032b982017-07-28 11:04:54 -04001548 }
Cary Clark73fa9722017-08-29 17:36:51 -04001549 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001550 childIter = std::next(childIter);
1551 }
Cary Clark137b8742018-05-30 09:21:49 -04001552 while (iter != includeDef->fTokens.end()) {
1553 iter->fPrivate = KeyWord::kPublic != currentKey;
1554 iter = std::next(iter);
1555 }
Cary Clark8032b982017-07-28 11:04:54 -04001556 SkASSERT(fParent->fParent);
1557 fParent = fParent->fParent;
1558 return true;
1559}
1560
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001561bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001562 int lineCount, Definition* markupDef) {
1563 TextParser parser(filename, start, end, lineCount);
1564 // parse doxygen if present
1565 if (parser.startsWith("**")) {
1566 parser.next();
1567 parser.next();
1568 parser.skipWhiteSpace();
1569 if ('\\' == parser.peek()) {
1570 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001571 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1572 if (parser.skipExact("file")) {
1573 if (Definition::Type::kFileType != fParent->fType) {
1574 return reportError<bool>("expected parent is file");
1575 }
1576 string filename = markupDef->fileName();
1577 if (!parser.skipWord(filename.c_str())) {
1578 return reportError<bool>("missing object type");
1579 }
1580 } else if (parser.skipExact("fn")) {
1581 SkASSERT(0); // incomplete
1582 } else {
1583 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1584 return reportError<bool>("missing object type");
1585 }
1586 if (!parser.skipWord(markupDef->fName.c_str()) &&
1587 KeyWord::kEnum != markupDef->fKeyWord) {
1588 return reportError<bool>("missing object name");
1589 }
Cary Clark8032b982017-07-28 11:04:54 -04001590 }
Cary Clark8032b982017-07-28 11:04:54 -04001591 }
1592 }
1593 // remove leading '*' if present
1594 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1595 while (!parser.eof() && parser.skipWhiteSpace()) {
1596 while ('*' == parser.peek()) {
1597 parser.next();
1598 if (parser.eof()) {
1599 break;
1600 }
1601 parser.skipWhiteSpace();
1602 }
1603 if (parser.eof()) {
1604 break;
1605 }
1606 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001607 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001608 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001609 parser.skipToEndBracket('\n');
1610 }
1611 return true;
1612}
1613
Cary Clarkd98f78c2018-04-26 08:32:37 -04001614bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04001615 if (!markupDef) {
1616 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1617 child->fLineCount, fParent, '\0');
1618 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04001619 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04001620 globalMarkupChild->fName = globalUniqueName;
1621 if (!this->findComments(*child, globalMarkupChild)) {
1622 return false;
1623 }
1624 fIConstMap[globalUniqueName] = globalMarkupChild;
1625 return true;
1626 }
1627 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1628 child->fLineCount, markupDef, '\0');
1629 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04001630 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001631 markupChild->fTerminator = markupChild->fContentEnd;
1632 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04001633 classDef.fConsts[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001634 return true;
1635}
1636
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001637bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1638 TextParser parser(child);
1639 if (!parser.skipExact("#define")) {
1640 return false;
1641 }
1642 if (!parser.skipSpace()) {
1643 return false;
1644 }
1645 const char* nameStart = parser.fChar;
1646 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1647 if (parser.eof()) {
1648 return true; // do nothing if #define doesn't define anything
1649 }
1650 string nameStr(nameStart, parser.fChar - nameStart);
1651 struct Param {
1652 const char* fStart;
1653 const char* fEnd;
1654 };
1655 vector<Param> params;
1656 if ('(' == parser.peek()) {
1657 parser.next();
1658 if (!parser.skipSpace()) {
1659 return false;
1660 }
1661 do {
1662 const char* paramStart = parser.fChar;
1663 if (!parser.skipExact("...")) {
1664 parser.skipToNonAlphaNum();
1665 }
1666 if (parser.eof()) {
1667 return false;
1668 }
1669 params.push_back({paramStart, parser.fChar});
1670 if (!parser.skipSpace()) {
1671 return false;
1672 }
1673 if (')' == parser.peek()) {
1674 parser.next();
1675 break;
1676 }
1677 if (',' != parser.next()) {
1678 return false;
1679 }
1680 if (!parser.skipSpace()) {
1681 return false;
1682 }
1683 } while (true);
1684 }
1685 if (!parser.skipSpace()) {
1686 return false;
1687 }
1688 if (!markupDef) {
1689 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1690 child->fLineCount, fParent, '\0');
1691 Definition* globalMarkupChild = &fGlobals.back();
1692 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1693 globalMarkupChild->fName = globalUniqueName;
1694 globalMarkupChild->fTerminator = child->fContentEnd;
1695 if (!this->findComments(*child, globalMarkupChild)) {
1696 return false;
1697 }
1698 fIDefineMap[globalUniqueName] = globalMarkupChild;
1699 for (Param param : params) {
1700 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1701 child->fLineCount, globalMarkupChild, '\0');
1702 Definition* paramChild = &globalMarkupChild->fTokens.back();
1703 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1704 paramChild->fTerminator = param.fEnd;
1705 }
1706 return true;
1707 }
1708 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1709 child->fLineCount, markupDef, '\0');
1710 Definition* markupChild = &markupDef->fTokens.back();
1711 markupChild->fName = nameStr;
1712 markupChild->fTerminator = markupChild->fContentEnd;
1713 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1714 if (!this->findComments(*child, markupChild)) {
1715 return false;
1716 }
1717 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001718 return true;
1719}
1720
1721bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001722 TextParser parser(child);
1723 parser.skipToEndBracket('{');
1724 if (parser.eof()) {
1725 return true; // if enum is a forward declaration, do nothing
1726 }
1727 parser.next();
1728 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001729 if (child->fTokens.size() > 0) {
1730 auto token = child->fTokens.begin();
1731 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1732 token = token->fTokens.begin();
1733 }
1734 if (Definition::Type::kWord == token->fType) {
1735 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1736 }
1737 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001738 Definition* markupChild;
1739 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001740 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1741 child->fLineCount, fParent, '\0');
1742 markupChild = &fGlobals.back();
1743 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1744 markupChild->fName = globalUniqueName;
1745 markupChild->fTerminator = child->fContentEnd;
1746 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001747 } else {
1748 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001749 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001750 markupChild = &markupDef->fTokens.back();
1751 }
Cary Clark8032b982017-07-28 11:04:54 -04001752 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1753 markupChild->fKeyWord = KeyWord::kEnum;
1754 TextParser enumName(child);
1755 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001756 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001757 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001758 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001759 markupChild->fMarkType = MarkType::kEnumClass;
1760 }
Cary Clark8032b982017-07-28 11:04:54 -04001761 const char* nameStart = enumName.fChar;
1762 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001763 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001764 markupChild->fName = markupDef->fName + "::" +
1765 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001766 }
Cary Clark8032b982017-07-28 11:04:54 -04001767 if (!this->findComments(*child, markupChild)) {
1768 return false;
1769 }
Cary Clark8032b982017-07-28 11:04:54 -04001770 const char* dataEnd;
1771 do {
Cary Clark8032b982017-07-28 11:04:54 -04001772 parser.skipWhiteSpace();
1773 if ('}' == parser.peek()) {
1774 break;
1775 }
1776 Definition* comment = nullptr;
1777 // note that comment, if any, can be before or after (on the same line, though) as member
1778 if ('#' == parser.peek()) {
1779 // fixme: handle preprecessor, but just skip it for now
1780 parser.skipToLineStart();
1781 }
1782 while (parser.startsWith("/*") || parser.startsWith("//")) {
1783 parser.next();
1784 const char* start = parser.fChar;
1785 const char* end;
1786 if ('*' == parser.peek()) {
1787 end = parser.strnstr("*/", parser.fEnd);
1788 parser.fChar = end;
1789 parser.next();
1790 parser.next();
1791 } else {
1792 end = parser.trimmedLineEnd();
1793 parser.skipToLineStart();
1794 }
1795 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001796 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001797 comment = &markupChild->fTokens.back();
1798 comment->fTerminator = end;
1799 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1800 return false;
1801 }
1802 parser.skipWhiteSpace();
1803 }
1804 parser.skipWhiteSpace();
1805 const char* memberStart = parser.fChar;
1806 if ('}' == memberStart[0]) {
1807 break;
1808 }
Cary Clark9174bda2017-09-19 17:39:32 -04001809 // if there's comment on same the line as member def, output first as if it was before
1810
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001811 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001812 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001813 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04001814 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04001815 }
Cary Clark8032b982017-07-28 11:04:54 -04001816 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001817 if ('=' == parser.peek()) {
1818 parser.skipToEndBracket(',');
1819 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001820 if (!parser.eof() && '#' == parser.peek()) {
1821 // fixme: handle preprecessor, but just skip it for now
1822 continue;
1823 }
Cary Clark9174bda2017-09-19 17:39:32 -04001824 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04001825 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04001826 }
1827 dataEnd = parser.fChar;
1828 const char* start = parser.anyOf("/\n");
1829 SkASSERT(start);
1830 parser.skipTo(start);
1831 if ('/' == parser.next()) {
1832 char slashStar = parser.next();
1833 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001834 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001835 char doxCheck = parser.next();
1836 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1837 save.restore();
1838 }
1839 }
1840 parser.skipWhiteSpace();
1841 const char* commentStart = parser.fChar;
1842 if ('/' == slashStar) {
1843 parser.skipToEndBracket('\n');
1844 } else {
1845 parser.skipToEndBracket("*/");
1846 }
1847 SkASSERT(!parser.eof());
1848 const char* commentEnd = parser.fChar;
1849 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001850 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001851 comment = &markupChild->fTokens.back();
1852 comment->fTerminator = commentEnd;
1853 }
Cary Clark8032b982017-07-28 11:04:54 -04001854 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001855 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001856 Definition* member = &markupChild->fTokens.back();
1857 member->fName = memberName;
1858 if (comment) {
1859 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001860 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001861 }
1862 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001863 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001864 for (auto outsideMember : child->fChildren) {
1865 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001866 continue;
1867 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001868 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1869 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001870 continue;
1871 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001872 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1873 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001874 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001875 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001876 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001877 // FIXME: ? add comment as well ?
1878 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001879 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001880 if (markupDef) {
1881 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1882 SkASSERT(classDef.fStart);
1883 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1884 markupChild->fName = uniqueName;
1885 classDef.fEnums[uniqueName] = markupChild;
1886 }
Cary Clark8032b982017-07-28 11:04:54 -04001887 return true;
1888}
1889
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001890bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001891 fParent = &fIncludeMap[name];
1892 fParent->fName = name;
1893 fParent->fFileName = fFileName;
1894 fParent->fType = Definition::Type::kFileType;
1895 fParent->fContentStart = fChar;
1896 fParent->fContentEnd = fEnd;
1897 // parse include file into tree
1898 while (fChar < fEnd) {
1899 if (!this->parseChar()) {
1900 return false;
1901 }
1902 }
1903 // parse tree and add named objects to maps
1904 fParent = &fIncludeMap[name];
1905 if (!this->parseObjects(fParent, nullptr)) {
1906 return false;
1907 }
1908 return true;
1909}
1910
1911bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1912 const char* typeStart = child->fChildren[0]->fContentStart;
1913 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001914 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001915 Definition* markupChild = &markupDef->fTokens.back();
1916 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001917 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001918 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1919 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1920 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1921 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001922 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001923 classDef.fMembers[uniqueName] = markupChild;
1924 if (child->fParentIndex >= 2) {
1925 auto comment = child->fParent->fTokens.begin();
1926 std::advance(comment, child->fParentIndex - 2);
1927 if (Definition::Type::kBracket == comment->fType
1928 && (Bracket::kSlashStar == comment->fBracket
1929 || Bracket::kSlashSlash == comment->fBracket)) {
1930 TextParser parser(&*comment);
1931 do {
1932 parser.skipToAlpha();
1933 if (parser.eof()) {
1934 break;
1935 }
1936 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001937 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001938 if (Bracket::kSlashStar == comment->fBracket) {
1939 const char* commentEnd = parser.strnstr("*/", end);
1940 if (commentEnd) {
1941 end = commentEnd;
1942 }
1943 }
1944 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001945 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001946 Definition* commentChild = &markupDef->fTokens.back();
1947 markupChild->fChildren.emplace_back(commentChild);
1948 parser.skipTo(end);
1949 } while (!parser.eof());
1950 }
1951 }
1952 return true;
1953}
1954
1955bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1956 auto tokenIter = child->fParent->fTokens.begin();
1957 std::advance(tokenIter, child->fParentIndex);
1958 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001959 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001960 bool addConst = false;
1961 auto operatorCheck = tokenIter;
1962 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1963 operatorCheck = std::prev(tokenIter);
1964 }
1965 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001966 auto closeParen = std::next(tokenIter);
1967 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1968 '(' == closeParen->fContentStart[0]);
1969 nameEnd = closeParen->fContentEnd + 1;
1970 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001971 if (Definition::Type::kKeyWord == closeParen->fType &&
1972 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001973 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001974 }
Cary Clarka560c472017-11-27 10:44:06 -05001975 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001976 }
1977 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001978 if (addConst) {
1979 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001980 }
Cary Clark8032b982017-07-28 11:04:54 -04001981 while (tokenIter != child->fParent->fTokens.begin()) {
1982 auto testIter = std::prev(tokenIter);
1983 switch (testIter->fType) {
1984 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001985 if (testIter == child->fParent->fTokens.begin() &&
1986 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1987 KeyWord::kIfndef == child->fParent->fKeyWord ||
1988 KeyWord::kIf == child->fParent->fKeyWord)) {
1989 std::next(tokenIter);
1990 break;
1991 }
Cary Clark8032b982017-07-28 11:04:54 -04001992 goto keepGoing;
1993 case Definition::Type::kKeyWord: {
1994 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1995 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1996 goto keepGoing;
1997 }
1998 } break;
1999 case Definition::Type::kBracket:
2000 if (Bracket::kAngle == testIter->fBracket) {
2001 goto keepGoing;
2002 }
2003 break;
2004 case Definition::Type::kPunctuation:
2005 if (Punctuation::kSemicolon == testIter->fPunctuation
2006 || Punctuation::kLeftBrace == testIter->fPunctuation
2007 || Punctuation::kColon == testIter->fPunctuation) {
2008 break;
2009 }
2010 keepGoing:
2011 tokenIter = testIter;
2012 continue;
2013 default:
2014 break;
2015 }
2016 break;
2017 }
Cary Clark224c7002018-06-27 11:00:21 -04002018 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04002019 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04002020 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04002021 auto testIter = child->fParent->fTokens.begin();
2022 SkASSERT(child->fParentIndex > 0);
2023 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04002024 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
2025 0 == tokenIter->fParentIndex) {
2026 tokenIter = std::next(tokenIter);
2027 }
Cary Clark8032b982017-07-28 11:04:54 -04002028 const char* start = tokenIter->fContentStart;
2029 const char* end = tokenIter->fContentEnd;
2030 const char kDebugCodeStr[] = "SkDEBUGCODE";
2031 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
2032 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
2033 std::advance(testIter, 1);
2034 start = testIter->fContentStart + 1;
2035 end = testIter->fContentEnd - 1;
2036 } else {
2037 end = testIter->fContentEnd;
2038 while (testIter != child->fParent->fTokens.end()) {
2039 testIter = std::next(testIter);
2040 switch (testIter->fType) {
2041 case Definition::Type::kPunctuation:
2042 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
2043 || Punctuation::kLeftBrace == testIter->fPunctuation
2044 || Punctuation::kColon == testIter->fPunctuation);
2045 end = testIter->fStart;
2046 break;
2047 case Definition::Type::kKeyWord: {
2048 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
2049 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
2050 continue;
2051 }
2052 } break;
2053 default:
2054 continue;
2055 }
2056 break;
2057 }
2058 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04002059 while (end > start && ' ' >= end[-1]) {
2060 --end;
2061 }
Cary Clark9174bda2017-09-19 17:39:32 -04002062 if (!markupDef) {
2063 auto parentIter = child->fParent->fTokens.begin();
2064 SkASSERT(child->fParentIndex > 0);
2065 std::advance(parentIter, child->fParentIndex - 1);
2066 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05002067 TextParser nameParser(methodName);
2068 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04002069 return true; // expect this is inline class definition outside of class
2070 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002071 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
2072 fParent, '\0');
2073 Definition* globalMarkupChild = &fGlobals.back();
2074 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
2075 globalMarkupChild->fName = globalUniqueName;
2076 if (!this->findComments(*child, globalMarkupChild)) {
2077 return false;
Cary Clarka560c472017-11-27 10:44:06 -05002078 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002079 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05002080 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04002081 }
Cary Clark8032b982017-07-28 11:04:54 -04002082 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05002083 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04002084 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04002085 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
2086 {
2087 auto mapIter = fIClassMap.find(markupDef->fName);
2088 SkASSERT(fIClassMap.end() != mapIter);
2089 IClassDefinition& classDef = mapIter->second;
2090 SkASSERT(classDef.fStart);
2091 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
2092 markupChild->fName = uniqueName;
2093 if (!this->findComments(*child, markupChild)) {
2094 return false;
2095 }
2096 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04002097 }
Cary Clark8032b982017-07-28 11:04:54 -04002098 return true;
2099}
2100
Cary Clark8032b982017-07-28 11:04:54 -04002101bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04002102 fPriorObject = nullptr;
2103 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04002104 if (!this->parseObject(child, markupDef)) {
2105 return false;
2106 }
Cary Clark0d225392018-06-07 09:59:07 -04002107 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04002108 }
2109 return true;
2110}
2111
2112bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
2113 // set up for error reporting
2114 fLine = fChar = child->fStart;
2115 fEnd = child->fContentEnd;
2116 // todo: put original line number in child as well
2117 switch (child->fType) {
2118 case Definition::Type::kKeyWord:
2119 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002120 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002121 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002122 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002123 }
2124 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002125 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002126 case KeyWord::kConst:
2127 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002128 if (!this->parseConst(child, markupDef)) {
2129 return child->reportError<bool>("failed to parse const or constexpr");
2130 }
2131 break;
Cary Clark8032b982017-07-28 11:04:54 -04002132 case KeyWord::kEnum:
2133 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002134 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002135 }
2136 break;
2137 case KeyWord::kStruct:
2138 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002139 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002140 }
2141 break;
2142 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002143 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002144 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002145 }
2146 break;
2147 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002148 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002149 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002150 }
2151 break;
2152 case KeyWord::kUnion:
2153 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002154 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002155 }
2156 break;
2157 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002158 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002159 }
2160 break;
2161 case Definition::Type::kBracket:
2162 switch (child->fBracket) {
2163 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04002164 if (fLastObject) {
2165 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
2166 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04002167 if (!checkDeprecated.eof()) {
2168 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002169 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2170 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002171 break;
2172 }
2173 }
2174 }
2175 {
2176 auto tokenIter = child->fParent->fTokens.begin();
2177 std::advance(tokenIter, child->fParentIndex);
2178 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002179 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002180 if (previousToken.startsWith(gAttrDeprecated)) {
2181 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002182 break;
2183 }
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002184 if (this->isMember(*tokenIter)) {
Cary Clarka7401902018-06-14 15:34:14 -04002185 break;
2186 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002187 if (Bracket::kPound == child->fParent->fBracket &&
2188 KeyWord::kIf == child->fParent->fKeyWord) {
2189 // TODO: this will skip methods named defined() -- for the
2190 // moment there aren't any
2191 if (previousToken.startsWith("defined")) {
2192 break;
2193 }
2194 }
Cary Clarkab5c9af2018-07-12 16:24:53 -04002195 if (previousToken.startsWith("sizeof") && 6 == previousToken.lineLength()) {
2196 break;
2197 }
Cary Clark73fa9722017-08-29 17:36:51 -04002198 }
Cary Clark0d225392018-06-07 09:59:07 -04002199 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2200 break;
2201 }
Cary Clark8032b982017-07-28 11:04:54 -04002202 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002203 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002204 }
Cary Clark89b14562018-03-19 09:04:10 -04002205 if (fAttrDeprecated) {
2206 Definition* lastMethod = &markupDef->fTokens.back();
2207 lastMethod->fDeprecated = true;
2208 fAttrDeprecated = nullptr;
2209 }
Cary Clark73fa9722017-08-29 17:36:51 -04002210 break;
Cary Clark8032b982017-07-28 11:04:54 -04002211 case Bracket::kSlashSlash:
2212 case Bracket::kSlashStar:
2213 // comments are picked up by parsing objects first
2214 break;
2215 case Bracket::kPound:
2216 // special-case the #xxx xxx_DEFINED entries
2217 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002218 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002219 case KeyWord::kIfndef:
2220 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002221 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002222 if (!this->parseObjects(child, markupDef)) {
2223 return false;
2224 }
2225 break;
2226 }
2227 goto preproError;
2228 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002229 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002230 break;
2231 }
2232 goto preproError;
2233 case KeyWord::kEndif:
2234 if (child->boilerplateEndIf()) {
2235 break;
2236 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002237 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002238 case KeyWord::kInclude:
2239 // ignored for now
2240 break;
2241 case KeyWord::kElse:
2242 case KeyWord::kElif:
2243 // todo: handle these
2244 break;
2245 default:
2246 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002247 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002248 }
2249 break;
2250 case Bracket::kAngle:
2251 // pick up templated function pieces when method is found
2252 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002253 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002254 if (!this->parseObjects(child, markupDef)) {
2255 return false;
2256 }
Cary Clark73fa9722017-08-29 17:36:51 -04002257 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002258 case Bracket::kSquare: {
2259 // check to see if parent is operator, the only case we handle so far
2260 auto prev = child->fParent->fTokens.begin();
2261 std::advance(prev, child->fParentIndex - 1);
2262 if (KeyWord::kOperator != prev->fKeyWord) {
2263 return child->reportError<bool>("expected operator overload");
2264 }
2265 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002266 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002267 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002268 }
2269 break;
2270 case Definition::Type::kWord:
2271 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002272 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002273 }
2274 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002275 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002276 }
2277 break;
2278 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002279 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002280 break;
2281 }
2282 return true;
2283}
2284
Cary Clarkbbfda252018-03-09 15:32:01 -05002285bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2286 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002287}
2288
Cary Clark2f466242017-12-11 16:03:17 -05002289bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2290 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002291 typedefParser.skipExact("typedef");
2292 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002293 string nameStr = typedefParser.typedefName();
2294 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002295 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2296 child->fLineCount, fParent, '\0');
2297 Definition* globalMarkupChild = &fGlobals.back();
2298 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2299 globalMarkupChild->fName = globalUniqueName;
2300 if (!this->findComments(*child, globalMarkupChild)) {
2301 return false;
2302 }
2303 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002304 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002305 return true;
2306 }
2307 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002308 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002309 Definition* markupChild = &markupDef->fTokens.back();
2310 markupChild->fName = nameStr;
2311 markupChild->fTerminator = markupChild->fContentEnd;
2312 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2313 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002314 child->fName = markupDef->fName + "::" + nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002315 return true;
2316}
2317
2318bool IncludeParser::parseUnion() {
2319
2320 return true;
2321}
2322
2323bool IncludeParser::parseChar() {
2324 char test = *fChar;
2325 if ('\\' == fPrev) {
2326 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002327// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002328 fLine = fChar + 1;
2329 }
2330 goto done;
2331 }
2332 switch (test) {
2333 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002334// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002335 fLine = fChar + 1;
2336 if (fInChar) {
2337 return reportError<bool>("malformed char");
2338 }
2339 if (fInString) {
2340 return reportError<bool>("malformed string");
2341 }
2342 if (!this->checkForWord()) {
2343 return false;
2344 }
2345 if (Bracket::kPound == this->topBracket()) {
2346 KeyWord keyWord = fParent->fKeyWord;
2347 if (KeyWord::kNone == keyWord) {
2348 return this->reportError<bool>("unhandled preprocessor directive");
2349 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002350 if (fInDefine) {
2351 SkASSERT(KeyWord::kDefine == keyWord);
2352 fInDefine = false;
2353 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002354 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002355 this->popBracket();
2356 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002357 if (fInBrace) {
2358 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2359 fInBrace = nullptr;
2360 }
Cary Clark8032b982017-07-28 11:04:54 -04002361 } else if (Bracket::kSlashSlash == this->topBracket()) {
2362 this->popBracket();
2363 }
2364 break;
2365 case '*':
2366 if (!fInCharCommentString && '/' == fPrev) {
2367 this->pushBracket(Bracket::kSlashStar);
2368 }
2369 if (!this->checkForWord()) {
2370 return false;
2371 }
2372 if (!fInCharCommentString) {
2373 this->addPunctuation(Punctuation::kAsterisk);
2374 }
2375 break;
2376 case '/':
2377 if ('*' == fPrev) {
2378 if (!fInCharCommentString) {
2379 return reportError<bool>("malformed closing comment");
2380 }
2381 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002382 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002383 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002384 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002385 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002386 }
2387 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002388 }
Cary Clark8032b982017-07-28 11:04:54 -04002389 if (!fInCharCommentString && '/' == fPrev) {
2390 this->pushBracket(Bracket::kSlashSlash);
2391 break;
2392 }
2393 if (!this->checkForWord()) {
2394 return false;
2395 }
2396 break;
2397 case '\'':
2398 if (Bracket::kChar == this->topBracket()) {
2399 this->popBracket();
2400 } else if (!fInComment && !fInString) {
2401 if (fIncludeWord) {
2402 return this->reportError<bool>("word then single-quote");
2403 }
2404 this->pushBracket(Bracket::kChar);
2405 }
2406 break;
2407 case '\"':
2408 if (Bracket::kString == this->topBracket()) {
2409 this->popBracket();
2410 } else if (!fInComment && !fInChar) {
2411 if (fIncludeWord) {
2412 return this->reportError<bool>("word then double-quote");
2413 }
2414 this->pushBracket(Bracket::kString);
2415 }
2416 break;
Cary Clark8032b982017-07-28 11:04:54 -04002417 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002418 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002419 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2420 this->pushBracket(Bracket::kDebugCode);
2421 break;
2422 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002423 case ':':
2424 case '[':
2425 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002426 if (fInCharCommentString) {
2427 break;
2428 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002429 if (fInDefine && fInBrace) {
2430 break;
2431 }
Cary Clark8032b982017-07-28 11:04:54 -04002432 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2433 break;
2434 }
Cary Clark0d225392018-06-07 09:59:07 -04002435 if (fConstExpr) {
2436 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2437 fConstExpr = nullptr;
2438 }
Cary Clark8032b982017-07-28 11:04:54 -04002439 if (!fInBrace) {
2440 if (!this->checkForWord()) {
2441 return false;
2442 }
2443 if (':' == test && !fInFunction) {
2444 break;
2445 }
2446 if ('{' == test) {
2447 this->addPunctuation(Punctuation::kLeftBrace);
2448 } else if (':' == test) {
2449 this->addPunctuation(Punctuation::kColon);
2450 }
2451 }
2452 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2453 && Bracket::kColon == fInBrace->fBracket) {
2454 Definition* braceParent = fParent->fParent;
2455 braceParent->fChildren.pop_back();
2456 braceParent->fTokens.pop_back();
2457 fParent = braceParent;
2458 fInBrace = nullptr;
2459 }
2460 this->pushBracket(
2461 '(' == test ? Bracket::kParen :
2462 '[' == test ? Bracket::kSquare :
2463 '{' == test ? Bracket::kBrace :
2464 Bracket::kColon);
2465 if (!fInBrace
2466 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2467 && fInFunction) {
2468 fInBrace = fParent;
2469 }
2470 } break;
2471 case '<':
2472 if (fInCharCommentString || fInBrace) {
2473 break;
2474 }
2475 if (!this->checkForWord()) {
2476 return false;
2477 }
2478 if (fInEnum) {
2479 break;
2480 }
2481 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002482 // this angle bracket may be an operator or may be a bracket
2483 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002484 break;
2485 case ')':
2486 case ']':
2487 case '}': {
2488 if (fInCharCommentString) {
2489 break;
2490 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002491 if (fInDefine && fInBrace) {
2492 break;
2493 }
Cary Clark8032b982017-07-28 11:04:54 -04002494 if (!fInBrace) {
2495 if (!this->checkForWord()) {
2496 return false;
2497 }
2498 }
2499 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002500 Bracket match = ')' == test ? Bracket::kParen :
2501 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2502 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002503 this->popBracket();
2504 if (!fInFunction) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002505 fInFunction = ')' == test && !this->inAlignAs();
Cary Clark8032b982017-07-28 11:04:54 -04002506 } else {
2507 fInFunction = '}' != test;
2508 }
Cary Clark73fa9722017-08-29 17:36:51 -04002509 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2510 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002511 } else if (Bracket::kAngle == this->topBracket()
2512 && match == this->grandParentBracket()) {
2513 this->popBracket();
2514 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002515 } else {
2516 return reportError<bool>("malformed close bracket");
2517 }
2518 if (popBraceParent) {
2519 Definition* braceParent = fInBrace->fParent;
2520 braceParent->fChildren.pop_back();
2521 braceParent->fTokens.pop_back();
2522 fInBrace = nullptr;
2523 }
2524 } break;
2525 case '>':
2526 if (fInCharCommentString || fInBrace) {
2527 break;
2528 }
2529 if (!this->checkForWord()) {
2530 return false;
2531 }
2532 if (fInEnum) {
2533 break;
2534 }
Cary Clarka560c472017-11-27 10:44:06 -05002535 if (Bracket::kPound == this->topBracket()) {
2536 break;
2537 }
Cary Clark8032b982017-07-28 11:04:54 -04002538 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04002539 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04002540 this->popBracket();
2541 } else {
2542 return reportError<bool>("malformed close angle bracket");
2543 }
2544 break;
2545 case '#': {
2546 if (fInCharCommentString || fInBrace) {
2547 break;
2548 }
2549 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2550 this->pushBracket(Bracket::kPound);
2551 break;
2552 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002553 case ' ':
2554 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2555 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2556 fInBrace = fParent;
2557 // delimiting brackets are space ... unescaped-linefeed
2558 }
Cary Clark8032b982017-07-28 11:04:54 -04002559 case '&':
2560 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002561 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05002562 case '-':
2563 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002564 if (fInCharCommentString || fInBrace) {
2565 break;
2566 }
2567 if (!this->checkForWord()) {
2568 return false;
2569 }
2570 break;
Cary Clark0d225392018-06-07 09:59:07 -04002571 case '=':
2572 if (fInCharCommentString || fInBrace) {
2573 break;
2574 }
2575 if (!this->checkForWord()) {
2576 return false;
2577 }
Cary Clarkd7895502018-07-18 15:10:08 -04002578 if (!fParent->fTokens.size()) {
2579 break;
2580 }
Cary Clark0d225392018-06-07 09:59:07 -04002581 {
2582 const Definition& lastToken = fParent->fTokens.back();
2583 if (lastToken.fType != Definition::Type::kWord) {
2584 break;
2585 }
2586 string name(lastToken.fContentStart, lastToken.length());
2587 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
2588 break;
2589 }
2590 // find token on start of line
2591 auto lineIter = fParent->fTokens.end();
2592 do {
Cary Clark80247e52018-07-11 16:18:41 -04002593 if (fParent->fTokens.begin() == lineIter) {
2594 break;
2595 }
Cary Clark0d225392018-06-07 09:59:07 -04002596 --lineIter;
2597 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04002598 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04002599 ++lineIter;
2600 }
2601 Definition* lineStart = &*lineIter;
2602 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
2603 bool sawConst = false;
2604 bool sawStatic = false;
2605 bool sawTemplate = false;
2606 bool sawType = false;
2607 while (&lastToken != &*lineIter) {
2608 if (KeyWord::kTemplate == lineIter->fKeyWord) {
2609 if (sawConst || sawStatic || sawTemplate) {
2610 sawConst = false;
2611 break;
2612 }
2613 if (&lastToken == &*++lineIter) {
2614 break;
2615 }
2616 if (KeyWord::kTypename != lineIter->fKeyWord) {
2617 break;
2618 }
2619 if (&lastToken == &*++lineIter) {
2620 break;
2621 }
2622 if (Definition::Type::kWord != lineIter->fType) {
2623 break;
2624 }
2625 sawTemplate = true;
2626 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
2627 if (sawConst || sawStatic) {
2628 sawConst = false;
2629 break;
2630 }
2631 sawStatic = true;
2632 } else if (KeyWord::kConst == lineIter->fKeyWord
2633 || KeyWord::kConstExpr == lineIter->fKeyWord) {
2634 if (sawConst) {
2635 sawConst = false;
2636 break;
2637 }
2638 sawConst = true;
2639 } else {
2640 if (sawType) {
2641 sawType = false;
2642 break;
2643 }
2644 if (Definition::Type::kKeyWord == lineIter->fType
2645 && KeyProperty::kNumber
2646 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
2647 sawType = true;
2648 } else if (Definition::Type::kWord == lineIter->fType) {
2649 string typeName(lineIter->fContentStart, lineIter->length());
2650 if ("Sk" != name.substr(0, 2)) {
2651 sawType = true;
2652 }
2653 }
2654 }
2655 ++lineIter;
2656 }
2657 if (sawType && sawConst) {
2658 // if found, name first
2659 lineStart->fName = name;
2660 lineStart->fMarkType = MarkType::kConst;
2661 fParent->fChildren.emplace_back(lineStart);
2662 fConstExpr = lineStart;
2663 }
2664 }
2665 break;
Cary Clark8032b982017-07-28 11:04:54 -04002666 case ';':
2667 if (fInCharCommentString || fInBrace) {
2668 break;
2669 }
2670 if (!this->checkForWord()) {
2671 return false;
2672 }
Cary Clark0d225392018-06-07 09:59:07 -04002673 if (fConstExpr) {
2674 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2675 fConstExpr = nullptr;
2676 }
Cary Clark8032b982017-07-28 11:04:54 -04002677 if (Definition::Type::kKeyWord == fParent->fType
2678 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002679 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2680 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002681 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2682 this->popObject();
2683 }
Cary Clark8032b982017-07-28 11:04:54 -04002684 if (KeyWord::kEnum == fParent->fKeyWord) {
2685 fInEnum = false;
2686 }
2687 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002688 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2689 this->popObject();
2690 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002691 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002692 } else if (Definition::Type::kBracket == fParent->fType
2693 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2694 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2695 list<Definition>::iterator baseIter = fParent->fTokens.end();
2696 list<Definition>::iterator namedIter = fParent->fTokens.end();
2697 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04002698 fParent->fTokens.begin() != tokenIter; ) {
2699 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04002700 if (tokenIter->fLineCount == fLineCount) {
Cary Clarkd2ca79c2018-08-10 13:09:13 -04002701 if (this->isMember(*tokenIter)) {
Cary Clark8032b982017-07-28 11:04:54 -04002702 if (namedIter != fParent->fTokens.end()) {
2703 return reportError<bool>("found two named member tokens");
2704 }
2705 namedIter = tokenIter;
2706 }
2707 baseIter = tokenIter;
2708 } else {
2709 break;
2710 }
2711 }
2712 // FIXME: if a member definition spans multiple lines, this won't work
2713 if (namedIter != fParent->fTokens.end()) {
2714 if (baseIter == namedIter) {
2715 return this->reportError<bool>("expected type before named token");
2716 }
2717 Definition* member = &*namedIter;
2718 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002719 if (!member->fTerminator) {
2720 member->fTerminator = member->fContentEnd;
2721 }
Cary Clark8032b982017-07-28 11:04:54 -04002722 fParent->fChildren.push_back(member);
2723 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2724 member->fChildren.push_back(&*nameType);
2725 }
Cary Clark8032b982017-07-28 11:04:54 -04002726 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002727 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002728 } else if (fParent->fChildren.size() > 0) {
2729 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002730 Definition* priorEnum = fPriorEnum;
2731 fPriorEnum = nullptr;
2732 if (!priorEnum) {
2733 while (fParent->fChildren.begin() != lastIter) {
2734 std::advance(lastIter, -1);
2735 priorEnum = *lastIter;
2736 if (Definition::Type::kBracket != priorEnum->fType ||
2737 (Bracket::kSlashSlash != priorEnum->fBracket
2738 && Bracket::kSlashStar != priorEnum->fBracket)) {
2739 break;
2740 }
Cary Clark8032b982017-07-28 11:04:54 -04002741 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002742 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002743 }
2744 if (Definition::Type::kKeyWord == priorEnum->fType
2745 && KeyWord::kEnum == priorEnum->fKeyWord) {
2746 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002747 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002748 while (tokenWalker != fParent->fTokens.end()) {
2749 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002750 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002751 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2752 break;
2753 }
2754 }
2755 while (tokenWalker != fParent->fTokens.end()) {
2756 std::advance(tokenWalker, 1);
2757 const Definition* test = &*tokenWalker;
2758 if (Definition::Type::kBracket != test->fType ||
2759 (Bracket::kSlashSlash != test->fBracket
2760 && Bracket::kSlashStar != test->fBracket)) {
2761 break;
2762 }
2763 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002764 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002765 Definition* start = &*tokenWalker;
2766 bool foundExpected = true;
2767 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2768 const Definition* test = &*tokenWalker;
2769 if (expected != test->fKeyWord) {
2770 foundExpected = false;
2771 break;
2772 }
2773 if (tokenWalker == fParent->fTokens.end()) {
2774 break;
2775 }
2776 std::advance(tokenWalker, 1);
2777 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002778 if (!foundExpected) {
2779 foundExpected = true;
2780 tokenWalker = saveTokenWalker;
2781 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2782 const Definition* test = &*tokenWalker;
2783 if (expected != test->fKeyWord) {
2784 foundExpected = false;
2785 break;
2786 }
2787 if (tokenWalker == fParent->fTokens.end()) {
2788 break;
2789 }
2790 if (KeyWord::kNone != expected) {
2791 std::advance(tokenWalker, 1);
2792 }
2793 }
2794 if (foundExpected) {
2795 auto nameToken = priorEnum->fTokens.begin();
2796 string enumName = string(nameToken->fContentStart,
2797 nameToken->fContentEnd - nameToken->fContentStart);
2798 const Definition* test = &*tokenWalker;
2799 string constType = string(test->fContentStart,
2800 test->fContentEnd - test->fContentStart);
2801 if (enumName != constType) {
2802 foundExpected = false;
2803 } else {
2804 std::advance(tokenWalker, 1);
2805 }
2806 }
2807 }
Cary Clark8032b982017-07-28 11:04:54 -04002808 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2809 const char* nameStart = tokenWalker->fStart;
2810 std::advance(tokenWalker, 1);
2811 if (tokenWalker != fParent->fTokens.end()) {
2812 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002813 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002814 start->fName = string(nameStart, tp.fChar - nameStart);
2815 start->fContentEnd = fChar;
2816 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002817 fPriorEnum = priorEnum;
2818 }
Cary Clark8032b982017-07-28 11:04:54 -04002819 }
2820 }
2821 }
2822 this->addPunctuation(Punctuation::kSemicolon);
2823 fInFunction = false;
2824 break;
2825 case '~':
2826 if (fInEnum) {
2827 break;
2828 }
2829 case '0': case '1': case '2': case '3': case '4':
2830 case '5': case '6': case '7': case '8': case '9':
2831 // TODO: don't want to parse numbers, but do need to track for enum defs
2832 // break;
2833 case 'A': case 'B': case 'C': case 'D': case 'E':
2834 case 'F': case 'G': case 'H': case 'I': case 'J':
2835 case 'K': case 'L': case 'M': case 'N': case 'O':
2836 case 'P': case 'Q': case 'R': case 'S': case 'T':
2837 case 'U': case 'V': case 'W': case 'X': case 'Y':
2838 case 'Z': case '_':
2839 case 'a': case 'b': case 'c': case 'd': case 'e':
2840 case 'f': case 'g': case 'h': case 'i': case 'j':
2841 case 'k': case 'l': case 'm': case 'n': case 'o':
2842 case 'p': case 'q': case 'r': case 's': case 't':
2843 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002844 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002845 if (fInCharCommentString || fInBrace) {
2846 break;
2847 }
2848 if (!fIncludeWord) {
2849 fIncludeWord = fChar;
2850 }
2851 break;
2852 }
2853done:
2854 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002855 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002856 return true;
2857}
2858
2859void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04002860 IncludeParser::ValidateKeyWords();
2861}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002862
Cary Clark186d08f2018-04-03 08:43:27 -04002863bool IncludeParser::references(const SkString& file) const {
2864 // if includes weren't passed one at a time, assume all references are valid
2865 if (fIncludeMap.empty()) {
2866 return true;
2867 }
2868 SkASSERT(file.endsWith(".bmh") );
2869 string root(file.c_str(), file.size() - 4);
2870 string kReference("_Reference");
2871 if (string::npos != root.find(kReference)) {
2872 root = root.substr(0, root.length() - kReference.length());
2873 }
2874 if (fIClassMap.end() != fIClassMap.find(root)) {
2875 return true;
2876 }
2877 if (fIStructMap.end() != fIStructMap.find(root)) {
2878 return true;
2879 }
Cary Clark224c7002018-06-27 11:00:21 -04002880 if (fIEnumMap.end() != fIEnumMap.find(root)) {
2881 return true;
2882 }
2883 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
2884 return true;
2885 }
Cary Clark186d08f2018-04-03 08:43:27 -04002886 return false;
2887}
2888
Cary Clark2dc84ad2018-01-26 12:56:22 -05002889void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2890 if (!sk_isdir(includes)) {
2891 IncludeParser::RemoveOneFile(docs, includes);
2892 } else {
2893 SkOSFile::Iter it(includes, ".h");
2894 for (SkString file; it.next(&file); ) {
2895 SkString p = SkOSPath::Join(includes, file.c_str());
2896 const char* hunk = p.c_str();
2897 if (!SkStrEndsWith(hunk, ".h")) {
2898 continue;
2899 }
2900 IncludeParser::RemoveOneFile(docs, hunk);
2901 }
2902 }
2903}
2904
2905void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2906 const char* lastForward = strrchr(includesFile, '/');
2907 const char* lastBackward = strrchr(includesFile, '\\');
2908 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2909 if (!last) {
2910 last = includesFile;
2911 } else {
2912 last += 1;
2913 }
2914 SkString baseName(last);
2915 SkASSERT(baseName.endsWith(".h"));
2916 baseName.remove(baseName.size() - 2, 2);
2917 baseName.append("_Reference.bmh");
2918 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2919 remove(fullName.c_str());
2920}
Cary Clark224c7002018-06-27 11:00:21 -04002921
2922Bracket IncludeParser::topBracket() const {
2923 Definition* parent = this->parentBracket(fParent);
2924 return parent ? parent->fBracket : Bracket::kNone;
2925}