blob: 238fcf5109d3758549235df2833cbdc9eb7aaab2 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
Cary Clark2dc84ad2018-01-26 12:56:22 -05009#include "SkOSFile.h"
10#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040011
Cary Clark89b14562018-03-19 09:04:10 -040012const char IncludeParser::gAttrDeprecated[] = "SK_ATTR_DEPRECATED";
13const size_t IncludeParser::kAttrDeprecatedLen = sizeof(gAttrDeprecated) - 1;
14
Cary Clark8032b982017-07-28 11:04:54 -040015const IncludeKey kKeyWords[] = {
16 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040017 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040018 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040019 { "bool", KeyWord::kBool, KeyProperty::kNumber },
20 { "char", KeyWord::kChar, KeyProperty::kNumber },
21 { "class", KeyWord::kClass, KeyProperty::kObject },
22 { "const", KeyWord::kConst, KeyProperty::kModifier },
23 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
24 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
25 { "double", KeyWord::kDouble, KeyProperty::kNumber },
26 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
27 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
28 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
29 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050030 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040031 { "float", KeyWord::kFloat, KeyProperty::kNumber },
32 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
33 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
34 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
35 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
36 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
37 { "inline", KeyWord::kInline, KeyProperty::kModifier },
38 { "int", KeyWord::kInt, KeyProperty::kNumber },
39 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
40 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
41 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
42 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
43 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
44 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
45 { "static", KeyWord::kStatic, KeyProperty::kModifier },
46 { "struct", KeyWord::kStruct, KeyProperty::kObject },
47 { "template", KeyWord::kTemplate, KeyProperty::kObject },
48 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clark0d225392018-06-07 09:59:07 -040049 { "typename", KeyWord::kTypename, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040050 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040051 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040052 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
53 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark4dc5a452018-05-21 11:56:57 -040054 { "uintptr_t", KeyWord::kUintPtr_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040055 { "union", KeyWord::kUnion, KeyProperty::kObject },
56 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
57 { "void", KeyWord::kVoid, KeyProperty::kNumber },
58};
59
60const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
61
62KeyWord IncludeParser::FindKey(const char* start, const char* end) {
63 int ch = 0;
64 for (size_t index = 0; index < kKeyWordCount; ) {
65 if (start[ch] > kKeyWords[index].fName[ch]) {
66 ++index;
67 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
68 return KeyWord::kNone;
69 }
70 continue;
71 }
72 if (start[ch] < kKeyWords[index].fName[ch]) {
73 return KeyWord::kNone;
74 }
75 ++ch;
76 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040077 if (end - start < (int) strlen(kKeyWords[index].fName)) {
78 return KeyWord::kNone;
79 }
Cary Clark8032b982017-07-28 11:04:54 -040080 return kKeyWords[index].fKeyWord;
81 }
82 }
83 return KeyWord::kNone;
84}
85
86void IncludeParser::ValidateKeyWords() {
87 for (size_t index = 1; index < kKeyWordCount; ++index) {
88 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
89 == (int) kKeyWords[index].fKeyWord);
90 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
91 }
92}
93
94void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050095 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040096 fIncludeWord = nullptr;
97 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
98 Definition* def = &fParent->fTokens.back();
99 this->addDefinition(def);
100 if (KeyWord::kEnum == fParent->fKeyWord) {
101 fInEnum = true;
102 }
103 }
104}
105
Ben Wagner63fd7602017-10-09 15:45:33 -0400106void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400107 const vector<string>& foundParams) {
108 for (auto& methodParam : methodParams) {
109 bool found = false;
110 for (auto& foundParam : foundParams) {
111 if (methodParam == foundParam) {
112 found = true;
113 break;
114 }
115 }
116 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400117 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400118 }
119 }
120 for (auto& foundParam : foundParams) {
121 bool found = false;
122 for (auto& methodParam : methodParams) {
123 if (methodParam == foundParam) {
124 found = true;
125 break;
126 }
127 }
128 if (!found) {
129 this->reportError("doxygen param does not match method declaration");
130 }
131 }
132}
133
134bool IncludeParser::checkForWord() {
135 if (!fIncludeWord) {
136 return true;
137 }
138 KeyWord keyWord = FindKey(fIncludeWord, fChar);
Cary Clark224c7002018-06-27 11:00:21 -0400139 if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
140 Bracket bracket = this->topBracket();
141 if (Bracket::kParen == bracket) {
142 return true;
143 }
144 }
Cary Clark8032b982017-07-28 11:04:54 -0400145 if (KeyWord::kNone != keyWord) {
146 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
147 this->addKeyword(keyWord);
148 return true;
149 }
150 } else {
151 this->addWord();
152 return true;
153 }
154 Definition* poundDef = fParent;
155 if (!fParent) {
156 return reportError<bool>("expected parent");
157 }
158 if (Definition::Type::kBracket != poundDef->fType) {
159 return reportError<bool>("expected bracket");
160 }
161 if (Bracket::kPound != poundDef->fBracket) {
162 return reportError<bool>("expected preprocessor");
163 }
164 if (KeyWord::kNone != poundDef->fKeyWord) {
165 return reportError<bool>("already found keyword");
166 }
167 poundDef->fKeyWord = keyWord;
168 fIncludeWord = nullptr;
169 switch (keyWord) {
170 // these do not link to other # directives
171 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400172 if (!fInBrace) {
173 SkASSERT(!fInDefine);
174 fInDefine = true;
175 }
Cary Clark8032b982017-07-28 11:04:54 -0400176 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500177 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400178 break;
179 // these start a # directive link
180 case KeyWord::kIf:
181 case KeyWord::kIfdef:
182 case KeyWord::kIfndef:
183 break;
184 // these continue a # directive link
185 case KeyWord::kElif:
186 case KeyWord::kElse: {
187 this->popObject(); // pop elif
188 if (Bracket::kPound != fParent->fBracket) {
189 return this->reportError<bool>("expected preprocessor directive");
190 }
191 this->popBracket(); // pop if
192 poundDef->fParent = fParent;
193 this->addDefinition(poundDef); // push elif back
194 } break;
195 // this ends a # directive link
196 case KeyWord::kEndif:
197 // FIXME : should this be calling popBracket() instead?
198 this->popObject(); // pop endif
199 if (Bracket::kPound != fParent->fBracket) {
200 return this->reportError<bool>("expected preprocessor directive");
201 }
202 this->popBracket(); // pop if/else
203 break;
204 default:
205 SkASSERT(0);
206 }
207 return true;
208}
209
210string IncludeParser::className() const {
211 string name(fParent->fName);
212 size_t slash = name.find_last_of("/");
213 if (string::npos == slash) {
214 slash = name.find_last_of("\\");
215 }
216 SkASSERT(string::npos != slash);
217 string result = name.substr(slash);
218 result = result.substr(1, result.size() - 3);
219 return result;
220}
221
Cary Clark884dd7d2017-10-11 10:37:52 -0400222#include <sstream>
223#include <iostream>
224
Cary Clark8032b982017-07-28 11:04:54 -0400225bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400226 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400227 string className = classMapper.first;
228 auto finder = bmhParser.fClassMap.find(className);
229 if (bmhParser.fClassMap.end() == finder) {
230 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400231 continue;
232 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400233 RootDefinition* root = &finder->second;
234 root->clearVisited();
235 }
236 for (auto& classMapper : fIClassMap) {
237 string className = classMapper.first;
238 std::istringstream iss(className);
239 string classStr;
240 string classBase;
241 RootDefinition* root = nullptr;
242 while (std::getline(iss, classStr, ':')) {
243 if (root) {
244 if (!classStr.length()) {
245 continue;
246 }
247 classBase += "::" + classStr;
248 auto finder = root->fBranches.find(classBase);
249 if (root->fBranches.end() != finder) {
250 root = finder->second;
251 } else {
252 SkASSERT(0);
253 }
254 } else {
255 classBase = classStr;
256 auto finder = bmhParser.fClassMap.find(classBase);
257 if (bmhParser.fClassMap.end() != finder) {
258 root = &finder->second;
259 } else {
260 SkASSERT(0);
261 }
262 }
263 }
Cary Clark8032b982017-07-28 11:04:54 -0400264 auto& classMap = classMapper.second;
265 auto& tokens = classMap.fTokens;
266 for (const auto& token : tokens) {
267 if (token.fPrivate) {
268 continue;
269 }
270 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400271 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400272 switch (token.fMarkType) {
273 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400274 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400275 continue;
276 }
Cary Clark8032b982017-07-28 11:04:54 -0400277 if (!def) {
278 string paramName = className + "::";
279 paramName += string(token.fContentStart,
280 token.fContentEnd - token.fContentStart);
Cary Clark82f1f742018-06-28 08:50:35 -0400281 if (string::npos != paramName.find('\n')) {
282 paramName.erase(std::remove(paramName.begin(), paramName.end(), '\n'),
283 paramName.end());
284 }
Cary Clarkce101242017-09-01 15:51:02 -0400285 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400286 if (!def && 0 == token.fName.find("operator")) {
287 string operatorName = className + "::";
288 TextParser oper("", token.fStart, token.fContentEnd, 0);
289 const char* start = oper.strnstr("operator", token.fContentEnd);
290 SkASSERT(start);
291 oper.skipTo(start);
292 oper.skipToEndBracket('(');
293 int parens = 0;
294 do {
295 if ('(' == oper.peek()) {
296 ++parens;
297 } else if (')' == oper.peek()) {
298 --parens;
299 }
300 } while (!oper.eof() && oper.next() && parens > 0);
301 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400302 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400303 }
304 }
305 if (!def) {
306 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
307 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
308 string constructorName = className + "::";
309 constructorName += string(token.fContentStart + skip,
310 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400311 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400312 }
313 if (!def && 0 == token.fName.find("SK_")) {
314 string incName = token.fName + "()";
315 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400316 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400317 if (def) {
318 if (def->fName == incName) {
319 def->fVisited = true;
320 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400321 def = root->find(className + "::toString",
322 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400323 if (def) {
324 def->fVisited = true;
325 } else {
326 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500327 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400328 }
329 }
330 break;
331 } else {
332 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500333 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400334 }
335 }
336 }
337 if (!def) {
338 bool allLower = true;
339 for (size_t index = 0; index < token.fName.length(); ++index) {
340 if (!islower(token.fName[index])) {
341 allLower = false;
342 break;
343 }
344 }
345 if (allLower) {
346 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400347 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400348 }
349 }
350 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400351 if (gAttrDeprecated == token.fName) {
352 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400353 break;
354 }
355 if (0 == token.fName.find("SkDEBUGCODE")) {
356 break;
357 }
358 }
359 if (!def) {
360 // simple method names inside nested classes have a bug and are missing trailing parens
361 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400362 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400363 }
364 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500365 if (!root->fDeprecated) {
366 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
367 fFailed = true;
368 }
Cary Clark8032b982017-07-28 11:04:54 -0400369 break;
370 }
Cary Clark73fa9722017-08-29 17:36:51 -0400371 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400372 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400373 if (token.fDeprecated && !def->fDeprecated) {
374 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
375 }
Cary Clark8032b982017-07-28 11:04:54 -0400376 } else {
377 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500378 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400379 }
380 } break;
381 case MarkType::kComment:
382 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400383 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400384 case MarkType::kEnum: {
385 if (!def) {
386 // work backwards from first word to deduce #Enum name
387 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
388 SkAssertResult(firstMember.skipName("enum"));
389 SkAssertResult(firstMember.skipToEndBracket('{'));
390 firstMember.next();
391 firstMember.skipWhiteSpace();
392 SkASSERT('k' == firstMember.peek());
393 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400394 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400395 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400396 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400397 const char* lastUnderscore = nullptr;
398 do {
399 if (!firstMember.skipToEndBracket('_')) {
400 break;
401 }
402 if (firstMember.fChar > wordEnd) {
403 break;
404 }
405 lastUnderscore = firstMember.fChar;
406 } while (firstMember.next());
407 if (lastUnderscore) {
408 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400409 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400410 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400411 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400412 }
413 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500414 if (!root->fDeprecated) {
415 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
416 fFailed = true;
417 }
Cary Clark8032b982017-07-28 11:04:54 -0400418 break;
419 }
420 }
421 def->fVisited = true;
422 for (auto& child : def->fChildren) {
423 if (MarkType::kCode == child->fMarkType) {
424 def = child;
425 break;
426 }
427 }
428 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500429 if (!root->fDeprecated) {
430 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
431 fFailed = true;
432 }
Cary Clark8032b982017-07-28 11:04:54 -0400433 break;
434 }
435 if (def->crossCheck(token)) {
436 def->fVisited = true;
437 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500438 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
439 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400440 }
441 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400442 string constName = MarkType::kEnumClass == token.fMarkType ?
443 fullName : className;
444 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400445 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400446 if (!def) {
447 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400448 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400449 }
450 if (!def) {
451 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500452 if (!root->fDeprecated) {
453 SkDebugf("const missing from bmh: %s\n", constName.c_str());
454 fFailed = true;
455 }
Cary Clark8032b982017-07-28 11:04:54 -0400456 }
457 } else {
458 def->fVisited = true;
459 }
460 }
461 } break;
462 case MarkType::kMember:
463 if (def) {
464 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500465 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400466 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500467 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400468 }
469 break;
Cary Clark2f466242017-12-11 16:03:17 -0500470 case MarkType::kTypedef:
471 if (def) {
472 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500473 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500474 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500475 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500476 }
477 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400478 case MarkType::kConst:
479 if (def) {
480 def->fVisited = true;
481 } else if (!root->fDeprecated) {
482 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
483 fFailed = true;
484 }
485 break;
Cary Clark8032b982017-07-28 11:04:54 -0400486 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400487 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400488 break;
489 }
490 }
491 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500492 int crossChecks = 0;
493 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400494 for (auto& classMapper : fIClassMap) {
495 string className = classMapper.first;
496 auto finder = bmhParser.fClassMap.find(className);
497 if (bmhParser.fClassMap.end() == finder) {
498 continue;
499 }
500 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500501 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500502 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400503 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500504 if (crossChecks) {
505 SkDebugf(".");
506 } else {
507 SkDebugf("cross-check");
508 firstCheck = className;
509 }
510 ++crossChecks;
511 }
512 if (crossChecks) {
513 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500514 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500515 }
516 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400517 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400518 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500519 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400520}
521
522IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400523 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400524 string className;
525 const Definition* test = fParent;
526 while (Definition::Type::kFileType != test->fType) {
527 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
528 className = test->fName + "::";
529 break;
530 }
531 test = test->fParent;
532 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400533 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400534 unordered_map<string, IClassDefinition>& map = fIClassMap;
535 IClassDefinition& markupDef = map[className];
536 if (markupDef.fStart) {
537 typedef IClassDefinition* IClassDefPtr;
538 return INHERITED::reportError<IClassDefPtr>("class already defined");
539 }
540 markupDef.fFileName = fFileName;
541 markupDef.fStart = includeDef.fStart;
542 markupDef.fContentStart = includeDef.fStart;
543 markupDef.fName = className;
544 markupDef.fContentEnd = includeDef.fContentEnd;
545 markupDef.fTerminator = includeDef.fTerminator;
546 markupDef.fParent = fParent;
547 markupDef.fLineCount = fLineCount;
548 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
549 MarkType::kStruct : MarkType::kClass;
550 markupDef.fKeyWord = includeDef.fKeyWord;
551 markupDef.fType = Definition::Type::kMark;
552 fParent = &markupDef;
553 return &markupDef;
554}
555
556void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
557 auto& tokens = classDef.fTokens;
558 for (auto& token : tokens) {
559 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
560 continue;
561 }
562 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400563 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400564 }
565 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400566 case MarkType::kConst:
567 this->dumpConst(token, classDef.fName);
568 break;
Cary Clark8032b982017-07-28 11:04:54 -0400569 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500570 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500571 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400572 break;
573 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400574 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400575 break;
576 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400577 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400578 continue;
579 break;
580 default:
581 SkASSERT(0);
582 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400583 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400584 }
585}
Cary Clark9174bda2017-09-19 17:39:32 -0400586void IncludeParser::dumpComment(const Definition& token) {
587 fLineCount = token.fLineCount;
588 fChar = fLine = token.fContentStart;
589 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400590 bool sawParam = false;
591 bool multiline = false;
592 bool sawReturn = false;
593 bool sawComment = false;
594 bool methodHasReturn = false;
595 vector<string> methodParams;
596 vector<string> foundParams;
597 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400598 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
599 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500600 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400601 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500602 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400603 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400604 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500605 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400606 && !methodParser.strnchr('~', methodParser.fEnd);
607 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
608 const char* nextEnd = paren;
609 do {
610 string paramName;
611 methodParser.fChar = nextEnd + 1;
612 methodParser.skipSpace();
613 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
614 continue;
615 }
616 methodParams.push_back(paramName);
617 } while (')' != nextEnd[0]);
618 }
Cary Clark9174bda2017-09-19 17:39:32 -0400619 for (const auto& child : token.fTokens) {
620 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
621 break;
622 }
Cary Clark8032b982017-07-28 11:04:54 -0400623 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400624 if (child.fPrivate) {
625 break;
626 }
Cary Clark8032b982017-07-28 11:04:54 -0400627 if ('@' == child.fContentStart[0]) {
628 TextParser parser(&child);
629 do {
630 parser.next();
631 if (parser.startsWith("param ")) {
632 parser.skipWord("param");
633 const char* parmStart = parser.fChar;
634 parser.skipToSpace();
635 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
636 parser.skipWhiteSpace();
637 do {
638 size_t nextComma = parmName.find(',');
639 string piece;
640 if (string::npos == nextComma) {
641 piece = parmName;
642 parmName = "";
643 } else {
644 piece = parmName.substr(0, nextComma);
645 parmName = parmName.substr(nextComma + 1);
646 }
647 if (sawParam) {
648 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400649 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400650 }
Cary Clark9174bda2017-09-19 17:39:32 -0400651 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400652 } else {
653 if (sawComment) {
654 this->nl();
655 }
656 this->lf(2);
657 }
658 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400659 this->writeTag("Param", piece);
660 this->writeSpace(2);
661 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
662 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400663 sawParam = true;
664 sawComment = false;
665 } while (parmName.length());
666 parser.skipTo(parser.fEnd);
667 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
668 parser.skipWord("return");
669 if ('s' == parser.peek()) {
670 parser.next();
671 }
672 if (sawParam) {
673 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400674 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400675 }
Cary Clark9174bda2017-09-19 17:39:32 -0400676 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400677 }
678 this->checkForMissingParams(methodParams, foundParams);
679 sawParam = false;
680 sawComment = false;
681 multiline = false;
682 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400683 this->writeTag("Return");
684 this->writeSpace(2);
685 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
686 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400687 sawReturn = true;
688 parser.skipTo(parser.fEnd);
689 } else {
690 this->reportError("unexpected doxygen directive");
691 }
692 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400693 } else if (child.length() > 1) {
694 const char* start = child.fContentStart;
695 ptrdiff_t length = child.fContentEnd - start;
696 SkASSERT(length >= 0);
697 while (length && '/' == start[0]) {
698 start += 1;
699 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400700 }
Cary Clark9174bda2017-09-19 17:39:32 -0400701 while (length && '/' == start[length - 1]) {
702 length -= 1;
703 if (length && '*' == start[length - 1]) {
704 length -= 1;
705 }
706 }
707 if (length) {
708 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
709 if (sawParam || sawReturn) {
710 this->indentToColumn(8);
711 }
712 this->writeBlock(length, start);
713 this->writeSpace();
714 sawComment = true;
715 if (sawParam || sawReturn) {
716 multiline = true;
717 }
Cary Clark8032b982017-07-28 11:04:54 -0400718 }
719 }
720 }
721 }
722 if (sawParam || sawReturn) {
723 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400724 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400725 }
Cary Clark9174bda2017-09-19 17:39:32 -0400726 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400727 }
728 if (!sawReturn) {
729 if (!sawParam) {
730 if (sawComment) {
731 this->nl();
732 }
733 this->lf(2);
734 }
735 this->checkForMissingParams(methodParams, foundParams);
736 }
737 if (methodHasReturn != sawReturn) {
738 if (!methodHasReturn) {
739 this->reportError("unexpected doxygen return");
740 } else {
741 if (sawComment) {
742 this->nl();
743 }
744 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400745 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400746 }
747 }
748}
749
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400750void IncludeParser::dumpCommonTail(const Definition& token) {
751 this->lf(2);
752 this->writeTag("Example");
753 this->lf(1);
754 this->writeString("// incomplete");
755 this->lf(1);
756 this->writeEndTag();
757 this->lf(2);
758 this->writeTag("SeeAlso");
759 this->writeSpace();
760 this->writeString("incomplete");
761 this->lf(2);
762 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
763 this->lf(2);
764}
765
Cary Clark224c7002018-06-27 11:00:21 -0400766void IncludeParser::dumpConst(const Definition& token, string className) {
767 this->writeTag("Const");
768 this->writeSpace();
769 this->writeString(token.fName);
770 this->writeTagTable("Line", "incomplete");
771 this->lf(2);
772 this->dumpComment(token);
773}
774
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400775void IncludeParser::dumpDefine(const Definition& token) {
776 this->writeTag("Define", token.fName);
777 this->lf(2);
778 this->writeTag("Code");
779 this->lfAlways(1);
780 this->writeString("###$");
781 this->lfAlways(1);
782 this->indentToColumn(4);
783 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
784 this->lf(1);
785 this->indentToColumn(0);
786 this->writeString("$$$#");
787
788 this->writeEndTag();
789 this->lf(2);
790 this->dumpComment(token);
791 for (auto& child : token.fTokens) {
792 if (MarkType::kComment == child.fMarkType) {
793 continue;
794 }
795 this->writeTag("Param", child.fName);
796 this->writeSpace();
797 this->writeString("incomplete");
798 this->writeSpace();
799 this->writeString("##");
800 this->lf(1);
801 }
802}
803
804void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500805 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400806 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400807 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400808 this->lfAlways(1);
809 this->indentToColumn(4);
810 this->writeString("enum");
811 this->writeSpace();
812 if ("_anonymous" != token.fName.substr(0, 10)) {
813 this->writeString(token.fName);
814 this->writeSpace();
815 }
816 this->writeString("{");
817 this->lfAlways(1);
818 for (auto& child : token.fChildren) {
819 this->indentToColumn(8);
820 this->writeString(child->fName);
821 if (child->length()) {
822 this->writeSpace();
823 this->writeBlock(child->length(), child->fContentStart);
824 }
825 if (',' != fLastChar) {
826 this->writeString(",");
827 }
828 this->lfAlways(1);
829 }
830 this->indentToColumn(4);
831 this->writeString("};");
832 this->lf(1);
833 this->writeString("##");
834 this->lf(2);
835 this->dumpComment(token);
836 for (auto& child : token.fChildren) {
837 // start here;
838 // get comments before
839 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400840 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400841 this->writeSpace();
842 this->writeString(child->fName);
843 TextParser val(child);
844 if (!val.eof()) {
845 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
846 val.next();
847 val.skipSpace();
848 const char* valEnd = val.anyOf(",\n");
849 if (!valEnd) {
850 valEnd = val.fEnd;
851 }
852 this->writeSpace();
853 this->writeBlock(valEnd - val.fStart, val.fStart);
854 } else {
855 this->writeSpace();
856 this->writeDefinition(*child);
857 }
858 }
859 this->lf(1);
860 for (auto comment : child->fChildren) {
861 if (MarkType::kComment == comment->fMarkType) {
862 TextParser parser(comment);
863 parser.skipExact("*");
864 parser.skipExact("*");
865 while (!parser.eof() && parser.skipWhiteSpace()) {
866 parser.skipExact("*");
867 parser.skipWhiteSpace();
868 const char* start = parser.fChar;
869 parser.skipToEndBracket('\n');
870 this->lf(1);
871 this->writeBlock(parser.fChar - start, start);
872 }
873 }
874 }
875 this->writeEndTag();
876 }
877 this->lf(2);
878}
879
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400880bool IncludeParser::dumpGlobals() {
Cary Clark224c7002018-06-27 11:00:21 -0400881 if (fIDefineMap.empty() && fIFunctionMap.empty() && fIEnumMap.empty() && fITemplateMap.empty()
882 && fITypedefMap.empty() && fIUnionMap.empty()) {
883 return true;
884 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400885 size_t lastBSlash = fFileName.rfind('\\');
886 size_t lastSlash = fFileName.rfind('/');
887 size_t lastDotH = fFileName.rfind(".h");
888 SkASSERT(string::npos != lastDotH);
889 if (string::npos != lastBSlash && (string::npos == lastSlash
890 || lastBSlash < lastSlash)) {
891 lastSlash = lastBSlash;
892 } else if (string::npos == lastSlash) {
893 lastSlash = -1;
894 }
895 lastSlash += 1;
896 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
897 string fileName = globalsName + "_Reference.bmh";
898 fOut = fopen(fileName.c_str(), "wb");
899 if (!fOut) {
900 SkDebugf("could not open output file %s\n", globalsName.c_str());
901 return false;
902 }
903 string prefixName = globalsName.substr(0, 2);
904 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
905 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
906 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -0400907 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400908 this->lf(2);
909 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400910 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400911 this->writeEndTag();
912 this->lf(2);
913 if (!fIDefineMap.empty()) {
914 this->writeTag("Subtopic", "Define");
915 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400916 this->writeEndTag();
917 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400918 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400919 if (!fIFunctionMap.empty()) {
920 this->writeTag("Subtopic", "Function");
921 this->writeTag("Populate");
922 this->writeEndTag();
923 this->lf(2);
924 }
925 if (!fIEnumMap.empty()) {
926 this->writeTag("Subtopic", "Enum");
927 this->writeTag("Populate");
928 this->writeEndTag();
929 this->lf(2);
930 }
931 if (!fITemplateMap.empty()) {
932 this->writeTag("Subtopic", "Template");
933 this->writeTag("Populate");
934 this->writeEndTag();
935 this->lf(2);
936 }
937 if (!fITypedefMap.empty()) {
938 this->writeTag("Subtopic", "Typedef");
939 this->writeTag("Populate");
940 this->writeEndTag();
941 this->lf(2);
942 }
943 if (!fIUnionMap.empty()) {
944 this->writeTag("Subtopic", "Union");
945 this->writeTag("Populate");
946 this->writeEndTag();
947 this->lf(2);
948 }
949 std::map<int, Definition*> sortedDefs;
950 for (const auto& entry : fIDefineMap) {
951 sortedDefs[entry.second->fLineCount] = entry.second;
952 }
953 for (const auto& entry : fIFunctionMap) {
954 sortedDefs[entry.second->fLineCount] = entry.second;
955 }
956 for (const auto& entry : fIEnumMap) {
957 sortedDefs[entry.second->fLineCount] = entry.second;
958 }
959 for (const auto& entry : fITemplateMap) {
960 sortedDefs[entry.second->fLineCount] = entry.second;
961 }
962 for (const auto& entry : fITypedefMap) {
963 sortedDefs[entry.second->fLineCount] = entry.second;
964 }
965 for (const auto& entry : fIUnionMap) {
966 sortedDefs[entry.second->fLineCount] = entry.second;
967 }
968 for (const auto& entry : sortedDefs) {
969 const Definition* def = entry.second;
970 this->writeBlockSeparator();
971 switch (def->fMarkType) {
972 case MarkType::kDefine:
973 this->dumpDefine(*def);
974 break;
975 case MarkType::kMethod:
976 this->dumpMethod(*def, globalsName);
977 break;
978 case MarkType::kEnum:
979 case MarkType::kEnumClass:
980 this->dumpEnum(*def, globalsName);
981 break;
982 case MarkType::kTemplate:
983 SkASSERT(0); // incomplete
984 break;
985 case MarkType::kTypedef: {
986 this->writeTag("Typedef");
987 this->writeSpace();
988 TextParser parser(def);
989 if (!parser.skipExact("typedef")) {
990 return false;
991 }
992 if (!parser.skipSpace()) {
993 return false;
994 }
995 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
996 this->lf(2);
997 this->dumpComment(*def);
998 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
999 this->lf(2);
1000 } continue;
1001 case MarkType::kUnion:
1002 SkASSERT(0); // incomplete
1003 break;
1004 default:
1005 SkASSERT(0);
1006 }
1007 this->dumpCommonTail(*def);
1008 }
1009 this->writeEndTag("Topic", topicName);
1010 this->lfAlways(1);
1011 fclose(fOut);
1012 SkDebugf("wrote %s\n", fileName.c_str());
1013 return true;
1014}
1015
1016bool IncludeParser::isClone(const Definition& token) {
1017 string name = token.fName;
1018 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1019}
1020
1021bool IncludeParser::isConstructor(const Definition& token, string className) {
1022 string name = token.fName;
1023 return 0 == name.find(className) || '~' == name[0];
1024}
1025
1026bool IncludeParser::isInternalName(const Definition& token) {
1027 string name = token.fName;
1028 // exception for this SkCanvas function .. for now
1029 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1030 return false;
1031 }
1032 return name.substr(0, 7) == "android"
1033 || 0 == token.fName.find("internal_")
1034 || 0 == token.fName.find("Internal_")
1035 || 0 == token.fName.find("legacy_")
1036 || 0 == token.fName.find("temporary_")
1037 || 0 == token.fName.find("private_");
1038}
1039
1040bool IncludeParser::isOperator(const Definition& token) {
1041 return "operator" == token.fName.substr(0, 8);
1042}
1043
1044void IncludeParser::dumpMethod(const Definition& token, string className) {
1045 this->writeTag("Method");
1046 this->writeSpace();
1047
1048 string name = string(token.fStart ? token.fStart : token.fContentStart,
1049 token.length());
1050 if (this->isOperator(token)) {
1051 string spaceConst(" const");
1052 size_t constPos = name.rfind(spaceConst);
1053 if (name.length() - spaceConst.length() == constPos) {
1054 name = name.substr(0, constPos) + "_const";
1055 }
1056 }
Cary Clark224c7002018-06-27 11:00:21 -04001057 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001058 string inType;
1059 if (this->isConstructor(token, className)) {
1060 inType = "Constructor";
1061 } else if (this->isOperator(token)) {
1062 inType = "Operator";
1063 } else {
1064 inType = "incomplete";
1065 }
1066 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001067 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001068 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001069 this->dumpComment(token);
1070}
1071
1072void IncludeParser::dumpMember(const Definition& token) {
1073 this->writeTag("Member");
1074 this->writeSpace();
1075 this->writeDefinition(token, token.fName, 2);
1076 lf(1);
1077 for (auto child : token.fChildren) {
1078 this->writeDefinition(*child);
1079 }
1080 this->writeEndTag();
1081 lf(2);
1082}
1083
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001084bool IncludeParser::dumpTokens() {
1085 if (!this->dumpGlobals()) {
1086 return false;
1087 }
Cary Clark9174bda2017-09-19 17:39:32 -04001088 for (const auto& member : fIClassMap) {
1089 if (string::npos != member.first.find("::")) {
1090 continue;
1091 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001092 if (!this->dumpTokens(member.first)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001093 return false;
1094 }
1095 }
1096 return true;
1097}
1098
Ben Wagner63fd7602017-10-09 15:45:33 -04001099 // dump equivalent markup
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001100bool IncludeParser::dumpTokens(string skClassName) {
1101 string fileName = skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -04001102 fOut = fopen(fileName.c_str(), "wb");
1103 if (!fOut) {
1104 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -04001105 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001106 }
1107 string prefixName = skClassName.substr(0, 2);
1108 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1109 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -04001110 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001111 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark9174bda2017-09-19 17:39:32 -04001112 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001113 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001114 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1115 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001116 this->writeTag(containerType, skClassName);
1117 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001118 auto& tokens = classMap.fTokens;
1119 for (auto& token : tokens) {
1120 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1121 continue;
1122 }
Cary Clark9174bda2017-09-19 17:39:32 -04001123 this->writeDefinition(token);
1124 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001125 }
1126 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001127 bool hasClass = false;
1128 bool hasConst = !fIEnumMap.empty();
1129 bool hasConstructor = false;
1130 bool hasMember = false;
1131 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001132 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001133 for (const auto& oneClass : fIClassMap) {
1134 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1135 continue;
1136 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001137 hasClass = true;
1138 break;
Cary Clark8032b982017-07-28 11:04:54 -04001139 }
Cary Clark224c7002018-06-27 11:00:21 -04001140 for (const auto& oneStruct : fIStructMap) {
1141 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1142 continue;
1143 }
1144 hasStruct = true;
1145 break;
1146 }
Cary Clark8032b982017-07-28 11:04:54 -04001147 for (const auto& token : classMap.fTokens) {
1148 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1149 continue;
1150 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001151 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001152 continue;
1153 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001154 if (this->isConstructor(token, skClassName)) {
1155 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001156 continue;
1157 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001158 if (this->isOperator(token)) {
1159 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001160 continue;
1161 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001162 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001163 continue;
1164 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001165 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001166 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001167 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001168 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001169 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001170 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001171
1172 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001173 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001174 this->writeTag("Populate");
1175 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001176 this->lf(2);
1177 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001178 if (hasConst) {
1179 this->writeTag("Subtopic", "Constant");
1180 this->writeTag("Populate");
1181 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001182 this->lf(2);
1183 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001184 if (hasConstructor) {
1185 this->writeTag("Subtopic", "Constructor");
1186 this->writeTag("Populate");
1187 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001188 this->lf(2);
1189 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001190 if (hasOperator) {
1191 this->writeTag("Subtopic", "Operator");
1192 this->writeTag("Populate");
1193 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001194 this->lf(2);
1195 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001196 if (hasMember) {
1197 this->writeTag("Subtopic", "Member_Function");
1198 this->writeTag("Populate");
1199 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001200 this->lf(2);
1201 }
Cary Clark224c7002018-06-27 11:00:21 -04001202 if (hasStruct) {
1203 this->writeTag("Subtopic", "Struct");
1204 this->writeTag("Populate");
1205 this->writeEndTag();
1206 this->lf(2);
1207 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001208 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001209 this->writeBlockSeparator();
1210 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001211 this->lf(2);
1212 this->writeTag("Example");
1213 this->lfcr();
1214 this->writeString("// incomplete");
1215 this->writeEndTag();
1216 this->lf(2);
1217 this->writeTag("SeeAlso", "incomplete");
1218 this->lf(2);
1219 this->writeEndTag("Enum", oneEnum.first);
1220 this->lf(2);
1221 }
Cary Clark8032b982017-07-28 11:04:54 -04001222 for (auto& oneClass : fIClassMap) {
1223 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1224 continue;
1225 }
1226 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001227 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001228 KeyWord keyword = oneClass.second.fKeyWord;
1229 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1230 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001231 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001232 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001233 this->lf(2);
1234 this->writeTag("Code");
1235 this->writeEndTag("ToDo", "fill this in manually");
1236 this->writeEndTag();
1237 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001238 for (auto& token : oneClass.second.fTokens) {
1239 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1240 continue;
1241 }
Cary Clark9174bda2017-09-19 17:39:32 -04001242 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001243 }
1244 this->lf(2);
1245 this->dumpClassTokens(oneClass.second);
1246 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001247 this->writeEndTag(containerType, innerName);
1248 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001249 }
1250 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001251 this->writeEndTag(containerType, skClassName);
1252 this->lf(2);
1253 this->writeEndTag("Topic", topicName);
1254 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001255 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001256 SkDebugf("wrote %s\n", fileName.c_str());
1257 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001258}
1259
1260bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1261 // add comment preceding class, if any
1262 const Definition* parent = includeDef.fParent;
1263 int index = includeDef.fParentIndex;
1264 auto wordIter = parent->fTokens.begin();
1265 std::advance(wordIter, index);
1266 SkASSERT(&*wordIter == &includeDef);
1267 while (parent->fTokens.begin() != wordIter) {
1268 auto testIter = std::prev(wordIter);
1269 if (Definition::Type::kWord != testIter->fType
1270 && Definition::Type::kKeyWord != testIter->fType
1271 && (Definition::Type::kBracket != testIter->fType
1272 || Bracket::kAngle != testIter->fBracket)
1273 && (Definition::Type::kPunctuation != testIter->fType
1274 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1275 break;
1276 }
1277 wordIter = testIter;
1278 }
1279 auto commentIter = wordIter;
1280 while (parent->fTokens.begin() != commentIter) {
1281 auto testIter = std::prev(commentIter);
1282 bool isComment = Definition::Type::kBracket == testIter->fType
1283 && (Bracket::kSlashSlash == testIter->fBracket
1284 || Bracket::kSlashStar == testIter->fBracket);
1285 if (!isComment) {
1286 break;
1287 }
1288 commentIter = testIter;
1289 }
1290 while (commentIter != wordIter) {
1291 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1292 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1293 return false;
1294 }
1295 commentIter = std::next(commentIter);
1296 }
1297 return true;
1298}
1299
Cary Clark0d225392018-06-07 09:59:07 -04001300Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1301 string typeName) {
1302 typedef Definition* DefinitionPtr;
1303 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1304 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1305 if (mapIter == fMaps.end()) {
1306 return nullptr;
1307 }
1308 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1309 return reportError<DefinitionPtr>("invalid mark type");
1310 }
1311 string name = this->uniqueName(*mapIter->fInclude, typeName);
1312 Definition& markupDef = *(*mapIter->fInclude)[name];
1313 if (markupDef.fStart) {
1314 return reportError<DefinitionPtr>("definition already defined");
1315 }
1316 markupDef.fFileName = fFileName;
1317 markupDef.fStart = includeDef.fStart;
1318 markupDef.fContentStart = includeDef.fStart;
1319 markupDef.fName = name;
1320 markupDef.fContentEnd = includeDef.fContentEnd;
1321 markupDef.fTerminator = includeDef.fTerminator;
1322 markupDef.fParent = fParent;
1323 markupDef.fLineCount = includeDef.fLineCount;
1324 markupDef.fMarkType = markType;
1325 markupDef.fKeyWord = includeDef.fKeyWord;
1326 markupDef.fType = Definition::Type::kMark;
1327 return &markupDef;
1328}
1329
Cary Clark224c7002018-06-27 11:00:21 -04001330Definition* IncludeParser::parentBracket(Definition* parent) const {
1331 while (parent && Definition::Type::kBracket != parent->fType) {
1332 parent = parent->fParent;
1333 }
1334 return parent;
1335}
1336
1337Bracket IncludeParser::grandParentBracket() const {
1338 Definition* parent = parentBracket(fParent);
1339 parent = parentBracket(parent ? parent->fParent : nullptr);
1340 return parent ? parent->fBracket : Bracket::kNone;
1341}
1342
Cary Clark137b8742018-05-30 09:21:49 -04001343// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001344bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1345 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001346 // parse class header
1347 auto iter = includeDef->fTokens.begin();
1348 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1349 // todo : documentation is ignoring this for now
1350 iter = std::next(iter);
1351 }
1352 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1353 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001354 iter = std::next(iter);
1355 if (iter == includeDef->fTokens.end()) {
1356 return true; // forward declaration only
1357 }
Cary Clark8032b982017-07-28 11:04:54 -04001358 do {
1359 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001360 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001361 }
1362 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1363 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001364 }
Cary Clark8032b982017-07-28 11:04:54 -04001365 } while (static_cast<void>(iter = std::next(iter)), true);
1366 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001367 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001368 }
1369 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1370 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001371 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001372 }
1373 markupDef->fStart = iter->fStart;
1374 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001375 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001376 }
1377// if (1 != includeDef->fChildren.size()) {
1378// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1379// }
1380 includeDef = includeDef->fChildren.front();
1381 iter = includeDef->fTokens.begin();
1382 // skip until public
1383 int publicIndex = 0;
1384 if (IsStruct::kNo == isStruct) {
1385 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1386 size_t publicLen = strlen(publicName);
1387 while (iter != includeDef->fTokens.end()
1388 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1389 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001390 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001391 iter = std::next(iter);
1392 ++publicIndex;
1393 }
1394 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001395 int keyIndex = publicIndex;
1396 KeyWord currentKey = KeyWord::kPublic;
1397 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1398 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001399 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1400 size_t protectedLen = strlen(protectedName);
1401 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1402 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001403 auto childIter = includeDef->fChildren.begin();
Cary Clarkb7a72a52018-06-15 05:11:24 -04001404 while ((*childIter)->fPrivate) {
1405 std::advance(childIter, 1);
1406 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001407 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001408 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001409 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001410 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001411 const char* testStart = iter->fStart;
1412 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1413 iter = std::next(iter);
1414 ++keyIndex;
1415 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1416 currentKey = KeyWord::kPublic;
1417 break;
1418 }
1419 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1420 currentKey = KeyWord::kProtected;
1421 break;
1422 }
1423 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1424 currentKey = KeyWord::kPrivate;
1425 break;
1426 }
1427 }
1428 fLastObject = nullptr;
1429 if (KeyWord::kPublic == currentKey) {
1430 if (!this->parseObject(child, markupDef)) {
1431 return false;
1432 }
Cary Clark8032b982017-07-28 11:04:54 -04001433 }
Cary Clark73fa9722017-08-29 17:36:51 -04001434 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001435 childIter = std::next(childIter);
1436 }
Cary Clark137b8742018-05-30 09:21:49 -04001437 while (iter != includeDef->fTokens.end()) {
1438 iter->fPrivate = KeyWord::kPublic != currentKey;
1439 iter = std::next(iter);
1440 }
Cary Clark8032b982017-07-28 11:04:54 -04001441 SkASSERT(fParent->fParent);
1442 fParent = fParent->fParent;
1443 return true;
1444}
1445
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001446bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001447 int lineCount, Definition* markupDef) {
1448 TextParser parser(filename, start, end, lineCount);
1449 // parse doxygen if present
1450 if (parser.startsWith("**")) {
1451 parser.next();
1452 parser.next();
1453 parser.skipWhiteSpace();
1454 if ('\\' == parser.peek()) {
1455 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001456 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1457 if (parser.skipExact("file")) {
1458 if (Definition::Type::kFileType != fParent->fType) {
1459 return reportError<bool>("expected parent is file");
1460 }
1461 string filename = markupDef->fileName();
1462 if (!parser.skipWord(filename.c_str())) {
1463 return reportError<bool>("missing object type");
1464 }
1465 } else if (parser.skipExact("fn")) {
1466 SkASSERT(0); // incomplete
1467 } else {
1468 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1469 return reportError<bool>("missing object type");
1470 }
1471 if (!parser.skipWord(markupDef->fName.c_str()) &&
1472 KeyWord::kEnum != markupDef->fKeyWord) {
1473 return reportError<bool>("missing object name");
1474 }
Cary Clark8032b982017-07-28 11:04:54 -04001475 }
Cary Clark8032b982017-07-28 11:04:54 -04001476 }
1477 }
1478 // remove leading '*' if present
1479 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1480 while (!parser.eof() && parser.skipWhiteSpace()) {
1481 while ('*' == parser.peek()) {
1482 parser.next();
1483 if (parser.eof()) {
1484 break;
1485 }
1486 parser.skipWhiteSpace();
1487 }
1488 if (parser.eof()) {
1489 break;
1490 }
1491 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001492 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001493 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001494 parser.skipToEndBracket('\n');
1495 }
1496 return true;
1497}
1498
Cary Clarkd98f78c2018-04-26 08:32:37 -04001499bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04001500 if (!markupDef) {
1501 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1502 child->fLineCount, fParent, '\0');
1503 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04001504 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04001505 globalMarkupChild->fName = globalUniqueName;
1506 if (!this->findComments(*child, globalMarkupChild)) {
1507 return false;
1508 }
1509 fIConstMap[globalUniqueName] = globalMarkupChild;
1510 return true;
1511 }
1512 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1513 child->fLineCount, markupDef, '\0');
1514 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04001515 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001516 markupChild->fTerminator = markupChild->fContentEnd;
1517 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04001518 classDef.fConsts[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001519 return true;
1520}
1521
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001522bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1523 TextParser parser(child);
1524 if (!parser.skipExact("#define")) {
1525 return false;
1526 }
1527 if (!parser.skipSpace()) {
1528 return false;
1529 }
1530 const char* nameStart = parser.fChar;
1531 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1532 if (parser.eof()) {
1533 return true; // do nothing if #define doesn't define anything
1534 }
1535 string nameStr(nameStart, parser.fChar - nameStart);
1536 struct Param {
1537 const char* fStart;
1538 const char* fEnd;
1539 };
1540 vector<Param> params;
1541 if ('(' == parser.peek()) {
1542 parser.next();
1543 if (!parser.skipSpace()) {
1544 return false;
1545 }
1546 do {
1547 const char* paramStart = parser.fChar;
1548 if (!parser.skipExact("...")) {
1549 parser.skipToNonAlphaNum();
1550 }
1551 if (parser.eof()) {
1552 return false;
1553 }
1554 params.push_back({paramStart, parser.fChar});
1555 if (!parser.skipSpace()) {
1556 return false;
1557 }
1558 if (')' == parser.peek()) {
1559 parser.next();
1560 break;
1561 }
1562 if (',' != parser.next()) {
1563 return false;
1564 }
1565 if (!parser.skipSpace()) {
1566 return false;
1567 }
1568 } while (true);
1569 }
1570 if (!parser.skipSpace()) {
1571 return false;
1572 }
1573 if (!markupDef) {
1574 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1575 child->fLineCount, fParent, '\0');
1576 Definition* globalMarkupChild = &fGlobals.back();
1577 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1578 globalMarkupChild->fName = globalUniqueName;
1579 globalMarkupChild->fTerminator = child->fContentEnd;
1580 if (!this->findComments(*child, globalMarkupChild)) {
1581 return false;
1582 }
1583 fIDefineMap[globalUniqueName] = globalMarkupChild;
1584 for (Param param : params) {
1585 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1586 child->fLineCount, globalMarkupChild, '\0');
1587 Definition* paramChild = &globalMarkupChild->fTokens.back();
1588 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1589 paramChild->fTerminator = param.fEnd;
1590 }
1591 return true;
1592 }
1593 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1594 child->fLineCount, markupDef, '\0');
1595 Definition* markupChild = &markupDef->fTokens.back();
1596 markupChild->fName = nameStr;
1597 markupChild->fTerminator = markupChild->fContentEnd;
1598 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1599 if (!this->findComments(*child, markupChild)) {
1600 return false;
1601 }
1602 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001603 return true;
1604}
1605
1606bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001607 TextParser parser(child);
1608 parser.skipToEndBracket('{');
1609 if (parser.eof()) {
1610 return true; // if enum is a forward declaration, do nothing
1611 }
1612 parser.next();
1613 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001614 if (child->fTokens.size() > 0) {
1615 auto token = child->fTokens.begin();
1616 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1617 token = token->fTokens.begin();
1618 }
1619 if (Definition::Type::kWord == token->fType) {
1620 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1621 }
1622 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001623 Definition* markupChild;
1624 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001625 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1626 child->fLineCount, fParent, '\0');
1627 markupChild = &fGlobals.back();
1628 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1629 markupChild->fName = globalUniqueName;
1630 markupChild->fTerminator = child->fContentEnd;
1631 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001632 } else {
1633 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001634 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001635 markupChild = &markupDef->fTokens.back();
1636 }
Cary Clark8032b982017-07-28 11:04:54 -04001637 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1638 markupChild->fKeyWord = KeyWord::kEnum;
1639 TextParser enumName(child);
1640 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001641 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001642 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001643 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001644 markupChild->fMarkType = MarkType::kEnumClass;
1645 }
Cary Clark8032b982017-07-28 11:04:54 -04001646 const char* nameStart = enumName.fChar;
1647 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001648 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001649 markupChild->fName = markupDef->fName + "::" +
1650 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001651 }
Cary Clark8032b982017-07-28 11:04:54 -04001652 if (!this->findComments(*child, markupChild)) {
1653 return false;
1654 }
Cary Clark8032b982017-07-28 11:04:54 -04001655 const char* dataEnd;
1656 do {
Cary Clark8032b982017-07-28 11:04:54 -04001657 parser.skipWhiteSpace();
1658 if ('}' == parser.peek()) {
1659 break;
1660 }
1661 Definition* comment = nullptr;
1662 // note that comment, if any, can be before or after (on the same line, though) as member
1663 if ('#' == parser.peek()) {
1664 // fixme: handle preprecessor, but just skip it for now
1665 parser.skipToLineStart();
1666 }
1667 while (parser.startsWith("/*") || parser.startsWith("//")) {
1668 parser.next();
1669 const char* start = parser.fChar;
1670 const char* end;
1671 if ('*' == parser.peek()) {
1672 end = parser.strnstr("*/", parser.fEnd);
1673 parser.fChar = end;
1674 parser.next();
1675 parser.next();
1676 } else {
1677 end = parser.trimmedLineEnd();
1678 parser.skipToLineStart();
1679 }
1680 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001681 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001682 comment = &markupChild->fTokens.back();
1683 comment->fTerminator = end;
1684 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1685 return false;
1686 }
1687 parser.skipWhiteSpace();
1688 }
1689 parser.skipWhiteSpace();
1690 const char* memberStart = parser.fChar;
1691 if ('}' == memberStart[0]) {
1692 break;
1693 }
Cary Clark9174bda2017-09-19 17:39:32 -04001694 // if there's comment on same the line as member def, output first as if it was before
1695
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001696 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001697 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001698 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04001699 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04001700 }
Cary Clark8032b982017-07-28 11:04:54 -04001701 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001702 if ('=' == parser.peek()) {
1703 parser.skipToEndBracket(',');
1704 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001705 if (!parser.eof() && '#' == parser.peek()) {
1706 // fixme: handle preprecessor, but just skip it for now
1707 continue;
1708 }
Cary Clark9174bda2017-09-19 17:39:32 -04001709 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04001710 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04001711 }
1712 dataEnd = parser.fChar;
1713 const char* start = parser.anyOf("/\n");
1714 SkASSERT(start);
1715 parser.skipTo(start);
1716 if ('/' == parser.next()) {
1717 char slashStar = parser.next();
1718 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001719 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001720 char doxCheck = parser.next();
1721 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1722 save.restore();
1723 }
1724 }
1725 parser.skipWhiteSpace();
1726 const char* commentStart = parser.fChar;
1727 if ('/' == slashStar) {
1728 parser.skipToEndBracket('\n');
1729 } else {
1730 parser.skipToEndBracket("*/");
1731 }
1732 SkASSERT(!parser.eof());
1733 const char* commentEnd = parser.fChar;
1734 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001735 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001736 comment = &markupChild->fTokens.back();
1737 comment->fTerminator = commentEnd;
1738 }
Cary Clark8032b982017-07-28 11:04:54 -04001739 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001740 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001741 Definition* member = &markupChild->fTokens.back();
1742 member->fName = memberName;
1743 if (comment) {
1744 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001745 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001746 }
1747 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001748 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001749 for (auto outsideMember : child->fChildren) {
1750 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001751 continue;
1752 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001753 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1754 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001755 continue;
1756 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001757 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1758 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001759 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001760 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001761 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001762 // FIXME: ? add comment as well ?
1763 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001764 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001765 if (markupDef) {
1766 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1767 SkASSERT(classDef.fStart);
1768 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1769 markupChild->fName = uniqueName;
1770 classDef.fEnums[uniqueName] = markupChild;
1771 }
Cary Clark8032b982017-07-28 11:04:54 -04001772 return true;
1773}
1774
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001775bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001776 fParent = &fIncludeMap[name];
1777 fParent->fName = name;
1778 fParent->fFileName = fFileName;
1779 fParent->fType = Definition::Type::kFileType;
1780 fParent->fContentStart = fChar;
1781 fParent->fContentEnd = fEnd;
1782 // parse include file into tree
1783 while (fChar < fEnd) {
1784 if (!this->parseChar()) {
1785 return false;
1786 }
1787 }
1788 // parse tree and add named objects to maps
1789 fParent = &fIncludeMap[name];
1790 if (!this->parseObjects(fParent, nullptr)) {
1791 return false;
1792 }
1793 return true;
1794}
1795
1796bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1797 const char* typeStart = child->fChildren[0]->fContentStart;
1798 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001799 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001800 Definition* markupChild = &markupDef->fTokens.back();
1801 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001802 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001803 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1804 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1805 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1806 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001807 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001808 classDef.fMembers[uniqueName] = markupChild;
1809 if (child->fParentIndex >= 2) {
1810 auto comment = child->fParent->fTokens.begin();
1811 std::advance(comment, child->fParentIndex - 2);
1812 if (Definition::Type::kBracket == comment->fType
1813 && (Bracket::kSlashStar == comment->fBracket
1814 || Bracket::kSlashSlash == comment->fBracket)) {
1815 TextParser parser(&*comment);
1816 do {
1817 parser.skipToAlpha();
1818 if (parser.eof()) {
1819 break;
1820 }
1821 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001822 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001823 if (Bracket::kSlashStar == comment->fBracket) {
1824 const char* commentEnd = parser.strnstr("*/", end);
1825 if (commentEnd) {
1826 end = commentEnd;
1827 }
1828 }
1829 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001830 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001831 Definition* commentChild = &markupDef->fTokens.back();
1832 markupChild->fChildren.emplace_back(commentChild);
1833 parser.skipTo(end);
1834 } while (!parser.eof());
1835 }
1836 }
1837 return true;
1838}
1839
1840bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1841 auto tokenIter = child->fParent->fTokens.begin();
1842 std::advance(tokenIter, child->fParentIndex);
1843 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001844 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001845 bool addConst = false;
1846 auto operatorCheck = tokenIter;
1847 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1848 operatorCheck = std::prev(tokenIter);
1849 }
1850 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001851 auto closeParen = std::next(tokenIter);
1852 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1853 '(' == closeParen->fContentStart[0]);
1854 nameEnd = closeParen->fContentEnd + 1;
1855 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001856 if (Definition::Type::kKeyWord == closeParen->fType &&
1857 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001858 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001859 }
Cary Clarka560c472017-11-27 10:44:06 -05001860 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001861 }
1862 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001863 if (addConst) {
1864 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001865 }
Cary Clark8032b982017-07-28 11:04:54 -04001866 while (tokenIter != child->fParent->fTokens.begin()) {
1867 auto testIter = std::prev(tokenIter);
1868 switch (testIter->fType) {
1869 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001870 if (testIter == child->fParent->fTokens.begin() &&
1871 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1872 KeyWord::kIfndef == child->fParent->fKeyWord ||
1873 KeyWord::kIf == child->fParent->fKeyWord)) {
1874 std::next(tokenIter);
1875 break;
1876 }
Cary Clark8032b982017-07-28 11:04:54 -04001877 goto keepGoing;
1878 case Definition::Type::kKeyWord: {
1879 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1880 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1881 goto keepGoing;
1882 }
1883 } break;
1884 case Definition::Type::kBracket:
1885 if (Bracket::kAngle == testIter->fBracket) {
1886 goto keepGoing;
1887 }
1888 break;
1889 case Definition::Type::kPunctuation:
1890 if (Punctuation::kSemicolon == testIter->fPunctuation
1891 || Punctuation::kLeftBrace == testIter->fPunctuation
1892 || Punctuation::kColon == testIter->fPunctuation) {
1893 break;
1894 }
1895 keepGoing:
1896 tokenIter = testIter;
1897 continue;
1898 default:
1899 break;
1900 }
1901 break;
1902 }
Cary Clark224c7002018-06-27 11:00:21 -04001903 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04001904 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001905 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001906 auto testIter = child->fParent->fTokens.begin();
1907 SkASSERT(child->fParentIndex > 0);
1908 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001909 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1910 0 == tokenIter->fParentIndex) {
1911 tokenIter = std::next(tokenIter);
1912 }
Cary Clark8032b982017-07-28 11:04:54 -04001913 const char* start = tokenIter->fContentStart;
1914 const char* end = tokenIter->fContentEnd;
1915 const char kDebugCodeStr[] = "SkDEBUGCODE";
1916 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1917 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1918 std::advance(testIter, 1);
1919 start = testIter->fContentStart + 1;
1920 end = testIter->fContentEnd - 1;
1921 } else {
1922 end = testIter->fContentEnd;
1923 while (testIter != child->fParent->fTokens.end()) {
1924 testIter = std::next(testIter);
1925 switch (testIter->fType) {
1926 case Definition::Type::kPunctuation:
1927 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1928 || Punctuation::kLeftBrace == testIter->fPunctuation
1929 || Punctuation::kColon == testIter->fPunctuation);
1930 end = testIter->fStart;
1931 break;
1932 case Definition::Type::kKeyWord: {
1933 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1934 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1935 continue;
1936 }
1937 } break;
1938 default:
1939 continue;
1940 }
1941 break;
1942 }
1943 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001944 while (end > start && ' ' >= end[-1]) {
1945 --end;
1946 }
Cary Clark9174bda2017-09-19 17:39:32 -04001947 if (!markupDef) {
1948 auto parentIter = child->fParent->fTokens.begin();
1949 SkASSERT(child->fParentIndex > 0);
1950 std::advance(parentIter, child->fParentIndex - 1);
1951 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001952 TextParser nameParser(methodName);
1953 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001954 return true; // expect this is inline class definition outside of class
1955 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001956 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1957 fParent, '\0');
1958 Definition* globalMarkupChild = &fGlobals.back();
1959 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1960 globalMarkupChild->fName = globalUniqueName;
1961 if (!this->findComments(*child, globalMarkupChild)) {
1962 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001963 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001964 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001965 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001966 }
Cary Clark8032b982017-07-28 11:04:54 -04001967 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001968 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001969 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04001970 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
1971 {
1972 auto mapIter = fIClassMap.find(markupDef->fName);
1973 SkASSERT(fIClassMap.end() != mapIter);
1974 IClassDefinition& classDef = mapIter->second;
1975 SkASSERT(classDef.fStart);
1976 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1977 markupChild->fName = uniqueName;
1978 if (!this->findComments(*child, markupChild)) {
1979 return false;
1980 }
1981 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001982 }
Cary Clark8032b982017-07-28 11:04:54 -04001983 return true;
1984}
1985
Cary Clark8032b982017-07-28 11:04:54 -04001986bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04001987 fPriorObject = nullptr;
1988 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04001989 if (!this->parseObject(child, markupDef)) {
1990 return false;
1991 }
Cary Clark0d225392018-06-07 09:59:07 -04001992 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001993 }
1994 return true;
1995}
1996
1997bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1998 // set up for error reporting
1999 fLine = fChar = child->fStart;
2000 fEnd = child->fContentEnd;
2001 // todo: put original line number in child as well
2002 switch (child->fType) {
2003 case Definition::Type::kKeyWord:
2004 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002005 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002006 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002007 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002008 }
2009 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002010 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002011 case KeyWord::kConst:
2012 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002013 if (!this->parseConst(child, markupDef)) {
2014 return child->reportError<bool>("failed to parse const or constexpr");
2015 }
2016 break;
Cary Clark8032b982017-07-28 11:04:54 -04002017 case KeyWord::kEnum:
2018 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002019 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002020 }
2021 break;
2022 case KeyWord::kStruct:
2023 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002024 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002025 }
2026 break;
2027 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002028 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002029 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002030 }
2031 break;
2032 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002033 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002034 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002035 }
2036 break;
2037 case KeyWord::kUnion:
2038 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002039 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002040 }
2041 break;
2042 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002043 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002044 }
2045 break;
2046 case Definition::Type::kBracket:
2047 switch (child->fBracket) {
2048 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04002049 if (fLastObject) {
2050 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
2051 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04002052 if (!checkDeprecated.eof()) {
2053 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002054 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2055 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002056 break;
2057 }
2058 }
2059 }
2060 {
2061 auto tokenIter = child->fParent->fTokens.begin();
2062 std::advance(tokenIter, child->fParentIndex);
2063 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002064 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002065 if (previousToken.startsWith(gAttrDeprecated)) {
2066 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002067 break;
2068 }
Cary Clarka7401902018-06-14 15:34:14 -04002069 if ('f' == previousToken.fStart[0] && isupper(previousToken.fStart[1])) {
2070 break;
2071 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002072 if (Bracket::kPound == child->fParent->fBracket &&
2073 KeyWord::kIf == child->fParent->fKeyWord) {
2074 // TODO: this will skip methods named defined() -- for the
2075 // moment there aren't any
2076 if (previousToken.startsWith("defined")) {
2077 break;
2078 }
2079 }
Cary Clark73fa9722017-08-29 17:36:51 -04002080 }
Cary Clark0d225392018-06-07 09:59:07 -04002081 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2082 break;
2083 }
Cary Clark8032b982017-07-28 11:04:54 -04002084 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002085 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002086 }
Cary Clark89b14562018-03-19 09:04:10 -04002087 if (fAttrDeprecated) {
2088 Definition* lastMethod = &markupDef->fTokens.back();
2089 lastMethod->fDeprecated = true;
2090 fAttrDeprecated = nullptr;
2091 }
Cary Clark73fa9722017-08-29 17:36:51 -04002092 break;
Cary Clark8032b982017-07-28 11:04:54 -04002093 case Bracket::kSlashSlash:
2094 case Bracket::kSlashStar:
2095 // comments are picked up by parsing objects first
2096 break;
2097 case Bracket::kPound:
2098 // special-case the #xxx xxx_DEFINED entries
2099 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002100 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002101 case KeyWord::kIfndef:
2102 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002103 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002104 if (!this->parseObjects(child, markupDef)) {
2105 return false;
2106 }
2107 break;
2108 }
2109 goto preproError;
2110 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002111 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002112 break;
2113 }
2114 goto preproError;
2115 case KeyWord::kEndif:
2116 if (child->boilerplateEndIf()) {
2117 break;
2118 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002119 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002120 case KeyWord::kInclude:
2121 // ignored for now
2122 break;
2123 case KeyWord::kElse:
2124 case KeyWord::kElif:
2125 // todo: handle these
2126 break;
2127 default:
2128 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002129 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002130 }
2131 break;
2132 case Bracket::kAngle:
2133 // pick up templated function pieces when method is found
2134 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002135 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002136 if (!this->parseObjects(child, markupDef)) {
2137 return false;
2138 }
Cary Clark73fa9722017-08-29 17:36:51 -04002139 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002140 case Bracket::kSquare: {
2141 // check to see if parent is operator, the only case we handle so far
2142 auto prev = child->fParent->fTokens.begin();
2143 std::advance(prev, child->fParentIndex - 1);
2144 if (KeyWord::kOperator != prev->fKeyWord) {
2145 return child->reportError<bool>("expected operator overload");
2146 }
2147 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002148 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002149 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002150 }
2151 break;
2152 case Definition::Type::kWord:
2153 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002154 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002155 }
2156 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002157 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002158 }
2159 break;
2160 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002161 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002162 break;
2163 }
2164 return true;
2165}
2166
Cary Clarkbbfda252018-03-09 15:32:01 -05002167bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2168 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002169}
2170
Cary Clark2f466242017-12-11 16:03:17 -05002171bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2172 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002173 typedefParser.skipExact("typedef");
2174 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002175 string nameStr = typedefParser.typedefName();
2176 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002177 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2178 child->fLineCount, fParent, '\0');
2179 Definition* globalMarkupChild = &fGlobals.back();
2180 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2181 globalMarkupChild->fName = globalUniqueName;
2182 if (!this->findComments(*child, globalMarkupChild)) {
2183 return false;
2184 }
2185 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002186 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002187 return true;
2188 }
2189 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002190 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002191 Definition* markupChild = &markupDef->fTokens.back();
2192 markupChild->fName = nameStr;
2193 markupChild->fTerminator = markupChild->fContentEnd;
2194 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2195 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002196 child->fName = markupDef->fName + "::" + nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002197 return true;
2198}
2199
2200bool IncludeParser::parseUnion() {
2201
2202 return true;
2203}
2204
2205bool IncludeParser::parseChar() {
2206 char test = *fChar;
2207 if ('\\' == fPrev) {
2208 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002209// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002210 fLine = fChar + 1;
2211 }
2212 goto done;
2213 }
2214 switch (test) {
2215 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002216// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002217 fLine = fChar + 1;
2218 if (fInChar) {
2219 return reportError<bool>("malformed char");
2220 }
2221 if (fInString) {
2222 return reportError<bool>("malformed string");
2223 }
2224 if (!this->checkForWord()) {
2225 return false;
2226 }
2227 if (Bracket::kPound == this->topBracket()) {
2228 KeyWord keyWord = fParent->fKeyWord;
2229 if (KeyWord::kNone == keyWord) {
2230 return this->reportError<bool>("unhandled preprocessor directive");
2231 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002232 if (fInDefine) {
2233 SkASSERT(KeyWord::kDefine == keyWord);
2234 fInDefine = false;
2235 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002236 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002237 this->popBracket();
2238 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002239 if (fInBrace) {
2240 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2241 fInBrace = nullptr;
2242 }
Cary Clark8032b982017-07-28 11:04:54 -04002243 } else if (Bracket::kSlashSlash == this->topBracket()) {
2244 this->popBracket();
2245 }
2246 break;
2247 case '*':
2248 if (!fInCharCommentString && '/' == fPrev) {
2249 this->pushBracket(Bracket::kSlashStar);
2250 }
2251 if (!this->checkForWord()) {
2252 return false;
2253 }
2254 if (!fInCharCommentString) {
2255 this->addPunctuation(Punctuation::kAsterisk);
2256 }
2257 break;
2258 case '/':
2259 if ('*' == fPrev) {
2260 if (!fInCharCommentString) {
2261 return reportError<bool>("malformed closing comment");
2262 }
2263 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002264 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002265 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002266 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002267 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002268 }
2269 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002270 }
Cary Clark8032b982017-07-28 11:04:54 -04002271 if (!fInCharCommentString && '/' == fPrev) {
2272 this->pushBracket(Bracket::kSlashSlash);
2273 break;
2274 }
2275 if (!this->checkForWord()) {
2276 return false;
2277 }
2278 break;
2279 case '\'':
2280 if (Bracket::kChar == this->topBracket()) {
2281 this->popBracket();
2282 } else if (!fInComment && !fInString) {
2283 if (fIncludeWord) {
2284 return this->reportError<bool>("word then single-quote");
2285 }
2286 this->pushBracket(Bracket::kChar);
2287 }
2288 break;
2289 case '\"':
2290 if (Bracket::kString == this->topBracket()) {
2291 this->popBracket();
2292 } else if (!fInComment && !fInChar) {
2293 if (fIncludeWord) {
2294 return this->reportError<bool>("word then double-quote");
2295 }
2296 this->pushBracket(Bracket::kString);
2297 }
2298 break;
Cary Clark8032b982017-07-28 11:04:54 -04002299 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002300 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002301 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2302 this->pushBracket(Bracket::kDebugCode);
2303 break;
2304 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002305 case ':':
2306 case '[':
2307 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002308 if (fInCharCommentString) {
2309 break;
2310 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002311 if (fInDefine && fInBrace) {
2312 break;
2313 }
Cary Clark8032b982017-07-28 11:04:54 -04002314 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2315 break;
2316 }
Cary Clark0d225392018-06-07 09:59:07 -04002317 if (fConstExpr) {
2318 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2319 fConstExpr = nullptr;
2320 }
Cary Clark8032b982017-07-28 11:04:54 -04002321 if (!fInBrace) {
2322 if (!this->checkForWord()) {
2323 return false;
2324 }
2325 if (':' == test && !fInFunction) {
2326 break;
2327 }
2328 if ('{' == test) {
2329 this->addPunctuation(Punctuation::kLeftBrace);
2330 } else if (':' == test) {
2331 this->addPunctuation(Punctuation::kColon);
2332 }
2333 }
2334 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2335 && Bracket::kColon == fInBrace->fBracket) {
2336 Definition* braceParent = fParent->fParent;
2337 braceParent->fChildren.pop_back();
2338 braceParent->fTokens.pop_back();
2339 fParent = braceParent;
2340 fInBrace = nullptr;
2341 }
2342 this->pushBracket(
2343 '(' == test ? Bracket::kParen :
2344 '[' == test ? Bracket::kSquare :
2345 '{' == test ? Bracket::kBrace :
2346 Bracket::kColon);
2347 if (!fInBrace
2348 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2349 && fInFunction) {
2350 fInBrace = fParent;
2351 }
2352 } break;
2353 case '<':
2354 if (fInCharCommentString || fInBrace) {
2355 break;
2356 }
2357 if (!this->checkForWord()) {
2358 return false;
2359 }
2360 if (fInEnum) {
2361 break;
2362 }
2363 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002364 // this angle bracket may be an operator or may be a bracket
2365 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002366 break;
2367 case ')':
2368 case ']':
2369 case '}': {
2370 if (fInCharCommentString) {
2371 break;
2372 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002373 if (fInDefine && fInBrace) {
2374 break;
2375 }
Cary Clark8032b982017-07-28 11:04:54 -04002376 if (!fInBrace) {
2377 if (!this->checkForWord()) {
2378 return false;
2379 }
2380 }
2381 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002382 Bracket match = ')' == test ? Bracket::kParen :
2383 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2384 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002385 this->popBracket();
2386 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002387 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002388 } else {
2389 fInFunction = '}' != test;
2390 }
Cary Clark73fa9722017-08-29 17:36:51 -04002391 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2392 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002393 } else if (Bracket::kAngle == this->topBracket()
2394 && match == this->grandParentBracket()) {
2395 this->popBracket();
2396 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002397 } else {
2398 return reportError<bool>("malformed close bracket");
2399 }
2400 if (popBraceParent) {
2401 Definition* braceParent = fInBrace->fParent;
2402 braceParent->fChildren.pop_back();
2403 braceParent->fTokens.pop_back();
2404 fInBrace = nullptr;
2405 }
2406 } break;
2407 case '>':
2408 if (fInCharCommentString || fInBrace) {
2409 break;
2410 }
2411 if (!this->checkForWord()) {
2412 return false;
2413 }
2414 if (fInEnum) {
2415 break;
2416 }
Cary Clarka560c472017-11-27 10:44:06 -05002417 if (Bracket::kPound == this->topBracket()) {
2418 break;
2419 }
Cary Clark8032b982017-07-28 11:04:54 -04002420 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04002421 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04002422 this->popBracket();
2423 } else {
2424 return reportError<bool>("malformed close angle bracket");
2425 }
2426 break;
2427 case '#': {
2428 if (fInCharCommentString || fInBrace) {
2429 break;
2430 }
2431 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2432 this->pushBracket(Bracket::kPound);
2433 break;
2434 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002435 case ' ':
2436 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2437 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2438 fInBrace = fParent;
2439 // delimiting brackets are space ... unescaped-linefeed
2440 }
Cary Clark8032b982017-07-28 11:04:54 -04002441 case '&':
2442 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002443 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05002444 case '-':
2445 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002446 if (fInCharCommentString || fInBrace) {
2447 break;
2448 }
2449 if (!this->checkForWord()) {
2450 return false;
2451 }
2452 break;
Cary Clark0d225392018-06-07 09:59:07 -04002453 case '=':
2454 if (fInCharCommentString || fInBrace) {
2455 break;
2456 }
2457 if (!this->checkForWord()) {
2458 return false;
2459 }
2460 {
2461 const Definition& lastToken = fParent->fTokens.back();
2462 if (lastToken.fType != Definition::Type::kWord) {
2463 break;
2464 }
2465 string name(lastToken.fContentStart, lastToken.length());
2466 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
2467 break;
2468 }
2469 // find token on start of line
2470 auto lineIter = fParent->fTokens.end();
2471 do {
Cary Clark80247e52018-07-11 16:18:41 -04002472 if (fParent->fTokens.begin() == lineIter) {
2473 break;
2474 }
Cary Clark0d225392018-06-07 09:59:07 -04002475 --lineIter;
2476 } while (lineIter->fContentStart > fLine);
Cary Clark80247e52018-07-11 16:18:41 -04002477 if (lineIter->fContentStart < fLine && fParent->fTokens.end() != lineIter) {
Cary Clark0d225392018-06-07 09:59:07 -04002478 ++lineIter;
2479 }
2480 Definition* lineStart = &*lineIter;
2481 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
2482 bool sawConst = false;
2483 bool sawStatic = false;
2484 bool sawTemplate = false;
2485 bool sawType = false;
2486 while (&lastToken != &*lineIter) {
2487 if (KeyWord::kTemplate == lineIter->fKeyWord) {
2488 if (sawConst || sawStatic || sawTemplate) {
2489 sawConst = false;
2490 break;
2491 }
2492 if (&lastToken == &*++lineIter) {
2493 break;
2494 }
2495 if (KeyWord::kTypename != lineIter->fKeyWord) {
2496 break;
2497 }
2498 if (&lastToken == &*++lineIter) {
2499 break;
2500 }
2501 if (Definition::Type::kWord != lineIter->fType) {
2502 break;
2503 }
2504 sawTemplate = true;
2505 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
2506 if (sawConst || sawStatic) {
2507 sawConst = false;
2508 break;
2509 }
2510 sawStatic = true;
2511 } else if (KeyWord::kConst == lineIter->fKeyWord
2512 || KeyWord::kConstExpr == lineIter->fKeyWord) {
2513 if (sawConst) {
2514 sawConst = false;
2515 break;
2516 }
2517 sawConst = true;
2518 } else {
2519 if (sawType) {
2520 sawType = false;
2521 break;
2522 }
2523 if (Definition::Type::kKeyWord == lineIter->fType
2524 && KeyProperty::kNumber
2525 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
2526 sawType = true;
2527 } else if (Definition::Type::kWord == lineIter->fType) {
2528 string typeName(lineIter->fContentStart, lineIter->length());
2529 if ("Sk" != name.substr(0, 2)) {
2530 sawType = true;
2531 }
2532 }
2533 }
2534 ++lineIter;
2535 }
2536 if (sawType && sawConst) {
2537 // if found, name first
2538 lineStart->fName = name;
2539 lineStart->fMarkType = MarkType::kConst;
2540 fParent->fChildren.emplace_back(lineStart);
2541 fConstExpr = lineStart;
2542 }
2543 }
2544 break;
Cary Clark8032b982017-07-28 11:04:54 -04002545 case ';':
2546 if (fInCharCommentString || fInBrace) {
2547 break;
2548 }
2549 if (!this->checkForWord()) {
2550 return false;
2551 }
Cary Clark0d225392018-06-07 09:59:07 -04002552 if (fConstExpr) {
2553 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2554 fConstExpr = nullptr;
2555 }
Cary Clark8032b982017-07-28 11:04:54 -04002556 if (Definition::Type::kKeyWord == fParent->fType
2557 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002558 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2559 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002560 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2561 this->popObject();
2562 }
Cary Clark8032b982017-07-28 11:04:54 -04002563 if (KeyWord::kEnum == fParent->fKeyWord) {
2564 fInEnum = false;
2565 }
2566 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002567 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2568 this->popObject();
2569 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002570 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002571 } else if (Definition::Type::kBracket == fParent->fType
2572 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2573 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2574 list<Definition>::iterator baseIter = fParent->fTokens.end();
2575 list<Definition>::iterator namedIter = fParent->fTokens.end();
2576 for (auto tokenIter = fParent->fTokens.end();
Cary Clark80247e52018-07-11 16:18:41 -04002577 fParent->fTokens.begin() != tokenIter; ) {
2578 --tokenIter;
Cary Clark8032b982017-07-28 11:04:54 -04002579 if (tokenIter->fLineCount == fLineCount) {
2580 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2581 if (namedIter != fParent->fTokens.end()) {
2582 return reportError<bool>("found two named member tokens");
2583 }
2584 namedIter = tokenIter;
2585 }
2586 baseIter = tokenIter;
2587 } else {
2588 break;
2589 }
2590 }
2591 // FIXME: if a member definition spans multiple lines, this won't work
2592 if (namedIter != fParent->fTokens.end()) {
2593 if (baseIter == namedIter) {
2594 return this->reportError<bool>("expected type before named token");
2595 }
2596 Definition* member = &*namedIter;
2597 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002598 if (!member->fTerminator) {
2599 member->fTerminator = member->fContentEnd;
2600 }
Cary Clark8032b982017-07-28 11:04:54 -04002601 fParent->fChildren.push_back(member);
2602 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2603 member->fChildren.push_back(&*nameType);
2604 }
Cary Clark8032b982017-07-28 11:04:54 -04002605 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002606 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002607 } else if (fParent->fChildren.size() > 0) {
2608 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002609 Definition* priorEnum = fPriorEnum;
2610 fPriorEnum = nullptr;
2611 if (!priorEnum) {
2612 while (fParent->fChildren.begin() != lastIter) {
2613 std::advance(lastIter, -1);
2614 priorEnum = *lastIter;
2615 if (Definition::Type::kBracket != priorEnum->fType ||
2616 (Bracket::kSlashSlash != priorEnum->fBracket
2617 && Bracket::kSlashStar != priorEnum->fBracket)) {
2618 break;
2619 }
Cary Clark8032b982017-07-28 11:04:54 -04002620 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002621 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002622 }
2623 if (Definition::Type::kKeyWord == priorEnum->fType
2624 && KeyWord::kEnum == priorEnum->fKeyWord) {
2625 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002626 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002627 while (tokenWalker != fParent->fTokens.end()) {
2628 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002629 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002630 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2631 break;
2632 }
2633 }
2634 while (tokenWalker != fParent->fTokens.end()) {
2635 std::advance(tokenWalker, 1);
2636 const Definition* test = &*tokenWalker;
2637 if (Definition::Type::kBracket != test->fType ||
2638 (Bracket::kSlashSlash != test->fBracket
2639 && Bracket::kSlashStar != test->fBracket)) {
2640 break;
2641 }
2642 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002643 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002644 Definition* start = &*tokenWalker;
2645 bool foundExpected = true;
2646 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2647 const Definition* test = &*tokenWalker;
2648 if (expected != test->fKeyWord) {
2649 foundExpected = false;
2650 break;
2651 }
2652 if (tokenWalker == fParent->fTokens.end()) {
2653 break;
2654 }
2655 std::advance(tokenWalker, 1);
2656 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002657 if (!foundExpected) {
2658 foundExpected = true;
2659 tokenWalker = saveTokenWalker;
2660 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2661 const Definition* test = &*tokenWalker;
2662 if (expected != test->fKeyWord) {
2663 foundExpected = false;
2664 break;
2665 }
2666 if (tokenWalker == fParent->fTokens.end()) {
2667 break;
2668 }
2669 if (KeyWord::kNone != expected) {
2670 std::advance(tokenWalker, 1);
2671 }
2672 }
2673 if (foundExpected) {
2674 auto nameToken = priorEnum->fTokens.begin();
2675 string enumName = string(nameToken->fContentStart,
2676 nameToken->fContentEnd - nameToken->fContentStart);
2677 const Definition* test = &*tokenWalker;
2678 string constType = string(test->fContentStart,
2679 test->fContentEnd - test->fContentStart);
2680 if (enumName != constType) {
2681 foundExpected = false;
2682 } else {
2683 std::advance(tokenWalker, 1);
2684 }
2685 }
2686 }
Cary Clark8032b982017-07-28 11:04:54 -04002687 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2688 const char* nameStart = tokenWalker->fStart;
2689 std::advance(tokenWalker, 1);
2690 if (tokenWalker != fParent->fTokens.end()) {
2691 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002692 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002693 start->fName = string(nameStart, tp.fChar - nameStart);
2694 start->fContentEnd = fChar;
2695 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002696 fPriorEnum = priorEnum;
2697 }
Cary Clark8032b982017-07-28 11:04:54 -04002698 }
2699 }
2700 }
2701 this->addPunctuation(Punctuation::kSemicolon);
2702 fInFunction = false;
2703 break;
2704 case '~':
2705 if (fInEnum) {
2706 break;
2707 }
2708 case '0': case '1': case '2': case '3': case '4':
2709 case '5': case '6': case '7': case '8': case '9':
2710 // TODO: don't want to parse numbers, but do need to track for enum defs
2711 // break;
2712 case 'A': case 'B': case 'C': case 'D': case 'E':
2713 case 'F': case 'G': case 'H': case 'I': case 'J':
2714 case 'K': case 'L': case 'M': case 'N': case 'O':
2715 case 'P': case 'Q': case 'R': case 'S': case 'T':
2716 case 'U': case 'V': case 'W': case 'X': case 'Y':
2717 case 'Z': case '_':
2718 case 'a': case 'b': case 'c': case 'd': case 'e':
2719 case 'f': case 'g': case 'h': case 'i': case 'j':
2720 case 'k': case 'l': case 'm': case 'n': case 'o':
2721 case 'p': case 'q': case 'r': case 's': case 't':
2722 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002723 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002724 if (fInCharCommentString || fInBrace) {
2725 break;
2726 }
2727 if (!fIncludeWord) {
2728 fIncludeWord = fChar;
2729 }
2730 break;
2731 }
2732done:
2733 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002734 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002735 return true;
2736}
2737
2738void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04002739 IncludeParser::ValidateKeyWords();
2740}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002741
Cary Clark186d08f2018-04-03 08:43:27 -04002742bool IncludeParser::references(const SkString& file) const {
2743 // if includes weren't passed one at a time, assume all references are valid
2744 if (fIncludeMap.empty()) {
2745 return true;
2746 }
2747 SkASSERT(file.endsWith(".bmh") );
2748 string root(file.c_str(), file.size() - 4);
2749 string kReference("_Reference");
2750 if (string::npos != root.find(kReference)) {
2751 root = root.substr(0, root.length() - kReference.length());
2752 }
2753 if (fIClassMap.end() != fIClassMap.find(root)) {
2754 return true;
2755 }
2756 if (fIStructMap.end() != fIStructMap.find(root)) {
2757 return true;
2758 }
Cary Clark224c7002018-06-27 11:00:21 -04002759 if (fIEnumMap.end() != fIEnumMap.find(root)) {
2760 return true;
2761 }
2762 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
2763 return true;
2764 }
Cary Clark186d08f2018-04-03 08:43:27 -04002765 return false;
2766}
2767
Cary Clark2dc84ad2018-01-26 12:56:22 -05002768void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2769 if (!sk_isdir(includes)) {
2770 IncludeParser::RemoveOneFile(docs, includes);
2771 } else {
2772 SkOSFile::Iter it(includes, ".h");
2773 for (SkString file; it.next(&file); ) {
2774 SkString p = SkOSPath::Join(includes, file.c_str());
2775 const char* hunk = p.c_str();
2776 if (!SkStrEndsWith(hunk, ".h")) {
2777 continue;
2778 }
2779 IncludeParser::RemoveOneFile(docs, hunk);
2780 }
2781 }
2782}
2783
2784void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2785 const char* lastForward = strrchr(includesFile, '/');
2786 const char* lastBackward = strrchr(includesFile, '\\');
2787 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2788 if (!last) {
2789 last = includesFile;
2790 } else {
2791 last += 1;
2792 }
2793 SkString baseName(last);
2794 SkASSERT(baseName.endsWith(".h"));
2795 baseName.remove(baseName.size() - 2, 2);
2796 baseName.append("_Reference.bmh");
2797 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2798 remove(fullName.c_str());
2799}
Cary Clark224c7002018-06-27 11:00:21 -04002800
2801Bracket IncludeParser::topBracket() const {
2802 Definition* parent = this->parentBracket(fParent);
2803 return parent ? parent->fBracket : Bracket::kNone;
2804}