blob: fdaf0ccfe2758546ce84e6c54c2959e81f1ec36d [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 Clarkce101242017-09-01 15:51:02 -0400281 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400282 if (!def && 0 == token.fName.find("operator")) {
283 string operatorName = className + "::";
284 TextParser oper("", token.fStart, token.fContentEnd, 0);
285 const char* start = oper.strnstr("operator", token.fContentEnd);
286 SkASSERT(start);
287 oper.skipTo(start);
288 oper.skipToEndBracket('(');
289 int parens = 0;
290 do {
291 if ('(' == oper.peek()) {
292 ++parens;
293 } else if (')' == oper.peek()) {
294 --parens;
295 }
296 } while (!oper.eof() && oper.next() && parens > 0);
297 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400298 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400299 }
300 }
301 if (!def) {
302 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
303 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
304 string constructorName = className + "::";
305 constructorName += string(token.fContentStart + skip,
306 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400307 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400308 }
309 if (!def && 0 == token.fName.find("SK_")) {
310 string incName = token.fName + "()";
311 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400312 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400313 if (def) {
314 if (def->fName == incName) {
315 def->fVisited = true;
316 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400317 def = root->find(className + "::toString",
318 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400319 if (def) {
320 def->fVisited = true;
321 } else {
322 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500323 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400324 }
325 }
326 break;
327 } else {
328 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500329 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400330 }
331 }
332 }
333 if (!def) {
334 bool allLower = true;
335 for (size_t index = 0; index < token.fName.length(); ++index) {
336 if (!islower(token.fName[index])) {
337 allLower = false;
338 break;
339 }
340 }
341 if (allLower) {
342 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400343 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400344 }
345 }
346 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400347 if (gAttrDeprecated == token.fName) {
348 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400349 break;
350 }
351 if (0 == token.fName.find("SkDEBUGCODE")) {
352 break;
353 }
354 }
355 if (!def) {
356 // simple method names inside nested classes have a bug and are missing trailing parens
357 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400358 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400359 }
360 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500361 if (!root->fDeprecated) {
362 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
363 fFailed = true;
364 }
Cary Clark8032b982017-07-28 11:04:54 -0400365 break;
366 }
Cary Clark73fa9722017-08-29 17:36:51 -0400367 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400368 def->fVisited = true;
Cary Clark89b14562018-03-19 09:04:10 -0400369 if (token.fDeprecated && !def->fDeprecated) {
370 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
371 }
Cary Clark8032b982017-07-28 11:04:54 -0400372 } else {
373 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500374 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400375 }
376 } break;
377 case MarkType::kComment:
378 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400379 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400380 case MarkType::kEnum: {
381 if (!def) {
382 // work backwards from first word to deduce #Enum name
383 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
384 SkAssertResult(firstMember.skipName("enum"));
385 SkAssertResult(firstMember.skipToEndBracket('{'));
386 firstMember.next();
387 firstMember.skipWhiteSpace();
388 SkASSERT('k' == firstMember.peek());
389 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400390 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400391 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400392 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400393 const char* lastUnderscore = nullptr;
394 do {
395 if (!firstMember.skipToEndBracket('_')) {
396 break;
397 }
398 if (firstMember.fChar > wordEnd) {
399 break;
400 }
401 lastUnderscore = firstMember.fChar;
402 } while (firstMember.next());
403 if (lastUnderscore) {
404 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400405 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400406 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400407 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400408 }
409 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500410 if (!root->fDeprecated) {
411 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
412 fFailed = true;
413 }
Cary Clark8032b982017-07-28 11:04:54 -0400414 break;
415 }
416 }
417 def->fVisited = true;
418 for (auto& child : def->fChildren) {
419 if (MarkType::kCode == child->fMarkType) {
420 def = child;
421 break;
422 }
423 }
424 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500425 if (!root->fDeprecated) {
426 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
427 fFailed = true;
428 }
Cary Clark8032b982017-07-28 11:04:54 -0400429 break;
430 }
431 if (def->crossCheck(token)) {
432 def->fVisited = true;
433 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500434 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
435 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400436 }
437 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400438 string constName = MarkType::kEnumClass == token.fMarkType ?
439 fullName : className;
440 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400441 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400442 if (!def) {
443 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400444 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400445 }
446 if (!def) {
447 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500448 if (!root->fDeprecated) {
449 SkDebugf("const missing from bmh: %s\n", constName.c_str());
450 fFailed = true;
451 }
Cary Clark8032b982017-07-28 11:04:54 -0400452 }
453 } else {
454 def->fVisited = true;
455 }
456 }
457 } break;
458 case MarkType::kMember:
459 if (def) {
460 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500461 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400462 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500463 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400464 }
465 break;
Cary Clark2f466242017-12-11 16:03:17 -0500466 case MarkType::kTypedef:
467 if (def) {
468 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500469 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500470 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500471 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500472 }
473 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400474 case MarkType::kConst:
475 if (def) {
476 def->fVisited = true;
477 } else if (!root->fDeprecated) {
478 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
479 fFailed = true;
480 }
481 break;
Cary Clark8032b982017-07-28 11:04:54 -0400482 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400483 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400484 break;
485 }
486 }
487 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500488 int crossChecks = 0;
489 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400490 for (auto& classMapper : fIClassMap) {
491 string className = classMapper.first;
492 auto finder = bmhParser.fClassMap.find(className);
493 if (bmhParser.fClassMap.end() == finder) {
494 continue;
495 }
496 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500497 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500498 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400499 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500500 if (crossChecks) {
501 SkDebugf(".");
502 } else {
503 SkDebugf("cross-check");
504 firstCheck = className;
505 }
506 ++crossChecks;
507 }
508 if (crossChecks) {
509 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500510 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500511 }
512 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400513 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400514 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500515 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400516}
517
518IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400519 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400520 string className;
521 const Definition* test = fParent;
522 while (Definition::Type::kFileType != test->fType) {
523 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
524 className = test->fName + "::";
525 break;
526 }
527 test = test->fParent;
528 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400529 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400530 unordered_map<string, IClassDefinition>& map = fIClassMap;
531 IClassDefinition& markupDef = map[className];
532 if (markupDef.fStart) {
533 typedef IClassDefinition* IClassDefPtr;
534 return INHERITED::reportError<IClassDefPtr>("class already defined");
535 }
536 markupDef.fFileName = fFileName;
537 markupDef.fStart = includeDef.fStart;
538 markupDef.fContentStart = includeDef.fStart;
539 markupDef.fName = className;
540 markupDef.fContentEnd = includeDef.fContentEnd;
541 markupDef.fTerminator = includeDef.fTerminator;
542 markupDef.fParent = fParent;
543 markupDef.fLineCount = fLineCount;
544 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
545 MarkType::kStruct : MarkType::kClass;
546 markupDef.fKeyWord = includeDef.fKeyWord;
547 markupDef.fType = Definition::Type::kMark;
548 fParent = &markupDef;
549 return &markupDef;
550}
551
552void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
553 auto& tokens = classDef.fTokens;
554 for (auto& token : tokens) {
555 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
556 continue;
557 }
558 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400559 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400560 }
561 switch (token.fMarkType) {
Cary Clark224c7002018-06-27 11:00:21 -0400562 case MarkType::kConst:
563 this->dumpConst(token, classDef.fName);
564 break;
Cary Clark8032b982017-07-28 11:04:54 -0400565 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500566 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500567 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400568 break;
569 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400570 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400571 break;
572 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400573 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400574 continue;
575 break;
576 default:
577 SkASSERT(0);
578 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400579 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400580 }
581}
Cary Clark9174bda2017-09-19 17:39:32 -0400582void IncludeParser::dumpComment(const Definition& token) {
583 fLineCount = token.fLineCount;
584 fChar = fLine = token.fContentStart;
585 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400586 bool sawParam = false;
587 bool multiline = false;
588 bool sawReturn = false;
589 bool sawComment = false;
590 bool methodHasReturn = false;
591 vector<string> methodParams;
592 vector<string> foundParams;
593 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400594 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
595 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500596 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400597 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500598 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400599 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400600 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500601 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400602 && !methodParser.strnchr('~', methodParser.fEnd);
603 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
604 const char* nextEnd = paren;
605 do {
606 string paramName;
607 methodParser.fChar = nextEnd + 1;
608 methodParser.skipSpace();
609 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
610 continue;
611 }
612 methodParams.push_back(paramName);
613 } while (')' != nextEnd[0]);
614 }
Cary Clark9174bda2017-09-19 17:39:32 -0400615 for (const auto& child : token.fTokens) {
616 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
617 break;
618 }
Cary Clark8032b982017-07-28 11:04:54 -0400619 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400620 if (child.fPrivate) {
621 break;
622 }
Cary Clark8032b982017-07-28 11:04:54 -0400623 if ('@' == child.fContentStart[0]) {
624 TextParser parser(&child);
625 do {
626 parser.next();
627 if (parser.startsWith("param ")) {
628 parser.skipWord("param");
629 const char* parmStart = parser.fChar;
630 parser.skipToSpace();
631 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
632 parser.skipWhiteSpace();
633 do {
634 size_t nextComma = parmName.find(',');
635 string piece;
636 if (string::npos == nextComma) {
637 piece = parmName;
638 parmName = "";
639 } else {
640 piece = parmName.substr(0, nextComma);
641 parmName = parmName.substr(nextComma + 1);
642 }
643 if (sawParam) {
644 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400645 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400646 }
Cary Clark9174bda2017-09-19 17:39:32 -0400647 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400648 } else {
649 if (sawComment) {
650 this->nl();
651 }
652 this->lf(2);
653 }
654 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400655 this->writeTag("Param", piece);
656 this->writeSpace(2);
657 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
658 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400659 sawParam = true;
660 sawComment = false;
661 } while (parmName.length());
662 parser.skipTo(parser.fEnd);
663 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
664 parser.skipWord("return");
665 if ('s' == parser.peek()) {
666 parser.next();
667 }
668 if (sawParam) {
669 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400670 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400671 }
Cary Clark9174bda2017-09-19 17:39:32 -0400672 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400673 }
674 this->checkForMissingParams(methodParams, foundParams);
675 sawParam = false;
676 sawComment = false;
677 multiline = false;
678 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400679 this->writeTag("Return");
680 this->writeSpace(2);
681 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
682 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400683 sawReturn = true;
684 parser.skipTo(parser.fEnd);
685 } else {
686 this->reportError("unexpected doxygen directive");
687 }
688 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400689 } else if (child.length() > 1) {
690 const char* start = child.fContentStart;
691 ptrdiff_t length = child.fContentEnd - start;
692 SkASSERT(length >= 0);
693 while (length && '/' == start[0]) {
694 start += 1;
695 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400696 }
Cary Clark9174bda2017-09-19 17:39:32 -0400697 while (length && '/' == start[length - 1]) {
698 length -= 1;
699 if (length && '*' == start[length - 1]) {
700 length -= 1;
701 }
702 }
703 if (length) {
704 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
705 if (sawParam || sawReturn) {
706 this->indentToColumn(8);
707 }
708 this->writeBlock(length, start);
709 this->writeSpace();
710 sawComment = true;
711 if (sawParam || sawReturn) {
712 multiline = true;
713 }
Cary Clark8032b982017-07-28 11:04:54 -0400714 }
715 }
716 }
717 }
718 if (sawParam || sawReturn) {
719 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400720 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400721 }
Cary Clark9174bda2017-09-19 17:39:32 -0400722 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400723 }
724 if (!sawReturn) {
725 if (!sawParam) {
726 if (sawComment) {
727 this->nl();
728 }
729 this->lf(2);
730 }
731 this->checkForMissingParams(methodParams, foundParams);
732 }
733 if (methodHasReturn != sawReturn) {
734 if (!methodHasReturn) {
735 this->reportError("unexpected doxygen return");
736 } else {
737 if (sawComment) {
738 this->nl();
739 }
740 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400741 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400742 }
743 }
744}
745
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400746void IncludeParser::dumpCommonTail(const Definition& token) {
747 this->lf(2);
748 this->writeTag("Example");
749 this->lf(1);
750 this->writeString("// incomplete");
751 this->lf(1);
752 this->writeEndTag();
753 this->lf(2);
754 this->writeTag("SeeAlso");
755 this->writeSpace();
756 this->writeString("incomplete");
757 this->lf(2);
758 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
759 this->lf(2);
760}
761
Cary Clark224c7002018-06-27 11:00:21 -0400762void IncludeParser::dumpConst(const Definition& token, string className) {
763 this->writeTag("Const");
764 this->writeSpace();
765 this->writeString(token.fName);
766 this->writeTagTable("Line", "incomplete");
767 this->lf(2);
768 this->dumpComment(token);
769}
770
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400771void IncludeParser::dumpDefine(const Definition& token) {
772 this->writeTag("Define", token.fName);
773 this->lf(2);
774 this->writeTag("Code");
775 this->lfAlways(1);
776 this->writeString("###$");
777 this->lfAlways(1);
778 this->indentToColumn(4);
779 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
780 this->lf(1);
781 this->indentToColumn(0);
782 this->writeString("$$$#");
783
784 this->writeEndTag();
785 this->lf(2);
786 this->dumpComment(token);
787 for (auto& child : token.fTokens) {
788 if (MarkType::kComment == child.fMarkType) {
789 continue;
790 }
791 this->writeTag("Param", child.fName);
792 this->writeSpace();
793 this->writeString("incomplete");
794 this->writeSpace();
795 this->writeString("##");
796 this->lf(1);
797 }
798}
799
800void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500801 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400802 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400803 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400804 this->lfAlways(1);
805 this->indentToColumn(4);
806 this->writeString("enum");
807 this->writeSpace();
808 if ("_anonymous" != token.fName.substr(0, 10)) {
809 this->writeString(token.fName);
810 this->writeSpace();
811 }
812 this->writeString("{");
813 this->lfAlways(1);
814 for (auto& child : token.fChildren) {
815 this->indentToColumn(8);
816 this->writeString(child->fName);
817 if (child->length()) {
818 this->writeSpace();
819 this->writeBlock(child->length(), child->fContentStart);
820 }
821 if (',' != fLastChar) {
822 this->writeString(",");
823 }
824 this->lfAlways(1);
825 }
826 this->indentToColumn(4);
827 this->writeString("};");
828 this->lf(1);
829 this->writeString("##");
830 this->lf(2);
831 this->dumpComment(token);
832 for (auto& child : token.fChildren) {
833 // start here;
834 // get comments before
835 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400836 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400837 this->writeSpace();
838 this->writeString(child->fName);
839 TextParser val(child);
840 if (!val.eof()) {
841 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
842 val.next();
843 val.skipSpace();
844 const char* valEnd = val.anyOf(",\n");
845 if (!valEnd) {
846 valEnd = val.fEnd;
847 }
848 this->writeSpace();
849 this->writeBlock(valEnd - val.fStart, val.fStart);
850 } else {
851 this->writeSpace();
852 this->writeDefinition(*child);
853 }
854 }
855 this->lf(1);
856 for (auto comment : child->fChildren) {
857 if (MarkType::kComment == comment->fMarkType) {
858 TextParser parser(comment);
859 parser.skipExact("*");
860 parser.skipExact("*");
861 while (!parser.eof() && parser.skipWhiteSpace()) {
862 parser.skipExact("*");
863 parser.skipWhiteSpace();
864 const char* start = parser.fChar;
865 parser.skipToEndBracket('\n');
866 this->lf(1);
867 this->writeBlock(parser.fChar - start, start);
868 }
869 }
870 }
871 this->writeEndTag();
872 }
873 this->lf(2);
874}
875
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400876bool IncludeParser::dumpGlobals() {
Cary Clark224c7002018-06-27 11:00:21 -0400877 if (fIDefineMap.empty() && fIFunctionMap.empty() && fIEnumMap.empty() && fITemplateMap.empty()
878 && fITypedefMap.empty() && fIUnionMap.empty()) {
879 return true;
880 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400881 size_t lastBSlash = fFileName.rfind('\\');
882 size_t lastSlash = fFileName.rfind('/');
883 size_t lastDotH = fFileName.rfind(".h");
884 SkASSERT(string::npos != lastDotH);
885 if (string::npos != lastBSlash && (string::npos == lastSlash
886 || lastBSlash < lastSlash)) {
887 lastSlash = lastBSlash;
888 } else if (string::npos == lastSlash) {
889 lastSlash = -1;
890 }
891 lastSlash += 1;
892 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
893 string fileName = globalsName + "_Reference.bmh";
894 fOut = fopen(fileName.c_str(), "wb");
895 if (!fOut) {
896 SkDebugf("could not open output file %s\n", globalsName.c_str());
897 return false;
898 }
899 string prefixName = globalsName.substr(0, 2);
900 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
901 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
902 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -0400903 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400904 this->lf(2);
905 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400906 this->writeTag("Populate");
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400907 this->writeEndTag();
908 this->lf(2);
909 if (!fIDefineMap.empty()) {
910 this->writeTag("Subtopic", "Define");
911 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400912 this->writeEndTag();
913 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400914 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400915 if (!fIFunctionMap.empty()) {
916 this->writeTag("Subtopic", "Function");
917 this->writeTag("Populate");
918 this->writeEndTag();
919 this->lf(2);
920 }
921 if (!fIEnumMap.empty()) {
922 this->writeTag("Subtopic", "Enum");
923 this->writeTag("Populate");
924 this->writeEndTag();
925 this->lf(2);
926 }
927 if (!fITemplateMap.empty()) {
928 this->writeTag("Subtopic", "Template");
929 this->writeTag("Populate");
930 this->writeEndTag();
931 this->lf(2);
932 }
933 if (!fITypedefMap.empty()) {
934 this->writeTag("Subtopic", "Typedef");
935 this->writeTag("Populate");
936 this->writeEndTag();
937 this->lf(2);
938 }
939 if (!fIUnionMap.empty()) {
940 this->writeTag("Subtopic", "Union");
941 this->writeTag("Populate");
942 this->writeEndTag();
943 this->lf(2);
944 }
945 std::map<int, Definition*> sortedDefs;
946 for (const auto& entry : fIDefineMap) {
947 sortedDefs[entry.second->fLineCount] = entry.second;
948 }
949 for (const auto& entry : fIFunctionMap) {
950 sortedDefs[entry.second->fLineCount] = entry.second;
951 }
952 for (const auto& entry : fIEnumMap) {
953 sortedDefs[entry.second->fLineCount] = entry.second;
954 }
955 for (const auto& entry : fITemplateMap) {
956 sortedDefs[entry.second->fLineCount] = entry.second;
957 }
958 for (const auto& entry : fITypedefMap) {
959 sortedDefs[entry.second->fLineCount] = entry.second;
960 }
961 for (const auto& entry : fIUnionMap) {
962 sortedDefs[entry.second->fLineCount] = entry.second;
963 }
964 for (const auto& entry : sortedDefs) {
965 const Definition* def = entry.second;
966 this->writeBlockSeparator();
967 switch (def->fMarkType) {
968 case MarkType::kDefine:
969 this->dumpDefine(*def);
970 break;
971 case MarkType::kMethod:
972 this->dumpMethod(*def, globalsName);
973 break;
974 case MarkType::kEnum:
975 case MarkType::kEnumClass:
976 this->dumpEnum(*def, globalsName);
977 break;
978 case MarkType::kTemplate:
979 SkASSERT(0); // incomplete
980 break;
981 case MarkType::kTypedef: {
982 this->writeTag("Typedef");
983 this->writeSpace();
984 TextParser parser(def);
985 if (!parser.skipExact("typedef")) {
986 return false;
987 }
988 if (!parser.skipSpace()) {
989 return false;
990 }
991 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
992 this->lf(2);
993 this->dumpComment(*def);
994 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
995 this->lf(2);
996 } continue;
997 case MarkType::kUnion:
998 SkASSERT(0); // incomplete
999 break;
1000 default:
1001 SkASSERT(0);
1002 }
1003 this->dumpCommonTail(*def);
1004 }
1005 this->writeEndTag("Topic", topicName);
1006 this->lfAlways(1);
1007 fclose(fOut);
1008 SkDebugf("wrote %s\n", fileName.c_str());
1009 return true;
1010}
1011
1012bool IncludeParser::isClone(const Definition& token) {
1013 string name = token.fName;
1014 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1015}
1016
1017bool IncludeParser::isConstructor(const Definition& token, string className) {
1018 string name = token.fName;
1019 return 0 == name.find(className) || '~' == name[0];
1020}
1021
1022bool IncludeParser::isInternalName(const Definition& token) {
1023 string name = token.fName;
1024 // exception for this SkCanvas function .. for now
1025 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1026 return false;
1027 }
1028 return name.substr(0, 7) == "android"
1029 || 0 == token.fName.find("internal_")
1030 || 0 == token.fName.find("Internal_")
1031 || 0 == token.fName.find("legacy_")
1032 || 0 == token.fName.find("temporary_")
1033 || 0 == token.fName.find("private_");
1034}
1035
1036bool IncludeParser::isOperator(const Definition& token) {
1037 return "operator" == token.fName.substr(0, 8);
1038}
1039
1040void IncludeParser::dumpMethod(const Definition& token, string className) {
1041 this->writeTag("Method");
1042 this->writeSpace();
1043
1044 string name = string(token.fStart ? token.fStart : token.fContentStart,
1045 token.length());
1046 if (this->isOperator(token)) {
1047 string spaceConst(" const");
1048 size_t constPos = name.rfind(spaceConst);
1049 if (name.length() - spaceConst.length() == constPos) {
1050 name = name.substr(0, constPos) + "_const";
1051 }
1052 }
Cary Clark224c7002018-06-27 11:00:21 -04001053 this->writeBlock((int) name.size(), name.c_str());
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001054 string inType;
1055 if (this->isConstructor(token, className)) {
1056 inType = "Constructor";
1057 } else if (this->isOperator(token)) {
1058 inType = "Operator";
1059 } else {
1060 inType = "incomplete";
1061 }
1062 this->writeTag("In", inType);
Cary Clark224c7002018-06-27 11:00:21 -04001063 this->writeTagTable("Line", "incomplete");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001064 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001065 this->dumpComment(token);
1066}
1067
1068void IncludeParser::dumpMember(const Definition& token) {
1069 this->writeTag("Member");
1070 this->writeSpace();
1071 this->writeDefinition(token, token.fName, 2);
1072 lf(1);
1073 for (auto child : token.fChildren) {
1074 this->writeDefinition(*child);
1075 }
1076 this->writeEndTag();
1077 lf(2);
1078}
1079
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001080bool IncludeParser::dumpTokens() {
1081 if (!this->dumpGlobals()) {
1082 return false;
1083 }
Cary Clark9174bda2017-09-19 17:39:32 -04001084 for (const auto& member : fIClassMap) {
1085 if (string::npos != member.first.find("::")) {
1086 continue;
1087 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001088 if (!this->dumpTokens(member.first)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001089 return false;
1090 }
1091 }
1092 return true;
1093}
1094
Ben Wagner63fd7602017-10-09 15:45:33 -04001095 // dump equivalent markup
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001096bool IncludeParser::dumpTokens(string skClassName) {
1097 string fileName = skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -04001098 fOut = fopen(fileName.c_str(), "wb");
1099 if (!fOut) {
1100 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -04001101 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001102 }
1103 string prefixName = skClassName.substr(0, 2);
1104 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1105 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -04001106 this->writeTagNoLF("Topic", topicName);
Cary Clark224c7002018-06-27 11:00:21 -04001107 this->writeEndTag("Alias", topicName + "_Reference");
Cary Clark9174bda2017-09-19 17:39:32 -04001108 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001109 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001110 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1111 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001112 this->writeTag(containerType, skClassName);
1113 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001114 auto& tokens = classMap.fTokens;
1115 for (auto& token : tokens) {
1116 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1117 continue;
1118 }
Cary Clark9174bda2017-09-19 17:39:32 -04001119 this->writeDefinition(token);
1120 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001121 }
1122 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001123 bool hasClass = false;
1124 bool hasConst = !fIEnumMap.empty();
1125 bool hasConstructor = false;
1126 bool hasMember = false;
1127 bool hasOperator = false;
Cary Clark224c7002018-06-27 11:00:21 -04001128 bool hasStruct = false;
Cary Clark8032b982017-07-28 11:04:54 -04001129 for (const auto& oneClass : fIClassMap) {
1130 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1131 continue;
1132 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001133 hasClass = true;
1134 break;
Cary Clark8032b982017-07-28 11:04:54 -04001135 }
Cary Clark224c7002018-06-27 11:00:21 -04001136 for (const auto& oneStruct : fIStructMap) {
1137 if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
1138 continue;
1139 }
1140 hasStruct = true;
1141 break;
1142 }
Cary Clark8032b982017-07-28 11:04:54 -04001143 for (const auto& token : classMap.fTokens) {
1144 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1145 continue;
1146 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001147 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001148 continue;
1149 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001150 if (this->isConstructor(token, skClassName)) {
1151 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001152 continue;
1153 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001154 if (this->isOperator(token)) {
1155 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001156 continue;
1157 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001158 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001159 continue;
1160 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001161 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001162 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001163 this->writeTag("Subtopic", "Overview");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001164 this->writeTag("Populate");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001165 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001166 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001167
1168 if (hasClass) {
Cary Clark224c7002018-06-27 11:00:21 -04001169 this->writeTag("Subtopic", "Class");
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001170 this->writeTag("Populate");
1171 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001172 this->lf(2);
1173 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001174 if (hasConst) {
1175 this->writeTag("Subtopic", "Constant");
1176 this->writeTag("Populate");
1177 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001178 this->lf(2);
1179 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001180 if (hasConstructor) {
1181 this->writeTag("Subtopic", "Constructor");
1182 this->writeTag("Populate");
1183 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001184 this->lf(2);
1185 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001186 if (hasOperator) {
1187 this->writeTag("Subtopic", "Operator");
1188 this->writeTag("Populate");
1189 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001190 this->lf(2);
1191 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001192 if (hasMember) {
1193 this->writeTag("Subtopic", "Member_Function");
1194 this->writeTag("Populate");
1195 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001196 this->lf(2);
1197 }
Cary Clark224c7002018-06-27 11:00:21 -04001198 if (hasStruct) {
1199 this->writeTag("Subtopic", "Struct");
1200 this->writeTag("Populate");
1201 this->writeEndTag();
1202 this->lf(2);
1203 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001204 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001205 this->writeBlockSeparator();
1206 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001207 this->lf(2);
1208 this->writeTag("Example");
1209 this->lfcr();
1210 this->writeString("// incomplete");
1211 this->writeEndTag();
1212 this->lf(2);
1213 this->writeTag("SeeAlso", "incomplete");
1214 this->lf(2);
1215 this->writeEndTag("Enum", oneEnum.first);
1216 this->lf(2);
1217 }
Cary Clark8032b982017-07-28 11:04:54 -04001218 for (auto& oneClass : fIClassMap) {
1219 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1220 continue;
1221 }
1222 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001223 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001224 KeyWord keyword = oneClass.second.fKeyWord;
1225 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1226 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001227 this->writeTag(containerType, innerName);
Cary Clark224c7002018-06-27 11:00:21 -04001228 this->writeTagTable("Line", "incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -04001229 this->lf(2);
1230 this->writeTag("Code");
1231 this->writeEndTag("ToDo", "fill this in manually");
1232 this->writeEndTag();
1233 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001234 for (auto& token : oneClass.second.fTokens) {
1235 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1236 continue;
1237 }
Cary Clark9174bda2017-09-19 17:39:32 -04001238 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001239 }
1240 this->lf(2);
1241 this->dumpClassTokens(oneClass.second);
1242 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001243 this->writeEndTag(containerType, innerName);
1244 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001245 }
1246 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001247 this->writeEndTag(containerType, skClassName);
1248 this->lf(2);
1249 this->writeEndTag("Topic", topicName);
1250 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001251 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001252 SkDebugf("wrote %s\n", fileName.c_str());
1253 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001254}
1255
1256bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1257 // add comment preceding class, if any
1258 const Definition* parent = includeDef.fParent;
1259 int index = includeDef.fParentIndex;
1260 auto wordIter = parent->fTokens.begin();
1261 std::advance(wordIter, index);
1262 SkASSERT(&*wordIter == &includeDef);
1263 while (parent->fTokens.begin() != wordIter) {
1264 auto testIter = std::prev(wordIter);
1265 if (Definition::Type::kWord != testIter->fType
1266 && Definition::Type::kKeyWord != testIter->fType
1267 && (Definition::Type::kBracket != testIter->fType
1268 || Bracket::kAngle != testIter->fBracket)
1269 && (Definition::Type::kPunctuation != testIter->fType
1270 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1271 break;
1272 }
1273 wordIter = testIter;
1274 }
1275 auto commentIter = wordIter;
1276 while (parent->fTokens.begin() != commentIter) {
1277 auto testIter = std::prev(commentIter);
1278 bool isComment = Definition::Type::kBracket == testIter->fType
1279 && (Bracket::kSlashSlash == testIter->fBracket
1280 || Bracket::kSlashStar == testIter->fBracket);
1281 if (!isComment) {
1282 break;
1283 }
1284 commentIter = testIter;
1285 }
1286 while (commentIter != wordIter) {
1287 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1288 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1289 return false;
1290 }
1291 commentIter = std::next(commentIter);
1292 }
1293 return true;
1294}
1295
Cary Clark0d225392018-06-07 09:59:07 -04001296Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1297 string typeName) {
1298 typedef Definition* DefinitionPtr;
1299 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1300 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1301 if (mapIter == fMaps.end()) {
1302 return nullptr;
1303 }
1304 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1305 return reportError<DefinitionPtr>("invalid mark type");
1306 }
1307 string name = this->uniqueName(*mapIter->fInclude, typeName);
1308 Definition& markupDef = *(*mapIter->fInclude)[name];
1309 if (markupDef.fStart) {
1310 return reportError<DefinitionPtr>("definition already defined");
1311 }
1312 markupDef.fFileName = fFileName;
1313 markupDef.fStart = includeDef.fStart;
1314 markupDef.fContentStart = includeDef.fStart;
1315 markupDef.fName = name;
1316 markupDef.fContentEnd = includeDef.fContentEnd;
1317 markupDef.fTerminator = includeDef.fTerminator;
1318 markupDef.fParent = fParent;
1319 markupDef.fLineCount = includeDef.fLineCount;
1320 markupDef.fMarkType = markType;
1321 markupDef.fKeyWord = includeDef.fKeyWord;
1322 markupDef.fType = Definition::Type::kMark;
1323 return &markupDef;
1324}
1325
Cary Clark224c7002018-06-27 11:00:21 -04001326Definition* IncludeParser::parentBracket(Definition* parent) const {
1327 while (parent && Definition::Type::kBracket != parent->fType) {
1328 parent = parent->fParent;
1329 }
1330 return parent;
1331}
1332
1333Bracket IncludeParser::grandParentBracket() const {
1334 Definition* parent = parentBracket(fParent);
1335 parent = parentBracket(parent ? parent->fParent : nullptr);
1336 return parent ? parent->fBracket : Bracket::kNone;
1337}
1338
Cary Clark137b8742018-05-30 09:21:49 -04001339// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001340bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1341 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001342 // parse class header
1343 auto iter = includeDef->fTokens.begin();
1344 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1345 // todo : documentation is ignoring this for now
1346 iter = std::next(iter);
1347 }
1348 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1349 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001350 iter = std::next(iter);
1351 if (iter == includeDef->fTokens.end()) {
1352 return true; // forward declaration only
1353 }
Cary Clark8032b982017-07-28 11:04:54 -04001354 do {
1355 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001356 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001357 }
1358 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1359 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001360 }
Cary Clark8032b982017-07-28 11:04:54 -04001361 } while (static_cast<void>(iter = std::next(iter)), true);
1362 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001363 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001364 }
1365 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1366 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001367 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001368 }
1369 markupDef->fStart = iter->fStart;
1370 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001371 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001372 }
1373// if (1 != includeDef->fChildren.size()) {
1374// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1375// }
1376 includeDef = includeDef->fChildren.front();
1377 iter = includeDef->fTokens.begin();
1378 // skip until public
1379 int publicIndex = 0;
1380 if (IsStruct::kNo == isStruct) {
1381 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1382 size_t publicLen = strlen(publicName);
1383 while (iter != includeDef->fTokens.end()
1384 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1385 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001386 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001387 iter = std::next(iter);
1388 ++publicIndex;
1389 }
1390 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001391 int keyIndex = publicIndex;
1392 KeyWord currentKey = KeyWord::kPublic;
1393 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1394 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001395 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1396 size_t protectedLen = strlen(protectedName);
1397 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1398 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001399 auto childIter = includeDef->fChildren.begin();
Cary Clarkb7a72a52018-06-15 05:11:24 -04001400 while ((*childIter)->fPrivate) {
1401 std::advance(childIter, 1);
1402 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001403 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001404 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001405 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001406 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001407 const char* testStart = iter->fStart;
1408 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1409 iter = std::next(iter);
1410 ++keyIndex;
1411 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1412 currentKey = KeyWord::kPublic;
1413 break;
1414 }
1415 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1416 currentKey = KeyWord::kProtected;
1417 break;
1418 }
1419 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1420 currentKey = KeyWord::kPrivate;
1421 break;
1422 }
1423 }
1424 fLastObject = nullptr;
1425 if (KeyWord::kPublic == currentKey) {
1426 if (!this->parseObject(child, markupDef)) {
1427 return false;
1428 }
Cary Clark8032b982017-07-28 11:04:54 -04001429 }
Cary Clark73fa9722017-08-29 17:36:51 -04001430 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001431 childIter = std::next(childIter);
1432 }
Cary Clark137b8742018-05-30 09:21:49 -04001433 while (iter != includeDef->fTokens.end()) {
1434 iter->fPrivate = KeyWord::kPublic != currentKey;
1435 iter = std::next(iter);
1436 }
Cary Clark8032b982017-07-28 11:04:54 -04001437 SkASSERT(fParent->fParent);
1438 fParent = fParent->fParent;
1439 return true;
1440}
1441
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001442bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001443 int lineCount, Definition* markupDef) {
1444 TextParser parser(filename, start, end, lineCount);
1445 // parse doxygen if present
1446 if (parser.startsWith("**")) {
1447 parser.next();
1448 parser.next();
1449 parser.skipWhiteSpace();
1450 if ('\\' == parser.peek()) {
1451 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001452 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1453 if (parser.skipExact("file")) {
1454 if (Definition::Type::kFileType != fParent->fType) {
1455 return reportError<bool>("expected parent is file");
1456 }
1457 string filename = markupDef->fileName();
1458 if (!parser.skipWord(filename.c_str())) {
1459 return reportError<bool>("missing object type");
1460 }
1461 } else if (parser.skipExact("fn")) {
1462 SkASSERT(0); // incomplete
1463 } else {
1464 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1465 return reportError<bool>("missing object type");
1466 }
1467 if (!parser.skipWord(markupDef->fName.c_str()) &&
1468 KeyWord::kEnum != markupDef->fKeyWord) {
1469 return reportError<bool>("missing object name");
1470 }
Cary Clark8032b982017-07-28 11:04:54 -04001471 }
Cary Clark8032b982017-07-28 11:04:54 -04001472 }
1473 }
1474 // remove leading '*' if present
1475 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1476 while (!parser.eof() && parser.skipWhiteSpace()) {
1477 while ('*' == parser.peek()) {
1478 parser.next();
1479 if (parser.eof()) {
1480 break;
1481 }
1482 parser.skipWhiteSpace();
1483 }
1484 if (parser.eof()) {
1485 break;
1486 }
1487 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001488 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001489 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001490 parser.skipToEndBracket('\n');
1491 }
1492 return true;
1493}
1494
Cary Clarkd98f78c2018-04-26 08:32:37 -04001495bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04001496 if (!markupDef) {
1497 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1498 child->fLineCount, fParent, '\0');
1499 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04001500 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04001501 globalMarkupChild->fName = globalUniqueName;
1502 if (!this->findComments(*child, globalMarkupChild)) {
1503 return false;
1504 }
1505 fIConstMap[globalUniqueName] = globalMarkupChild;
1506 return true;
1507 }
1508 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1509 child->fLineCount, markupDef, '\0');
1510 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04001511 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001512 markupChild->fTerminator = markupChild->fContentEnd;
1513 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04001514 classDef.fConsts[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001515 return true;
1516}
1517
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001518bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1519 TextParser parser(child);
1520 if (!parser.skipExact("#define")) {
1521 return false;
1522 }
1523 if (!parser.skipSpace()) {
1524 return false;
1525 }
1526 const char* nameStart = parser.fChar;
1527 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1528 if (parser.eof()) {
1529 return true; // do nothing if #define doesn't define anything
1530 }
1531 string nameStr(nameStart, parser.fChar - nameStart);
1532 struct Param {
1533 const char* fStart;
1534 const char* fEnd;
1535 };
1536 vector<Param> params;
1537 if ('(' == parser.peek()) {
1538 parser.next();
1539 if (!parser.skipSpace()) {
1540 return false;
1541 }
1542 do {
1543 const char* paramStart = parser.fChar;
1544 if (!parser.skipExact("...")) {
1545 parser.skipToNonAlphaNum();
1546 }
1547 if (parser.eof()) {
1548 return false;
1549 }
1550 params.push_back({paramStart, parser.fChar});
1551 if (!parser.skipSpace()) {
1552 return false;
1553 }
1554 if (')' == parser.peek()) {
1555 parser.next();
1556 break;
1557 }
1558 if (',' != parser.next()) {
1559 return false;
1560 }
1561 if (!parser.skipSpace()) {
1562 return false;
1563 }
1564 } while (true);
1565 }
1566 if (!parser.skipSpace()) {
1567 return false;
1568 }
1569 if (!markupDef) {
1570 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1571 child->fLineCount, fParent, '\0');
1572 Definition* globalMarkupChild = &fGlobals.back();
1573 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1574 globalMarkupChild->fName = globalUniqueName;
1575 globalMarkupChild->fTerminator = child->fContentEnd;
1576 if (!this->findComments(*child, globalMarkupChild)) {
1577 return false;
1578 }
1579 fIDefineMap[globalUniqueName] = globalMarkupChild;
1580 for (Param param : params) {
1581 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1582 child->fLineCount, globalMarkupChild, '\0');
1583 Definition* paramChild = &globalMarkupChild->fTokens.back();
1584 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1585 paramChild->fTerminator = param.fEnd;
1586 }
1587 return true;
1588 }
1589 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1590 child->fLineCount, markupDef, '\0');
1591 Definition* markupChild = &markupDef->fTokens.back();
1592 markupChild->fName = nameStr;
1593 markupChild->fTerminator = markupChild->fContentEnd;
1594 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1595 if (!this->findComments(*child, markupChild)) {
1596 return false;
1597 }
1598 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001599 return true;
1600}
1601
1602bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001603 TextParser parser(child);
1604 parser.skipToEndBracket('{');
1605 if (parser.eof()) {
1606 return true; // if enum is a forward declaration, do nothing
1607 }
1608 parser.next();
1609 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001610 if (child->fTokens.size() > 0) {
1611 auto token = child->fTokens.begin();
1612 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1613 token = token->fTokens.begin();
1614 }
1615 if (Definition::Type::kWord == token->fType) {
1616 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1617 }
1618 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001619 Definition* markupChild;
1620 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001621 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1622 child->fLineCount, fParent, '\0');
1623 markupChild = &fGlobals.back();
1624 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1625 markupChild->fName = globalUniqueName;
1626 markupChild->fTerminator = child->fContentEnd;
1627 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001628 } else {
1629 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001630 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001631 markupChild = &markupDef->fTokens.back();
1632 }
Cary Clark8032b982017-07-28 11:04:54 -04001633 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1634 markupChild->fKeyWord = KeyWord::kEnum;
1635 TextParser enumName(child);
1636 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001637 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001638 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001639 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001640 markupChild->fMarkType = MarkType::kEnumClass;
1641 }
Cary Clark8032b982017-07-28 11:04:54 -04001642 const char* nameStart = enumName.fChar;
1643 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001644 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001645 markupChild->fName = markupDef->fName + "::" +
1646 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001647 }
Cary Clark8032b982017-07-28 11:04:54 -04001648 if (!this->findComments(*child, markupChild)) {
1649 return false;
1650 }
Cary Clark8032b982017-07-28 11:04:54 -04001651 const char* dataEnd;
1652 do {
Cary Clark8032b982017-07-28 11:04:54 -04001653 parser.skipWhiteSpace();
1654 if ('}' == parser.peek()) {
1655 break;
1656 }
1657 Definition* comment = nullptr;
1658 // note that comment, if any, can be before or after (on the same line, though) as member
1659 if ('#' == parser.peek()) {
1660 // fixme: handle preprecessor, but just skip it for now
1661 parser.skipToLineStart();
1662 }
1663 while (parser.startsWith("/*") || parser.startsWith("//")) {
1664 parser.next();
1665 const char* start = parser.fChar;
1666 const char* end;
1667 if ('*' == parser.peek()) {
1668 end = parser.strnstr("*/", parser.fEnd);
1669 parser.fChar = end;
1670 parser.next();
1671 parser.next();
1672 } else {
1673 end = parser.trimmedLineEnd();
1674 parser.skipToLineStart();
1675 }
1676 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001677 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001678 comment = &markupChild->fTokens.back();
1679 comment->fTerminator = end;
1680 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1681 return false;
1682 }
1683 parser.skipWhiteSpace();
1684 }
1685 parser.skipWhiteSpace();
1686 const char* memberStart = parser.fChar;
1687 if ('}' == memberStart[0]) {
1688 break;
1689 }
Cary Clark9174bda2017-09-19 17:39:32 -04001690 // if there's comment on same the line as member def, output first as if it was before
1691
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001692 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001693 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001694 if (parser.eof() || !parser.skipWhiteSpace()) {
Cary Clark224c7002018-06-27 11:00:21 -04001695 return parser.reportError<bool>("enum member must end with comma 1");
Cary Clark9174bda2017-09-19 17:39:32 -04001696 }
Cary Clark8032b982017-07-28 11:04:54 -04001697 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001698 if ('=' == parser.peek()) {
1699 parser.skipToEndBracket(',');
1700 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001701 if (!parser.eof() && '#' == parser.peek()) {
1702 // fixme: handle preprecessor, but just skip it for now
1703 continue;
1704 }
Cary Clark9174bda2017-09-19 17:39:32 -04001705 if (parser.eof() || ',' != parser.peek()) {
Cary Clark224c7002018-06-27 11:00:21 -04001706 return parser.reportError<bool>("enum member must end with comma 2");
Cary Clark9174bda2017-09-19 17:39:32 -04001707 }
1708 dataEnd = parser.fChar;
1709 const char* start = parser.anyOf("/\n");
1710 SkASSERT(start);
1711 parser.skipTo(start);
1712 if ('/' == parser.next()) {
1713 char slashStar = parser.next();
1714 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001715 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001716 char doxCheck = parser.next();
1717 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1718 save.restore();
1719 }
1720 }
1721 parser.skipWhiteSpace();
1722 const char* commentStart = parser.fChar;
1723 if ('/' == slashStar) {
1724 parser.skipToEndBracket('\n');
1725 } else {
1726 parser.skipToEndBracket("*/");
1727 }
1728 SkASSERT(!parser.eof());
1729 const char* commentEnd = parser.fChar;
1730 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001731 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001732 comment = &markupChild->fTokens.back();
1733 comment->fTerminator = commentEnd;
1734 }
Cary Clark8032b982017-07-28 11:04:54 -04001735 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001736 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001737 Definition* member = &markupChild->fTokens.back();
1738 member->fName = memberName;
1739 if (comment) {
1740 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001741 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001742 }
1743 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001744 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001745 for (auto outsideMember : child->fChildren) {
1746 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001747 continue;
1748 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001749 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1750 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001751 continue;
1752 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001753 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1754 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001755 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001756 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001757 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001758 // FIXME: ? add comment as well ?
1759 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001760 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001761 if (markupDef) {
1762 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1763 SkASSERT(classDef.fStart);
1764 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1765 markupChild->fName = uniqueName;
1766 classDef.fEnums[uniqueName] = markupChild;
1767 }
Cary Clark8032b982017-07-28 11:04:54 -04001768 return true;
1769}
1770
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001771bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001772 fParent = &fIncludeMap[name];
1773 fParent->fName = name;
1774 fParent->fFileName = fFileName;
1775 fParent->fType = Definition::Type::kFileType;
1776 fParent->fContentStart = fChar;
1777 fParent->fContentEnd = fEnd;
1778 // parse include file into tree
1779 while (fChar < fEnd) {
1780 if (!this->parseChar()) {
1781 return false;
1782 }
1783 }
1784 // parse tree and add named objects to maps
1785 fParent = &fIncludeMap[name];
1786 if (!this->parseObjects(fParent, nullptr)) {
1787 return false;
1788 }
1789 return true;
1790}
1791
1792bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1793 const char* typeStart = child->fChildren[0]->fContentStart;
1794 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001795 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001796 Definition* markupChild = &markupDef->fTokens.back();
1797 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001798 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001799 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1800 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1801 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1802 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001803 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001804 classDef.fMembers[uniqueName] = markupChild;
1805 if (child->fParentIndex >= 2) {
1806 auto comment = child->fParent->fTokens.begin();
1807 std::advance(comment, child->fParentIndex - 2);
1808 if (Definition::Type::kBracket == comment->fType
1809 && (Bracket::kSlashStar == comment->fBracket
1810 || Bracket::kSlashSlash == comment->fBracket)) {
1811 TextParser parser(&*comment);
1812 do {
1813 parser.skipToAlpha();
1814 if (parser.eof()) {
1815 break;
1816 }
1817 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001818 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001819 if (Bracket::kSlashStar == comment->fBracket) {
1820 const char* commentEnd = parser.strnstr("*/", end);
1821 if (commentEnd) {
1822 end = commentEnd;
1823 }
1824 }
1825 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001826 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001827 Definition* commentChild = &markupDef->fTokens.back();
1828 markupChild->fChildren.emplace_back(commentChild);
1829 parser.skipTo(end);
1830 } while (!parser.eof());
1831 }
1832 }
1833 return true;
1834}
1835
1836bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1837 auto tokenIter = child->fParent->fTokens.begin();
1838 std::advance(tokenIter, child->fParentIndex);
1839 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001840 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001841 bool addConst = false;
1842 auto operatorCheck = tokenIter;
1843 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1844 operatorCheck = std::prev(tokenIter);
1845 }
1846 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001847 auto closeParen = std::next(tokenIter);
1848 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1849 '(' == closeParen->fContentStart[0]);
1850 nameEnd = closeParen->fContentEnd + 1;
1851 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001852 if (Definition::Type::kKeyWord == closeParen->fType &&
1853 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001854 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001855 }
Cary Clarka560c472017-11-27 10:44:06 -05001856 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001857 }
1858 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001859 if (addConst) {
1860 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001861 }
Cary Clark8032b982017-07-28 11:04:54 -04001862 while (tokenIter != child->fParent->fTokens.begin()) {
1863 auto testIter = std::prev(tokenIter);
1864 switch (testIter->fType) {
1865 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001866 if (testIter == child->fParent->fTokens.begin() &&
1867 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1868 KeyWord::kIfndef == child->fParent->fKeyWord ||
1869 KeyWord::kIf == child->fParent->fKeyWord)) {
1870 std::next(tokenIter);
1871 break;
1872 }
Cary Clark8032b982017-07-28 11:04:54 -04001873 goto keepGoing;
1874 case Definition::Type::kKeyWord: {
1875 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1876 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1877 goto keepGoing;
1878 }
1879 } break;
1880 case Definition::Type::kBracket:
1881 if (Bracket::kAngle == testIter->fBracket) {
1882 goto keepGoing;
1883 }
1884 break;
1885 case Definition::Type::kPunctuation:
1886 if (Punctuation::kSemicolon == testIter->fPunctuation
1887 || Punctuation::kLeftBrace == testIter->fPunctuation
1888 || Punctuation::kColon == testIter->fPunctuation) {
1889 break;
1890 }
1891 keepGoing:
1892 tokenIter = testIter;
1893 continue;
1894 default:
1895 break;
1896 }
1897 break;
1898 }
Cary Clark224c7002018-06-27 11:00:21 -04001899 tokenIter->fName = nameStr; // simple token stream, OK if name is duplicate
Cary Clark8032b982017-07-28 11:04:54 -04001900 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001901 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001902 auto testIter = child->fParent->fTokens.begin();
1903 SkASSERT(child->fParentIndex > 0);
1904 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001905 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1906 0 == tokenIter->fParentIndex) {
1907 tokenIter = std::next(tokenIter);
1908 }
Cary Clark8032b982017-07-28 11:04:54 -04001909 const char* start = tokenIter->fContentStart;
1910 const char* end = tokenIter->fContentEnd;
1911 const char kDebugCodeStr[] = "SkDEBUGCODE";
1912 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1913 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1914 std::advance(testIter, 1);
1915 start = testIter->fContentStart + 1;
1916 end = testIter->fContentEnd - 1;
1917 } else {
1918 end = testIter->fContentEnd;
1919 while (testIter != child->fParent->fTokens.end()) {
1920 testIter = std::next(testIter);
1921 switch (testIter->fType) {
1922 case Definition::Type::kPunctuation:
1923 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1924 || Punctuation::kLeftBrace == testIter->fPunctuation
1925 || Punctuation::kColon == testIter->fPunctuation);
1926 end = testIter->fStart;
1927 break;
1928 case Definition::Type::kKeyWord: {
1929 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1930 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1931 continue;
1932 }
1933 } break;
1934 default:
1935 continue;
1936 }
1937 break;
1938 }
1939 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001940 while (end > start && ' ' >= end[-1]) {
1941 --end;
1942 }
Cary Clark9174bda2017-09-19 17:39:32 -04001943 if (!markupDef) {
1944 auto parentIter = child->fParent->fTokens.begin();
1945 SkASSERT(child->fParentIndex > 0);
1946 std::advance(parentIter, child->fParentIndex - 1);
1947 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001948 TextParser nameParser(methodName);
1949 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001950 return true; // expect this is inline class definition outside of class
1951 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001952 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1953 fParent, '\0');
1954 Definition* globalMarkupChild = &fGlobals.back();
1955 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1956 globalMarkupChild->fName = globalUniqueName;
1957 if (!this->findComments(*child, globalMarkupChild)) {
1958 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001959 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001960 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001961 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001962 }
Cary Clark8032b982017-07-28 11:04:54 -04001963 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001964 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001965 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark224c7002018-06-27 11:00:21 -04001966 // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
1967 {
1968 auto mapIter = fIClassMap.find(markupDef->fName);
1969 SkASSERT(fIClassMap.end() != mapIter);
1970 IClassDefinition& classDef = mapIter->second;
1971 SkASSERT(classDef.fStart);
1972 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1973 markupChild->fName = uniqueName;
1974 if (!this->findComments(*child, markupChild)) {
1975 return false;
1976 }
1977 classDef.fMethods[uniqueName] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001978 }
Cary Clark8032b982017-07-28 11:04:54 -04001979 return true;
1980}
1981
Cary Clark8032b982017-07-28 11:04:54 -04001982bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04001983 fPriorObject = nullptr;
1984 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04001985 if (!this->parseObject(child, markupDef)) {
1986 return false;
1987 }
Cary Clark0d225392018-06-07 09:59:07 -04001988 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001989 }
1990 return true;
1991}
1992
1993bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1994 // set up for error reporting
1995 fLine = fChar = child->fStart;
1996 fEnd = child->fContentEnd;
1997 // todo: put original line number in child as well
1998 switch (child->fType) {
1999 case Definition::Type::kKeyWord:
2000 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04002001 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04002002 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002003 return false;
Cary Clark8032b982017-07-28 11:04:54 -04002004 }
2005 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04002006 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04002007 case KeyWord::kConst:
2008 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04002009 if (!this->parseConst(child, markupDef)) {
2010 return child->reportError<bool>("failed to parse const or constexpr");
2011 }
2012 break;
Cary Clark8032b982017-07-28 11:04:54 -04002013 case KeyWord::kEnum:
2014 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002015 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04002016 }
2017 break;
2018 case KeyWord::kStruct:
2019 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002020 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04002021 }
2022 break;
2023 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05002024 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002025 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04002026 }
2027 break;
2028 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05002029 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002030 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04002031 }
2032 break;
2033 case KeyWord::kUnion:
2034 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002035 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002036 }
2037 break;
2038 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002039 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002040 }
2041 break;
2042 case Definition::Type::kBracket:
2043 switch (child->fBracket) {
2044 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04002045 if (fLastObject) {
2046 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
2047 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04002048 if (!checkDeprecated.eof()) {
2049 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002050 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2051 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002052 break;
2053 }
2054 }
2055 }
2056 {
2057 auto tokenIter = child->fParent->fTokens.begin();
2058 std::advance(tokenIter, child->fParentIndex);
2059 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002060 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002061 if (previousToken.startsWith(gAttrDeprecated)) {
2062 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002063 break;
2064 }
Cary Clarka7401902018-06-14 15:34:14 -04002065 if ('f' == previousToken.fStart[0] && isupper(previousToken.fStart[1])) {
2066 break;
2067 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002068 if (Bracket::kPound == child->fParent->fBracket &&
2069 KeyWord::kIf == child->fParent->fKeyWord) {
2070 // TODO: this will skip methods named defined() -- for the
2071 // moment there aren't any
2072 if (previousToken.startsWith("defined")) {
2073 break;
2074 }
2075 }
Cary Clark73fa9722017-08-29 17:36:51 -04002076 }
Cary Clark0d225392018-06-07 09:59:07 -04002077 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2078 break;
2079 }
Cary Clark8032b982017-07-28 11:04:54 -04002080 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002081 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002082 }
Cary Clark89b14562018-03-19 09:04:10 -04002083 if (fAttrDeprecated) {
2084 Definition* lastMethod = &markupDef->fTokens.back();
2085 lastMethod->fDeprecated = true;
2086 fAttrDeprecated = nullptr;
2087 }
Cary Clark73fa9722017-08-29 17:36:51 -04002088 break;
Cary Clark8032b982017-07-28 11:04:54 -04002089 case Bracket::kSlashSlash:
2090 case Bracket::kSlashStar:
2091 // comments are picked up by parsing objects first
2092 break;
2093 case Bracket::kPound:
2094 // special-case the #xxx xxx_DEFINED entries
2095 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002096 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002097 case KeyWord::kIfndef:
2098 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002099 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002100 if (!this->parseObjects(child, markupDef)) {
2101 return false;
2102 }
2103 break;
2104 }
2105 goto preproError;
2106 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002107 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002108 break;
2109 }
2110 goto preproError;
2111 case KeyWord::kEndif:
2112 if (child->boilerplateEndIf()) {
2113 break;
2114 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002115 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002116 case KeyWord::kInclude:
2117 // ignored for now
2118 break;
2119 case KeyWord::kElse:
2120 case KeyWord::kElif:
2121 // todo: handle these
2122 break;
2123 default:
2124 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002125 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002126 }
2127 break;
2128 case Bracket::kAngle:
2129 // pick up templated function pieces when method is found
2130 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002131 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002132 if (!this->parseObjects(child, markupDef)) {
2133 return false;
2134 }
Cary Clark73fa9722017-08-29 17:36:51 -04002135 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002136 case Bracket::kSquare: {
2137 // check to see if parent is operator, the only case we handle so far
2138 auto prev = child->fParent->fTokens.begin();
2139 std::advance(prev, child->fParentIndex - 1);
2140 if (KeyWord::kOperator != prev->fKeyWord) {
2141 return child->reportError<bool>("expected operator overload");
2142 }
2143 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002144 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002145 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002146 }
2147 break;
2148 case Definition::Type::kWord:
2149 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002150 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002151 }
2152 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002153 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002154 }
2155 break;
2156 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002157 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002158 break;
2159 }
2160 return true;
2161}
2162
Cary Clarkbbfda252018-03-09 15:32:01 -05002163bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2164 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002165}
2166
Cary Clark2f466242017-12-11 16:03:17 -05002167bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2168 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002169 typedefParser.skipExact("typedef");
2170 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002171 string nameStr = typedefParser.typedefName();
2172 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002173 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2174 child->fLineCount, fParent, '\0');
2175 Definition* globalMarkupChild = &fGlobals.back();
2176 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2177 globalMarkupChild->fName = globalUniqueName;
2178 if (!this->findComments(*child, globalMarkupChild)) {
2179 return false;
2180 }
2181 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002182 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002183 return true;
2184 }
2185 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002186 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002187 Definition* markupChild = &markupDef->fTokens.back();
2188 markupChild->fName = nameStr;
2189 markupChild->fTerminator = markupChild->fContentEnd;
2190 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2191 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002192 child->fName = markupDef->fName + "::" + nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002193 return true;
2194}
2195
2196bool IncludeParser::parseUnion() {
2197
2198 return true;
2199}
2200
2201bool IncludeParser::parseChar() {
2202 char test = *fChar;
2203 if ('\\' == fPrev) {
2204 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002205// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002206 fLine = fChar + 1;
2207 }
2208 goto done;
2209 }
2210 switch (test) {
2211 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002212// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002213 fLine = fChar + 1;
2214 if (fInChar) {
2215 return reportError<bool>("malformed char");
2216 }
2217 if (fInString) {
2218 return reportError<bool>("malformed string");
2219 }
2220 if (!this->checkForWord()) {
2221 return false;
2222 }
2223 if (Bracket::kPound == this->topBracket()) {
2224 KeyWord keyWord = fParent->fKeyWord;
2225 if (KeyWord::kNone == keyWord) {
2226 return this->reportError<bool>("unhandled preprocessor directive");
2227 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002228 if (fInDefine) {
2229 SkASSERT(KeyWord::kDefine == keyWord);
2230 fInDefine = false;
2231 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002232 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002233 this->popBracket();
2234 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002235 if (fInBrace) {
2236 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2237 fInBrace = nullptr;
2238 }
Cary Clark8032b982017-07-28 11:04:54 -04002239 } else if (Bracket::kSlashSlash == this->topBracket()) {
2240 this->popBracket();
2241 }
2242 break;
2243 case '*':
2244 if (!fInCharCommentString && '/' == fPrev) {
2245 this->pushBracket(Bracket::kSlashStar);
2246 }
2247 if (!this->checkForWord()) {
2248 return false;
2249 }
2250 if (!fInCharCommentString) {
2251 this->addPunctuation(Punctuation::kAsterisk);
2252 }
2253 break;
2254 case '/':
2255 if ('*' == fPrev) {
2256 if (!fInCharCommentString) {
2257 return reportError<bool>("malformed closing comment");
2258 }
2259 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002260 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002261 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002262 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002263 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002264 }
2265 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002266 }
Cary Clark8032b982017-07-28 11:04:54 -04002267 if (!fInCharCommentString && '/' == fPrev) {
2268 this->pushBracket(Bracket::kSlashSlash);
2269 break;
2270 }
2271 if (!this->checkForWord()) {
2272 return false;
2273 }
2274 break;
2275 case '\'':
2276 if (Bracket::kChar == this->topBracket()) {
2277 this->popBracket();
2278 } else if (!fInComment && !fInString) {
2279 if (fIncludeWord) {
2280 return this->reportError<bool>("word then single-quote");
2281 }
2282 this->pushBracket(Bracket::kChar);
2283 }
2284 break;
2285 case '\"':
2286 if (Bracket::kString == this->topBracket()) {
2287 this->popBracket();
2288 } else if (!fInComment && !fInChar) {
2289 if (fIncludeWord) {
2290 return this->reportError<bool>("word then double-quote");
2291 }
2292 this->pushBracket(Bracket::kString);
2293 }
2294 break;
Cary Clark8032b982017-07-28 11:04:54 -04002295 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002296 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002297 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2298 this->pushBracket(Bracket::kDebugCode);
2299 break;
2300 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002301 case ':':
2302 case '[':
2303 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002304 if (fInCharCommentString) {
2305 break;
2306 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002307 if (fInDefine && fInBrace) {
2308 break;
2309 }
Cary Clark8032b982017-07-28 11:04:54 -04002310 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2311 break;
2312 }
Cary Clark0d225392018-06-07 09:59:07 -04002313 if (fConstExpr) {
2314 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2315 fConstExpr = nullptr;
2316 }
Cary Clark8032b982017-07-28 11:04:54 -04002317 if (!fInBrace) {
2318 if (!this->checkForWord()) {
2319 return false;
2320 }
2321 if (':' == test && !fInFunction) {
2322 break;
2323 }
2324 if ('{' == test) {
2325 this->addPunctuation(Punctuation::kLeftBrace);
2326 } else if (':' == test) {
2327 this->addPunctuation(Punctuation::kColon);
2328 }
2329 }
2330 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2331 && Bracket::kColon == fInBrace->fBracket) {
2332 Definition* braceParent = fParent->fParent;
2333 braceParent->fChildren.pop_back();
2334 braceParent->fTokens.pop_back();
2335 fParent = braceParent;
2336 fInBrace = nullptr;
2337 }
2338 this->pushBracket(
2339 '(' == test ? Bracket::kParen :
2340 '[' == test ? Bracket::kSquare :
2341 '{' == test ? Bracket::kBrace :
2342 Bracket::kColon);
2343 if (!fInBrace
2344 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2345 && fInFunction) {
2346 fInBrace = fParent;
2347 }
2348 } break;
2349 case '<':
2350 if (fInCharCommentString || fInBrace) {
2351 break;
2352 }
2353 if (!this->checkForWord()) {
2354 return false;
2355 }
2356 if (fInEnum) {
2357 break;
2358 }
2359 this->pushBracket(Bracket::kAngle);
Cary Clark224c7002018-06-27 11:00:21 -04002360 // this angle bracket may be an operator or may be a bracket
2361 // wait for balancing close angle, if any, to decide
Cary Clark8032b982017-07-28 11:04:54 -04002362 break;
2363 case ')':
2364 case ']':
2365 case '}': {
2366 if (fInCharCommentString) {
2367 break;
2368 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002369 if (fInDefine && fInBrace) {
2370 break;
2371 }
Cary Clark8032b982017-07-28 11:04:54 -04002372 if (!fInBrace) {
2373 if (!this->checkForWord()) {
2374 return false;
2375 }
2376 }
2377 bool popBraceParent = fInBrace == fParent;
Cary Clark224c7002018-06-27 11:00:21 -04002378 Bracket match = ')' == test ? Bracket::kParen :
2379 ']' == test ? Bracket::kSquare : Bracket::kBrace;
2380 if (match == this->topBracket()) {
Cary Clark8032b982017-07-28 11:04:54 -04002381 this->popBracket();
2382 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002383 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002384 } else {
2385 fInFunction = '}' != test;
2386 }
Cary Clark73fa9722017-08-29 17:36:51 -04002387 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2388 this->popBracket();
Cary Clark224c7002018-06-27 11:00:21 -04002389 } else if (Bracket::kAngle == this->topBracket()
2390 && match == this->grandParentBracket()) {
2391 this->popBracket();
2392 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002393 } else {
2394 return reportError<bool>("malformed close bracket");
2395 }
2396 if (popBraceParent) {
2397 Definition* braceParent = fInBrace->fParent;
2398 braceParent->fChildren.pop_back();
2399 braceParent->fTokens.pop_back();
2400 fInBrace = nullptr;
2401 }
2402 } break;
2403 case '>':
2404 if (fInCharCommentString || fInBrace) {
2405 break;
2406 }
2407 if (!this->checkForWord()) {
2408 return false;
2409 }
2410 if (fInEnum) {
2411 break;
2412 }
Cary Clarka560c472017-11-27 10:44:06 -05002413 if (Bracket::kPound == this->topBracket()) {
2414 break;
2415 }
Cary Clark8032b982017-07-28 11:04:54 -04002416 if (Bracket::kAngle == this->topBracket()) {
Cary Clark224c7002018-06-27 11:00:21 -04002417 // looks like angle pair are braces, not operators
Cary Clark8032b982017-07-28 11:04:54 -04002418 this->popBracket();
2419 } else {
2420 return reportError<bool>("malformed close angle bracket");
2421 }
2422 break;
2423 case '#': {
2424 if (fInCharCommentString || fInBrace) {
2425 break;
2426 }
2427 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2428 this->pushBracket(Bracket::kPound);
2429 break;
2430 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002431 case ' ':
2432 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2433 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2434 fInBrace = fParent;
2435 // delimiting brackets are space ... unescaped-linefeed
2436 }
Cary Clark8032b982017-07-28 11:04:54 -04002437 case '&':
2438 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002439 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05002440 case '-':
2441 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002442 if (fInCharCommentString || fInBrace) {
2443 break;
2444 }
2445 if (!this->checkForWord()) {
2446 return false;
2447 }
2448 break;
Cary Clark0d225392018-06-07 09:59:07 -04002449 case '=':
2450 if (fInCharCommentString || fInBrace) {
2451 break;
2452 }
2453 if (!this->checkForWord()) {
2454 return false;
2455 }
2456 {
2457 const Definition& lastToken = fParent->fTokens.back();
2458 if (lastToken.fType != Definition::Type::kWord) {
2459 break;
2460 }
2461 string name(lastToken.fContentStart, lastToken.length());
2462 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
2463 break;
2464 }
2465 // find token on start of line
2466 auto lineIter = fParent->fTokens.end();
2467 do {
2468 --lineIter;
2469 } while (lineIter->fContentStart > fLine);
2470 if (lineIter->fContentStart < fLine) {
2471 ++lineIter;
2472 }
2473 Definition* lineStart = &*lineIter;
2474 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
2475 bool sawConst = false;
2476 bool sawStatic = false;
2477 bool sawTemplate = false;
2478 bool sawType = false;
2479 while (&lastToken != &*lineIter) {
2480 if (KeyWord::kTemplate == lineIter->fKeyWord) {
2481 if (sawConst || sawStatic || sawTemplate) {
2482 sawConst = false;
2483 break;
2484 }
2485 if (&lastToken == &*++lineIter) {
2486 break;
2487 }
2488 if (KeyWord::kTypename != lineIter->fKeyWord) {
2489 break;
2490 }
2491 if (&lastToken == &*++lineIter) {
2492 break;
2493 }
2494 if (Definition::Type::kWord != lineIter->fType) {
2495 break;
2496 }
2497 sawTemplate = true;
2498 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
2499 if (sawConst || sawStatic) {
2500 sawConst = false;
2501 break;
2502 }
2503 sawStatic = true;
2504 } else if (KeyWord::kConst == lineIter->fKeyWord
2505 || KeyWord::kConstExpr == lineIter->fKeyWord) {
2506 if (sawConst) {
2507 sawConst = false;
2508 break;
2509 }
2510 sawConst = true;
2511 } else {
2512 if (sawType) {
2513 sawType = false;
2514 break;
2515 }
2516 if (Definition::Type::kKeyWord == lineIter->fType
2517 && KeyProperty::kNumber
2518 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
2519 sawType = true;
2520 } else if (Definition::Type::kWord == lineIter->fType) {
2521 string typeName(lineIter->fContentStart, lineIter->length());
2522 if ("Sk" != name.substr(0, 2)) {
2523 sawType = true;
2524 }
2525 }
2526 }
2527 ++lineIter;
2528 }
2529 if (sawType && sawConst) {
2530 // if found, name first
2531 lineStart->fName = name;
2532 lineStart->fMarkType = MarkType::kConst;
2533 fParent->fChildren.emplace_back(lineStart);
2534 fConstExpr = lineStart;
2535 }
2536 }
2537 break;
Cary Clark8032b982017-07-28 11:04:54 -04002538 case ';':
2539 if (fInCharCommentString || fInBrace) {
2540 break;
2541 }
2542 if (!this->checkForWord()) {
2543 return false;
2544 }
Cary Clark0d225392018-06-07 09:59:07 -04002545 if (fConstExpr) {
2546 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2547 fConstExpr = nullptr;
2548 }
Cary Clark8032b982017-07-28 11:04:54 -04002549 if (Definition::Type::kKeyWord == fParent->fType
2550 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002551 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2552 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002553 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2554 this->popObject();
2555 }
Cary Clark8032b982017-07-28 11:04:54 -04002556 if (KeyWord::kEnum == fParent->fKeyWord) {
2557 fInEnum = false;
2558 }
2559 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002560 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2561 this->popObject();
2562 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002563 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002564 } else if (Definition::Type::kBracket == fParent->fType
2565 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2566 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2567 list<Definition>::iterator baseIter = fParent->fTokens.end();
2568 list<Definition>::iterator namedIter = fParent->fTokens.end();
2569 for (auto tokenIter = fParent->fTokens.end();
2570 fParent->fTokens.begin() != tokenIter--; ) {
2571 if (tokenIter->fLineCount == fLineCount) {
2572 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2573 if (namedIter != fParent->fTokens.end()) {
2574 return reportError<bool>("found two named member tokens");
2575 }
2576 namedIter = tokenIter;
2577 }
2578 baseIter = tokenIter;
2579 } else {
2580 break;
2581 }
2582 }
2583 // FIXME: if a member definition spans multiple lines, this won't work
2584 if (namedIter != fParent->fTokens.end()) {
2585 if (baseIter == namedIter) {
2586 return this->reportError<bool>("expected type before named token");
2587 }
2588 Definition* member = &*namedIter;
2589 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002590 if (!member->fTerminator) {
2591 member->fTerminator = member->fContentEnd;
2592 }
Cary Clark8032b982017-07-28 11:04:54 -04002593 fParent->fChildren.push_back(member);
2594 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2595 member->fChildren.push_back(&*nameType);
2596 }
Cary Clark8032b982017-07-28 11:04:54 -04002597 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002598 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002599 } else if (fParent->fChildren.size() > 0) {
2600 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002601 Definition* priorEnum = fPriorEnum;
2602 fPriorEnum = nullptr;
2603 if (!priorEnum) {
2604 while (fParent->fChildren.begin() != lastIter) {
2605 std::advance(lastIter, -1);
2606 priorEnum = *lastIter;
2607 if (Definition::Type::kBracket != priorEnum->fType ||
2608 (Bracket::kSlashSlash != priorEnum->fBracket
2609 && Bracket::kSlashStar != priorEnum->fBracket)) {
2610 break;
2611 }
Cary Clark8032b982017-07-28 11:04:54 -04002612 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002613 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002614 }
2615 if (Definition::Type::kKeyWord == priorEnum->fType
2616 && KeyWord::kEnum == priorEnum->fKeyWord) {
2617 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002618 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002619 while (tokenWalker != fParent->fTokens.end()) {
2620 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002621 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002622 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2623 break;
2624 }
2625 }
2626 while (tokenWalker != fParent->fTokens.end()) {
2627 std::advance(tokenWalker, 1);
2628 const Definition* test = &*tokenWalker;
2629 if (Definition::Type::kBracket != test->fType ||
2630 (Bracket::kSlashSlash != test->fBracket
2631 && Bracket::kSlashStar != test->fBracket)) {
2632 break;
2633 }
2634 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002635 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002636 Definition* start = &*tokenWalker;
2637 bool foundExpected = true;
2638 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2639 const Definition* test = &*tokenWalker;
2640 if (expected != test->fKeyWord) {
2641 foundExpected = false;
2642 break;
2643 }
2644 if (tokenWalker == fParent->fTokens.end()) {
2645 break;
2646 }
2647 std::advance(tokenWalker, 1);
2648 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002649 if (!foundExpected) {
2650 foundExpected = true;
2651 tokenWalker = saveTokenWalker;
2652 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2653 const Definition* test = &*tokenWalker;
2654 if (expected != test->fKeyWord) {
2655 foundExpected = false;
2656 break;
2657 }
2658 if (tokenWalker == fParent->fTokens.end()) {
2659 break;
2660 }
2661 if (KeyWord::kNone != expected) {
2662 std::advance(tokenWalker, 1);
2663 }
2664 }
2665 if (foundExpected) {
2666 auto nameToken = priorEnum->fTokens.begin();
2667 string enumName = string(nameToken->fContentStart,
2668 nameToken->fContentEnd - nameToken->fContentStart);
2669 const Definition* test = &*tokenWalker;
2670 string constType = string(test->fContentStart,
2671 test->fContentEnd - test->fContentStart);
2672 if (enumName != constType) {
2673 foundExpected = false;
2674 } else {
2675 std::advance(tokenWalker, 1);
2676 }
2677 }
2678 }
Cary Clark8032b982017-07-28 11:04:54 -04002679 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2680 const char* nameStart = tokenWalker->fStart;
2681 std::advance(tokenWalker, 1);
2682 if (tokenWalker != fParent->fTokens.end()) {
2683 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002684 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002685 start->fName = string(nameStart, tp.fChar - nameStart);
2686 start->fContentEnd = fChar;
2687 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002688 fPriorEnum = priorEnum;
2689 }
Cary Clark8032b982017-07-28 11:04:54 -04002690 }
2691 }
2692 }
2693 this->addPunctuation(Punctuation::kSemicolon);
2694 fInFunction = false;
2695 break;
2696 case '~':
2697 if (fInEnum) {
2698 break;
2699 }
2700 case '0': case '1': case '2': case '3': case '4':
2701 case '5': case '6': case '7': case '8': case '9':
2702 // TODO: don't want to parse numbers, but do need to track for enum defs
2703 // break;
2704 case 'A': case 'B': case 'C': case 'D': case 'E':
2705 case 'F': case 'G': case 'H': case 'I': case 'J':
2706 case 'K': case 'L': case 'M': case 'N': case 'O':
2707 case 'P': case 'Q': case 'R': case 'S': case 'T':
2708 case 'U': case 'V': case 'W': case 'X': case 'Y':
2709 case 'Z': case '_':
2710 case 'a': case 'b': case 'c': case 'd': case 'e':
2711 case 'f': case 'g': case 'h': case 'i': case 'j':
2712 case 'k': case 'l': case 'm': case 'n': case 'o':
2713 case 'p': case 'q': case 'r': case 's': case 't':
2714 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002715 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002716 if (fInCharCommentString || fInBrace) {
2717 break;
2718 }
2719 if (!fIncludeWord) {
2720 fIncludeWord = fChar;
2721 }
2722 break;
2723 }
2724done:
2725 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002726 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002727 return true;
2728}
2729
2730void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04002731 IncludeParser::ValidateKeyWords();
2732}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002733
Cary Clark186d08f2018-04-03 08:43:27 -04002734bool IncludeParser::references(const SkString& file) const {
2735 // if includes weren't passed one at a time, assume all references are valid
2736 if (fIncludeMap.empty()) {
2737 return true;
2738 }
2739 SkASSERT(file.endsWith(".bmh") );
2740 string root(file.c_str(), file.size() - 4);
2741 string kReference("_Reference");
2742 if (string::npos != root.find(kReference)) {
2743 root = root.substr(0, root.length() - kReference.length());
2744 }
2745 if (fIClassMap.end() != fIClassMap.find(root)) {
2746 return true;
2747 }
2748 if (fIStructMap.end() != fIStructMap.find(root)) {
2749 return true;
2750 }
Cary Clark224c7002018-06-27 11:00:21 -04002751 if (fIEnumMap.end() != fIEnumMap.find(root)) {
2752 return true;
2753 }
2754 if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
2755 return true;
2756 }
Cary Clark186d08f2018-04-03 08:43:27 -04002757 return false;
2758}
2759
Cary Clark2dc84ad2018-01-26 12:56:22 -05002760void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2761 if (!sk_isdir(includes)) {
2762 IncludeParser::RemoveOneFile(docs, includes);
2763 } else {
2764 SkOSFile::Iter it(includes, ".h");
2765 for (SkString file; it.next(&file); ) {
2766 SkString p = SkOSPath::Join(includes, file.c_str());
2767 const char* hunk = p.c_str();
2768 if (!SkStrEndsWith(hunk, ".h")) {
2769 continue;
2770 }
2771 IncludeParser::RemoveOneFile(docs, hunk);
2772 }
2773 }
2774}
2775
2776void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2777 const char* lastForward = strrchr(includesFile, '/');
2778 const char* lastBackward = strrchr(includesFile, '\\');
2779 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2780 if (!last) {
2781 last = includesFile;
2782 } else {
2783 last += 1;
2784 }
2785 SkString baseName(last);
2786 SkASSERT(baseName.endsWith(".h"));
2787 baseName.remove(baseName.size() - 2, 2);
2788 baseName.append("_Reference.bmh");
2789 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2790 remove(fullName.c_str());
2791}
Cary Clark224c7002018-06-27 11:00:21 -04002792
2793Bracket IncludeParser::topBracket() const {
2794 Definition* parent = this->parentBracket(fParent);
2795 return parent ? parent->fBracket : Bracket::kNone;
2796}