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