blob: 16c99af520e2b8f878aefd3887adcf151e382aba [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);
139 if (KeyWord::kNone != keyWord) {
140 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
141 this->addKeyword(keyWord);
142 return true;
143 }
144 } else {
145 this->addWord();
146 return true;
147 }
148 Definition* poundDef = fParent;
149 if (!fParent) {
150 return reportError<bool>("expected parent");
151 }
152 if (Definition::Type::kBracket != poundDef->fType) {
153 return reportError<bool>("expected bracket");
154 }
155 if (Bracket::kPound != poundDef->fBracket) {
156 return reportError<bool>("expected preprocessor");
157 }
158 if (KeyWord::kNone != poundDef->fKeyWord) {
159 return reportError<bool>("already found keyword");
160 }
161 poundDef->fKeyWord = keyWord;
162 fIncludeWord = nullptr;
163 switch (keyWord) {
164 // these do not link to other # directives
165 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400166 if (!fInBrace) {
167 SkASSERT(!fInDefine);
168 fInDefine = true;
169 }
Cary Clark8032b982017-07-28 11:04:54 -0400170 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500171 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400172 break;
173 // these start a # directive link
174 case KeyWord::kIf:
175 case KeyWord::kIfdef:
176 case KeyWord::kIfndef:
177 break;
178 // these continue a # directive link
179 case KeyWord::kElif:
180 case KeyWord::kElse: {
181 this->popObject(); // pop elif
182 if (Bracket::kPound != fParent->fBracket) {
183 return this->reportError<bool>("expected preprocessor directive");
184 }
185 this->popBracket(); // pop if
186 poundDef->fParent = fParent;
187 this->addDefinition(poundDef); // push elif back
188 } break;
189 // this ends a # directive link
190 case KeyWord::kEndif:
191 // FIXME : should this be calling popBracket() instead?
192 this->popObject(); // pop endif
193 if (Bracket::kPound != fParent->fBracket) {
194 return this->reportError<bool>("expected preprocessor directive");
195 }
196 this->popBracket(); // pop if/else
197 break;
198 default:
199 SkASSERT(0);
200 }
201 return true;
202}
203
204string IncludeParser::className() const {
205 string name(fParent->fName);
206 size_t slash = name.find_last_of("/");
207 if (string::npos == slash) {
208 slash = name.find_last_of("\\");
209 }
210 SkASSERT(string::npos != slash);
211 string result = name.substr(slash);
212 result = result.substr(1, result.size() - 3);
213 return result;
214}
215
Cary Clark884dd7d2017-10-11 10:37:52 -0400216#include <sstream>
217#include <iostream>
218
Cary Clark8032b982017-07-28 11:04:54 -0400219bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400220 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400221 string className = classMapper.first;
222 auto finder = bmhParser.fClassMap.find(className);
223 if (bmhParser.fClassMap.end() == finder) {
224 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400225 continue;
226 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400227 RootDefinition* root = &finder->second;
228 root->clearVisited();
229 }
230 for (auto& classMapper : fIClassMap) {
231 string className = classMapper.first;
232 std::istringstream iss(className);
233 string classStr;
234 string classBase;
235 RootDefinition* root = nullptr;
236 while (std::getline(iss, classStr, ':')) {
237 if (root) {
238 if (!classStr.length()) {
239 continue;
240 }
241 classBase += "::" + classStr;
242 auto finder = root->fBranches.find(classBase);
243 if (root->fBranches.end() != finder) {
244 root = finder->second;
245 } else {
246 SkASSERT(0);
247 }
248 } else {
249 classBase = classStr;
250 auto finder = bmhParser.fClassMap.find(classBase);
251 if (bmhParser.fClassMap.end() != finder) {
252 root = &finder->second;
253 } else {
254 SkASSERT(0);
255 }
256 }
257 }
Cary Clark8032b982017-07-28 11:04:54 -0400258 auto& classMap = classMapper.second;
259 auto& tokens = classMap.fTokens;
260 for (const auto& token : tokens) {
261 if (token.fPrivate) {
262 continue;
263 }
264 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400265 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400266 switch (token.fMarkType) {
267 case MarkType::kMethod: {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400268 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400269 continue;
270 }
Cary Clark8032b982017-07-28 11:04:54 -0400271 if (!def) {
272 string paramName = className + "::";
273 paramName += string(token.fContentStart,
274 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400275 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400276 if (!def && 0 == token.fName.find("operator")) {
277 string operatorName = className + "::";
278 TextParser oper("", token.fStart, token.fContentEnd, 0);
279 const char* start = oper.strnstr("operator", token.fContentEnd);
280 SkASSERT(start);
281 oper.skipTo(start);
282 oper.skipToEndBracket('(');
283 int parens = 0;
284 do {
285 if ('(' == oper.peek()) {
286 ++parens;
287 } else if (')' == oper.peek()) {
288 --parens;
289 }
290 } while (!oper.eof() && oper.next() && parens > 0);
291 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400292 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400293 }
294 }
295 if (!def) {
296 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
297 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
298 string constructorName = className + "::";
299 constructorName += string(token.fContentStart + skip,
300 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400301 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400302 }
303 if (!def && 0 == token.fName.find("SK_")) {
304 string incName = token.fName + "()";
305 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400306 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400307 if (def) {
308 if (def->fName == incName) {
309 def->fVisited = true;
310 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400311 def = root->find(className + "::toString",
312 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400313 if (def) {
314 def->fVisited = true;
315 } else {
316 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500317 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400318 }
319 }
320 break;
321 } else {
322 SkDebugf("method macro differs from 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 }
327 if (!def) {
328 bool allLower = true;
329 for (size_t index = 0; index < token.fName.length(); ++index) {
330 if (!islower(token.fName[index])) {
331 allLower = false;
332 break;
333 }
334 }
335 if (allLower) {
336 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400337 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400338 }
339 }
340 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400341 if (gAttrDeprecated == token.fName) {
342 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400343 break;
344 }
345 if (0 == token.fName.find("SkDEBUGCODE")) {
346 break;
347 }
348 }
349 if (!def) {
350 // simple method names inside nested classes have a bug and are missing trailing parens
351 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400352 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400353 }
354 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500355 if (!root->fDeprecated) {
356 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
357 fFailed = true;
358 }
Cary Clark8032b982017-07-28 11:04:54 -0400359 break;
360 }
Cary Clark73fa9722017-08-29 17:36:51 -0400361 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400362 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400363 if (MarkType::kDefinedBy == def->fMarkType) {
364 def->fParent->fVisited = true;
365 }
Cary Clark89b14562018-03-19 09:04:10 -0400366 if (token.fDeprecated && !def->fDeprecated) {
367 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
368 }
Cary Clark8032b982017-07-28 11:04:54 -0400369 } else {
370 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500371 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400372 }
373 } break;
374 case MarkType::kComment:
375 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400376 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400377 case MarkType::kEnum: {
378 if (!def) {
379 // work backwards from first word to deduce #Enum name
380 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
381 SkAssertResult(firstMember.skipName("enum"));
382 SkAssertResult(firstMember.skipToEndBracket('{'));
383 firstMember.next();
384 firstMember.skipWhiteSpace();
385 SkASSERT('k' == firstMember.peek());
386 const char* savePos = firstMember.fChar;
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400387 firstMember.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -0400388 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400389 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400390 const char* lastUnderscore = nullptr;
391 do {
392 if (!firstMember.skipToEndBracket('_')) {
393 break;
394 }
395 if (firstMember.fChar > wordEnd) {
396 break;
397 }
398 lastUnderscore = firstMember.fChar;
399 } while (firstMember.next());
400 if (lastUnderscore) {
401 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400402 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400403 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400404 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400405 }
406 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500407 if (!root->fDeprecated) {
408 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
409 fFailed = true;
410 }
Cary Clark8032b982017-07-28 11:04:54 -0400411 break;
412 }
413 }
414 def->fVisited = true;
415 for (auto& child : def->fChildren) {
416 if (MarkType::kCode == child->fMarkType) {
417 def = child;
418 break;
419 }
420 }
421 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500422 if (!root->fDeprecated) {
423 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
424 fFailed = true;
425 }
Cary Clark8032b982017-07-28 11:04:54 -0400426 break;
427 }
428 if (def->crossCheck(token)) {
429 def->fVisited = true;
430 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500431 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
432 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400433 }
434 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400435 string constName = MarkType::kEnumClass == token.fMarkType ?
436 fullName : className;
437 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400438 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400439 if (!def) {
440 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400441 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400442 }
443 if (!def) {
444 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500445 if (!root->fDeprecated) {
446 SkDebugf("const missing from bmh: %s\n", constName.c_str());
447 fFailed = true;
448 }
Cary Clark8032b982017-07-28 11:04:54 -0400449 }
450 } else {
451 def->fVisited = true;
452 }
453 }
454 } break;
455 case MarkType::kMember:
456 if (def) {
457 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500458 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400459 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500460 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400461 }
462 break;
Cary Clark2f466242017-12-11 16:03:17 -0500463 case MarkType::kTypedef:
464 if (def) {
465 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500466 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500467 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500468 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500469 }
470 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -0400471 case MarkType::kConst:
472 if (def) {
473 def->fVisited = true;
474 } else if (!root->fDeprecated) {
475 SkDebugf("const missing from bmh: %s\n", fullName.c_str());
476 fFailed = true;
477 }
478 break;
Cary Clark8032b982017-07-28 11:04:54 -0400479 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400480 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400481 break;
482 }
483 }
484 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500485 int crossChecks = 0;
486 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400487 for (auto& classMapper : fIClassMap) {
488 string className = classMapper.first;
489 auto finder = bmhParser.fClassMap.find(className);
490 if (bmhParser.fClassMap.end() == finder) {
491 continue;
492 }
493 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500494 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500495 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400496 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500497 if (crossChecks) {
498 SkDebugf(".");
499 } else {
500 SkDebugf("cross-check");
501 firstCheck = className;
502 }
503 ++crossChecks;
504 }
505 if (crossChecks) {
506 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500507 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500508 }
509 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400510 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400511 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500512 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400513}
514
515IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400516 string name) {
Cary Clark8032b982017-07-28 11:04:54 -0400517 string className;
518 const Definition* test = fParent;
519 while (Definition::Type::kFileType != test->fType) {
520 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
521 className = test->fName + "::";
522 break;
523 }
524 test = test->fParent;
525 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400526 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400527 unordered_map<string, IClassDefinition>& map = fIClassMap;
528 IClassDefinition& markupDef = map[className];
529 if (markupDef.fStart) {
530 typedef IClassDefinition* IClassDefPtr;
531 return INHERITED::reportError<IClassDefPtr>("class already defined");
532 }
533 markupDef.fFileName = fFileName;
534 markupDef.fStart = includeDef.fStart;
535 markupDef.fContentStart = includeDef.fStart;
536 markupDef.fName = className;
537 markupDef.fContentEnd = includeDef.fContentEnd;
538 markupDef.fTerminator = includeDef.fTerminator;
539 markupDef.fParent = fParent;
540 markupDef.fLineCount = fLineCount;
541 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
542 MarkType::kStruct : MarkType::kClass;
543 markupDef.fKeyWord = includeDef.fKeyWord;
544 markupDef.fType = Definition::Type::kMark;
545 fParent = &markupDef;
546 return &markupDef;
547}
548
549void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
550 auto& tokens = classDef.fTokens;
551 for (auto& token : tokens) {
552 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
553 continue;
554 }
555 if (MarkType::kMember != token.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400556 this->writeBlockSeparator();
Cary Clark8032b982017-07-28 11:04:54 -0400557 }
558 switch (token.fMarkType) {
559 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500560 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500561 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400562 break;
563 case MarkType::kMethod:
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400564 this->dumpMethod(token, classDef.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400565 break;
566 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400567 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400568 continue;
569 break;
570 default:
571 SkASSERT(0);
572 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400573 this->dumpCommonTail(token);
Cary Clark8032b982017-07-28 11:04:54 -0400574 }
575}
Cary Clark9174bda2017-09-19 17:39:32 -0400576void IncludeParser::dumpComment(const Definition& token) {
577 fLineCount = token.fLineCount;
578 fChar = fLine = token.fContentStart;
579 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400580 bool sawParam = false;
581 bool multiline = false;
582 bool sawReturn = false;
583 bool sawComment = false;
584 bool methodHasReturn = false;
585 vector<string> methodParams;
586 vector<string> foundParams;
587 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400588 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
589 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500590 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400591 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500592 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400593 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400594 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500595 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400596 && !methodParser.strnchr('~', methodParser.fEnd);
597 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
598 const char* nextEnd = paren;
599 do {
600 string paramName;
601 methodParser.fChar = nextEnd + 1;
602 methodParser.skipSpace();
603 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
604 continue;
605 }
606 methodParams.push_back(paramName);
607 } while (')' != nextEnd[0]);
608 }
Cary Clark9174bda2017-09-19 17:39:32 -0400609 for (const auto& child : token.fTokens) {
610 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
611 break;
612 }
Cary Clark8032b982017-07-28 11:04:54 -0400613 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400614 if (child.fPrivate) {
615 break;
616 }
Cary Clark8032b982017-07-28 11:04:54 -0400617 if ('@' == child.fContentStart[0]) {
618 TextParser parser(&child);
619 do {
620 parser.next();
621 if (parser.startsWith("param ")) {
622 parser.skipWord("param");
623 const char* parmStart = parser.fChar;
624 parser.skipToSpace();
625 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
626 parser.skipWhiteSpace();
627 do {
628 size_t nextComma = parmName.find(',');
629 string piece;
630 if (string::npos == nextComma) {
631 piece = parmName;
632 parmName = "";
633 } else {
634 piece = parmName.substr(0, nextComma);
635 parmName = parmName.substr(nextComma + 1);
636 }
637 if (sawParam) {
638 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400639 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400640 }
Cary Clark9174bda2017-09-19 17:39:32 -0400641 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400642 } else {
643 if (sawComment) {
644 this->nl();
645 }
646 this->lf(2);
647 }
648 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400649 this->writeTag("Param", piece);
650 this->writeSpace(2);
651 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
652 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400653 sawParam = true;
654 sawComment = false;
655 } while (parmName.length());
656 parser.skipTo(parser.fEnd);
657 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
658 parser.skipWord("return");
659 if ('s' == parser.peek()) {
660 parser.next();
661 }
662 if (sawParam) {
663 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400664 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400665 }
Cary Clark9174bda2017-09-19 17:39:32 -0400666 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400667 }
668 this->checkForMissingParams(methodParams, foundParams);
669 sawParam = false;
670 sawComment = false;
671 multiline = false;
672 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400673 this->writeTag("Return");
674 this->writeSpace(2);
675 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
676 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400677 sawReturn = true;
678 parser.skipTo(parser.fEnd);
679 } else {
680 this->reportError("unexpected doxygen directive");
681 }
682 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400683 } else if (child.length() > 1) {
684 const char* start = child.fContentStart;
685 ptrdiff_t length = child.fContentEnd - start;
686 SkASSERT(length >= 0);
687 while (length && '/' == start[0]) {
688 start += 1;
689 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400690 }
Cary Clark9174bda2017-09-19 17:39:32 -0400691 while (length && '/' == start[length - 1]) {
692 length -= 1;
693 if (length && '*' == start[length - 1]) {
694 length -= 1;
695 }
696 }
697 if (length) {
698 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
699 if (sawParam || sawReturn) {
700 this->indentToColumn(8);
701 }
702 this->writeBlock(length, start);
703 this->writeSpace();
704 sawComment = true;
705 if (sawParam || sawReturn) {
706 multiline = true;
707 }
Cary Clark8032b982017-07-28 11:04:54 -0400708 }
709 }
710 }
711 }
712 if (sawParam || sawReturn) {
713 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400714 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400715 }
Cary Clark9174bda2017-09-19 17:39:32 -0400716 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400717 }
718 if (!sawReturn) {
719 if (!sawParam) {
720 if (sawComment) {
721 this->nl();
722 }
723 this->lf(2);
724 }
725 this->checkForMissingParams(methodParams, foundParams);
726 }
727 if (methodHasReturn != sawReturn) {
728 if (!methodHasReturn) {
729 this->reportError("unexpected doxygen return");
730 } else {
731 if (sawComment) {
732 this->nl();
733 }
734 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400735 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400736 }
737 }
738}
739
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400740void IncludeParser::dumpCommonTail(const Definition& token) {
741 this->lf(2);
742 this->writeTag("Example");
743 this->lf(1);
744 this->writeString("// incomplete");
745 this->lf(1);
746 this->writeEndTag();
747 this->lf(2);
748 this->writeTag("SeeAlso");
749 this->writeSpace();
750 this->writeString("incomplete");
751 this->lf(2);
752 this->writeEndTag(BmhParser::kMarkProps[(int) token.fMarkType].fName);
753 this->lf(2);
754}
755
756void IncludeParser::dumpDefine(const Definition& token) {
757 this->writeTag("Define", token.fName);
758 this->lf(2);
759 this->writeTag("Code");
760 this->lfAlways(1);
761 this->writeString("###$");
762 this->lfAlways(1);
763 this->indentToColumn(4);
764 this->writeBlock(token.fTerminator - token.fStart, token.fStart);
765 this->lf(1);
766 this->indentToColumn(0);
767 this->writeString("$$$#");
768
769 this->writeEndTag();
770 this->lf(2);
771 this->dumpComment(token);
772 for (auto& child : token.fTokens) {
773 if (MarkType::kComment == child.fMarkType) {
774 continue;
775 }
776 this->writeTag("Param", child.fName);
777 this->writeSpace();
778 this->writeString("incomplete");
779 this->writeSpace();
780 this->writeString("##");
781 this->lf(1);
782 }
783}
784
785void IncludeParser::dumpEnum(const Definition& token, string name) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500786 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400787 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400788 this->writeTag("Code");
Cary Clark9174bda2017-09-19 17:39:32 -0400789 this->lfAlways(1);
790 this->indentToColumn(4);
791 this->writeString("enum");
792 this->writeSpace();
793 if ("_anonymous" != token.fName.substr(0, 10)) {
794 this->writeString(token.fName);
795 this->writeSpace();
796 }
797 this->writeString("{");
798 this->lfAlways(1);
799 for (auto& child : token.fChildren) {
800 this->indentToColumn(8);
801 this->writeString(child->fName);
802 if (child->length()) {
803 this->writeSpace();
804 this->writeBlock(child->length(), child->fContentStart);
805 }
806 if (',' != fLastChar) {
807 this->writeString(",");
808 }
809 this->lfAlways(1);
810 }
811 this->indentToColumn(4);
812 this->writeString("};");
813 this->lf(1);
814 this->writeString("##");
815 this->lf(2);
816 this->dumpComment(token);
817 for (auto& child : token.fChildren) {
818 // start here;
819 // get comments before
820 // or after const values
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400821 this->writeTag("Const");
Cary Clark9174bda2017-09-19 17:39:32 -0400822 this->writeSpace();
823 this->writeString(child->fName);
824 TextParser val(child);
825 if (!val.eof()) {
826 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
827 val.next();
828 val.skipSpace();
829 const char* valEnd = val.anyOf(",\n");
830 if (!valEnd) {
831 valEnd = val.fEnd;
832 }
833 this->writeSpace();
834 this->writeBlock(valEnd - val.fStart, val.fStart);
835 } else {
836 this->writeSpace();
837 this->writeDefinition(*child);
838 }
839 }
840 this->lf(1);
841 for (auto comment : child->fChildren) {
842 if (MarkType::kComment == comment->fMarkType) {
843 TextParser parser(comment);
844 parser.skipExact("*");
845 parser.skipExact("*");
846 while (!parser.eof() && parser.skipWhiteSpace()) {
847 parser.skipExact("*");
848 parser.skipWhiteSpace();
849 const char* start = parser.fChar;
850 parser.skipToEndBracket('\n');
851 this->lf(1);
852 this->writeBlock(parser.fChar - start, start);
853 }
854 }
855 }
856 this->writeEndTag();
857 }
858 this->lf(2);
859}
860
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400861bool IncludeParser::dumpGlobals() {
862 size_t lastBSlash = fFileName.rfind('\\');
863 size_t lastSlash = fFileName.rfind('/');
864 size_t lastDotH = fFileName.rfind(".h");
865 SkASSERT(string::npos != lastDotH);
866 if (string::npos != lastBSlash && (string::npos == lastSlash
867 || lastBSlash < lastSlash)) {
868 lastSlash = lastBSlash;
869 } else if (string::npos == lastSlash) {
870 lastSlash = -1;
871 }
872 lastSlash += 1;
873 string globalsName = fFileName.substr(lastSlash, lastDotH - lastSlash);
874 string fileName = globalsName + "_Reference.bmh";
875 fOut = fopen(fileName.c_str(), "wb");
876 if (!fOut) {
877 SkDebugf("could not open output file %s\n", globalsName.c_str());
878 return false;
879 }
880 string prefixName = globalsName.substr(0, 2);
881 string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
882 ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
883 this->writeTagNoLF("Topic", topicName);
884 this->writeTag("Alias", topicName + "_Reference");
885 this->lf(2);
886 this->writeTag("Subtopic", "Overview");
887 fIndent += 4;
888 this->writeTag("Subtopic", "Subtopic");
889 fIndent += 4;
890 this->writeTag("Populate");
891 fIndent -= 4;
892 this->writeEndTag();
893 fIndent -= 4;
894 this->writeEndTag();
895 this->lf(2);
896 if (!fIDefineMap.empty()) {
897 this->writeTag("Subtopic", "Define");
898 this->writeTag("Populate");
Cary Clark9174bda2017-09-19 17:39:32 -0400899 this->writeEndTag();
900 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400901 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400902 if (!fIFunctionMap.empty()) {
903 this->writeTag("Subtopic", "Function");
904 this->writeTag("Populate");
905 this->writeEndTag();
906 this->lf(2);
907 }
908 if (!fIEnumMap.empty()) {
909 this->writeTag("Subtopic", "Enum");
910 this->writeTag("Populate");
911 this->writeEndTag();
912 this->lf(2);
913 }
914 if (!fITemplateMap.empty()) {
915 this->writeTag("Subtopic", "Template");
916 this->writeTag("Populate");
917 this->writeEndTag();
918 this->lf(2);
919 }
920 if (!fITypedefMap.empty()) {
921 this->writeTag("Subtopic", "Typedef");
922 this->writeTag("Populate");
923 this->writeEndTag();
924 this->lf(2);
925 }
926 if (!fIUnionMap.empty()) {
927 this->writeTag("Subtopic", "Union");
928 this->writeTag("Populate");
929 this->writeEndTag();
930 this->lf(2);
931 }
932 std::map<int, Definition*> sortedDefs;
933 for (const auto& entry : fIDefineMap) {
934 sortedDefs[entry.second->fLineCount] = entry.second;
935 }
936 for (const auto& entry : fIFunctionMap) {
937 sortedDefs[entry.second->fLineCount] = entry.second;
938 }
939 for (const auto& entry : fIEnumMap) {
940 sortedDefs[entry.second->fLineCount] = entry.second;
941 }
942 for (const auto& entry : fITemplateMap) {
943 sortedDefs[entry.second->fLineCount] = entry.second;
944 }
945 for (const auto& entry : fITypedefMap) {
946 sortedDefs[entry.second->fLineCount] = entry.second;
947 }
948 for (const auto& entry : fIUnionMap) {
949 sortedDefs[entry.second->fLineCount] = entry.second;
950 }
951 for (const auto& entry : sortedDefs) {
952 const Definition* def = entry.second;
953 this->writeBlockSeparator();
954 switch (def->fMarkType) {
955 case MarkType::kDefine:
956 this->dumpDefine(*def);
957 break;
958 case MarkType::kMethod:
959 this->dumpMethod(*def, globalsName);
960 break;
961 case MarkType::kEnum:
962 case MarkType::kEnumClass:
963 this->dumpEnum(*def, globalsName);
964 break;
965 case MarkType::kTemplate:
966 SkASSERT(0); // incomplete
967 break;
968 case MarkType::kTypedef: {
969 this->writeTag("Typedef");
970 this->writeSpace();
971 TextParser parser(def);
972 if (!parser.skipExact("typedef")) {
973 return false;
974 }
975 if (!parser.skipSpace()) {
976 return false;
977 }
978 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
979 this->lf(2);
980 this->dumpComment(*def);
981 this->writeEndTag(BmhParser::kMarkProps[(int) entry.second->fMarkType].fName);
982 this->lf(2);
983 } continue;
984 case MarkType::kUnion:
985 SkASSERT(0); // incomplete
986 break;
987 default:
988 SkASSERT(0);
989 }
990 this->dumpCommonTail(*def);
991 }
992 this->writeEndTag("Topic", topicName);
993 this->lfAlways(1);
994 fclose(fOut);
995 SkDebugf("wrote %s\n", fileName.c_str());
996 return true;
997}
998
999bool IncludeParser::isClone(const Definition& token) {
1000 string name = token.fName;
1001 return name[name.length() - 2] == '_' && isdigit(name[name.length() - 1]);
1002}
1003
1004bool IncludeParser::isConstructor(const Definition& token, string className) {
1005 string name = token.fName;
1006 return 0 == name.find(className) || '~' == name[0];
1007}
1008
1009bool IncludeParser::isInternalName(const Definition& token) {
1010 string name = token.fName;
1011 // exception for this SkCanvas function .. for now
1012 if (0 == token.fName.find("androidFramework_setDeviceClipRestriction")) {
1013 return false;
1014 }
1015 return name.substr(0, 7) == "android"
1016 || 0 == token.fName.find("internal_")
1017 || 0 == token.fName.find("Internal_")
1018 || 0 == token.fName.find("legacy_")
1019 || 0 == token.fName.find("temporary_")
1020 || 0 == token.fName.find("private_");
1021}
1022
1023bool IncludeParser::isOperator(const Definition& token) {
1024 return "operator" == token.fName.substr(0, 8);
1025}
1026
1027void IncludeParser::dumpMethod(const Definition& token, string className) {
1028 this->writeTag("Method");
1029 this->writeSpace();
1030
1031 string name = string(token.fStart ? token.fStart : token.fContentStart,
1032 token.length());
1033 if (this->isOperator(token)) {
1034 string spaceConst(" const");
1035 size_t constPos = name.rfind(spaceConst);
1036 if (name.length() - spaceConst.length() == constPos) {
1037 name = name.substr(0, constPos) + "_const";
1038 }
1039 }
1040 this->writeString(name);
1041 string inType;
1042 if (this->isConstructor(token, className)) {
1043 inType = "Constructor";
1044 } else if (this->isOperator(token)) {
1045 inType = "Operator";
1046 } else {
1047 inType = "incomplete";
1048 }
1049 this->writeTag("In", inType);
1050 this->writeTag("Line");
1051 this->writeSpace(1);
1052 this->writeString("#");
1053 this->writeSpace(1);
1054 this->writeString("incomplete");
1055 this->writeSpace(1);
1056 this->writeString("##");
1057 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001058 this->dumpComment(token);
1059}
1060
1061void IncludeParser::dumpMember(const Definition& token) {
1062 this->writeTag("Member");
1063 this->writeSpace();
1064 this->writeDefinition(token, token.fName, 2);
1065 lf(1);
1066 for (auto child : token.fChildren) {
1067 this->writeDefinition(*child);
1068 }
1069 this->writeEndTag();
1070 lf(2);
1071}
1072
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001073bool IncludeParser::dumpTokens() {
1074 if (!this->dumpGlobals()) {
1075 return false;
1076 }
Cary Clark9174bda2017-09-19 17:39:32 -04001077 for (const auto& member : fIClassMap) {
1078 if (string::npos != member.first.find("::")) {
1079 continue;
1080 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001081 if (!this->dumpTokens(member.first)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001082 return false;
1083 }
1084 }
1085 return true;
1086}
1087
Ben Wagner63fd7602017-10-09 15:45:33 -04001088 // dump equivalent markup
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001089bool IncludeParser::dumpTokens(string skClassName) {
1090 string fileName = skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -04001091 fOut = fopen(fileName.c_str(), "wb");
1092 if (!fOut) {
1093 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -04001094 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001095 }
1096 string prefixName = skClassName.substr(0, 2);
1097 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
1098 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -04001099 this->writeTagNoLF("Topic", topicName);
1100 this->writeTag("Alias", topicName + "_Reference");
1101 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001102 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -05001103 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
1104 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001105 this->writeTag(containerType, skClassName);
1106 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001107 auto& tokens = classMap.fTokens;
1108 for (auto& token : tokens) {
1109 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1110 continue;
1111 }
Cary Clark9174bda2017-09-19 17:39:32 -04001112 this->writeDefinition(token);
1113 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -04001114 }
1115 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001116 bool hasClass = false;
1117 bool hasConst = !fIEnumMap.empty();
1118 bool hasConstructor = false;
1119 bool hasMember = false;
1120 bool hasOperator = false;
Cary Clark8032b982017-07-28 11:04:54 -04001121 for (const auto& oneClass : fIClassMap) {
1122 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1123 continue;
1124 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001125 hasClass = true;
1126 break;
Cary Clark8032b982017-07-28 11:04:54 -04001127 }
Cary Clark8032b982017-07-28 11:04:54 -04001128 for (const auto& token : classMap.fTokens) {
1129 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
1130 continue;
1131 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001132 if (this->isInternalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001133 continue;
1134 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001135 if (this->isConstructor(token, skClassName)) {
1136 hasConstructor = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001137 continue;
1138 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001139 if (this->isOperator(token)) {
1140 hasOperator = true;
Cary Clark9174bda2017-09-19 17:39:32 -04001141 continue;
1142 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001143 if (this->isClone(token)) {
Cary Clark8032b982017-07-28 11:04:54 -04001144 continue;
1145 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001146 hasMember = true;
Cary Clark8032b982017-07-28 11:04:54 -04001147 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001148 this->writeTag("Subtopic", "Overview");
1149 fIndent += 4;
1150 this->writeTag("Subtopic", "Subtopic");
1151 fIndent += 4;
1152 this->writeTag("Populate");
1153 fIndent -= 4;
1154 this->writeEndTag();
1155 fIndent -= 4;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001156 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001157 this->lf(2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001158
1159 if (hasClass) {
1160 this->writeTag("Subtopic", "Class_or_Struct");
1161 this->writeTag("Populate");
1162 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001163 this->lf(2);
1164 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001165 if (hasConst) {
1166 this->writeTag("Subtopic", "Constant");
1167 this->writeTag("Populate");
1168 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001169 this->lf(2);
1170 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001171 if (hasConstructor) {
1172 this->writeTag("Subtopic", "Constructor");
1173 this->writeTag("Populate");
1174 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001175 this->lf(2);
1176 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001177 if (hasOperator) {
1178 this->writeTag("Subtopic", "Operator");
1179 this->writeTag("Populate");
1180 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001181 this->lf(2);
1182 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001183 if (hasMember) {
1184 this->writeTag("Subtopic", "Member_Function");
1185 this->writeTag("Populate");
1186 this->writeEndTag();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001187 this->lf(2);
1188 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001189 for (auto& oneEnum : fIEnumMap) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001190 this->writeBlockSeparator();
1191 this->dumpEnum(*oneEnum.second, oneEnum.first);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001192 this->lf(2);
1193 this->writeTag("Example");
1194 this->lfcr();
1195 this->writeString("// incomplete");
1196 this->writeEndTag();
1197 this->lf(2);
1198 this->writeTag("SeeAlso", "incomplete");
1199 this->lf(2);
1200 this->writeEndTag("Enum", oneEnum.first);
1201 this->lf(2);
1202 }
Cary Clark8032b982017-07-28 11:04:54 -04001203 for (auto& oneClass : fIClassMap) {
1204 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1205 continue;
1206 }
1207 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001208 this->writeBlockSeparator();
Cary Clarka560c472017-11-27 10:44:06 -05001209 KeyWord keyword = oneClass.second.fKeyWord;
1210 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1211 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001212 this->writeTag(containerType, innerName);
1213 this->lf(2);
1214 this->writeTag("Code");
1215 this->writeEndTag("ToDo", "fill this in manually");
1216 this->writeEndTag();
1217 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001218 for (auto& token : oneClass.second.fTokens) {
1219 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1220 continue;
1221 }
Cary Clark9174bda2017-09-19 17:39:32 -04001222 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001223 }
1224 this->lf(2);
1225 this->dumpClassTokens(oneClass.second);
1226 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001227 this->writeEndTag(containerType, innerName);
1228 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001229 }
1230 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001231 this->writeEndTag(containerType, skClassName);
1232 this->lf(2);
1233 this->writeEndTag("Topic", topicName);
1234 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001235 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001236 SkDebugf("wrote %s\n", fileName.c_str());
1237 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001238}
1239
1240bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1241 // add comment preceding class, if any
1242 const Definition* parent = includeDef.fParent;
1243 int index = includeDef.fParentIndex;
1244 auto wordIter = parent->fTokens.begin();
1245 std::advance(wordIter, index);
1246 SkASSERT(&*wordIter == &includeDef);
1247 while (parent->fTokens.begin() != wordIter) {
1248 auto testIter = std::prev(wordIter);
1249 if (Definition::Type::kWord != testIter->fType
1250 && Definition::Type::kKeyWord != testIter->fType
1251 && (Definition::Type::kBracket != testIter->fType
1252 || Bracket::kAngle != testIter->fBracket)
1253 && (Definition::Type::kPunctuation != testIter->fType
1254 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1255 break;
1256 }
1257 wordIter = testIter;
1258 }
1259 auto commentIter = wordIter;
1260 while (parent->fTokens.begin() != commentIter) {
1261 auto testIter = std::prev(commentIter);
1262 bool isComment = Definition::Type::kBracket == testIter->fType
1263 && (Bracket::kSlashSlash == testIter->fBracket
1264 || Bracket::kSlashStar == testIter->fBracket);
1265 if (!isComment) {
1266 break;
1267 }
1268 commentIter = testIter;
1269 }
1270 while (commentIter != wordIter) {
1271 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1272 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1273 return false;
1274 }
1275 commentIter = std::next(commentIter);
1276 }
1277 return true;
1278}
1279
Cary Clark0d225392018-06-07 09:59:07 -04001280Definition* IncludeParser::findIncludeObject(const Definition& includeDef, MarkType markType,
1281 string typeName) {
1282 typedef Definition* DefinitionPtr;
1283 auto mapIter = std::find_if(fMaps.begin(), fMaps.end(),
1284 [markType](DefinitionMap& defMap){ return markType == defMap.fMarkType; } );
1285 if (mapIter == fMaps.end()) {
1286 return nullptr;
1287 }
1288 if (mapIter->fInclude->end() == mapIter->fInclude->find(typeName)) {
1289 return reportError<DefinitionPtr>("invalid mark type");
1290 }
1291 string name = this->uniqueName(*mapIter->fInclude, typeName);
1292 Definition& markupDef = *(*mapIter->fInclude)[name];
1293 if (markupDef.fStart) {
1294 return reportError<DefinitionPtr>("definition already defined");
1295 }
1296 markupDef.fFileName = fFileName;
1297 markupDef.fStart = includeDef.fStart;
1298 markupDef.fContentStart = includeDef.fStart;
1299 markupDef.fName = name;
1300 markupDef.fContentEnd = includeDef.fContentEnd;
1301 markupDef.fTerminator = includeDef.fTerminator;
1302 markupDef.fParent = fParent;
1303 markupDef.fLineCount = includeDef.fLineCount;
1304 markupDef.fMarkType = markType;
1305 markupDef.fKeyWord = includeDef.fKeyWord;
1306 markupDef.fType = Definition::Type::kMark;
1307 return &markupDef;
1308}
1309
Cary Clark137b8742018-05-30 09:21:49 -04001310// caller just returns, so report error here
Cary Clark8032b982017-07-28 11:04:54 -04001311bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1312 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001313 // parse class header
1314 auto iter = includeDef->fTokens.begin();
1315 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1316 // todo : documentation is ignoring this for now
1317 iter = std::next(iter);
1318 }
1319 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1320 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001321 iter = std::next(iter);
1322 if (iter == includeDef->fTokens.end()) {
1323 return true; // forward declaration only
1324 }
Cary Clark8032b982017-07-28 11:04:54 -04001325 do {
1326 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001327 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001328 }
1329 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1330 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001331 }
Cary Clark8032b982017-07-28 11:04:54 -04001332 } while (static_cast<void>(iter = std::next(iter)), true);
1333 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001334 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001335 }
1336 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1337 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001338 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001339 }
1340 markupDef->fStart = iter->fStart;
1341 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001342 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001343 }
1344// if (1 != includeDef->fChildren.size()) {
1345// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1346// }
1347 includeDef = includeDef->fChildren.front();
1348 iter = includeDef->fTokens.begin();
1349 // skip until public
1350 int publicIndex = 0;
1351 if (IsStruct::kNo == isStruct) {
1352 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1353 size_t publicLen = strlen(publicName);
1354 while (iter != includeDef->fTokens.end()
1355 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1356 || strncmp(iter->fStart, publicName, publicLen))) {
Cary Clark137b8742018-05-30 09:21:49 -04001357 iter->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001358 iter = std::next(iter);
1359 ++publicIndex;
1360 }
1361 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001362 int keyIndex = publicIndex;
1363 KeyWord currentKey = KeyWord::kPublic;
1364 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1365 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001366 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1367 size_t protectedLen = strlen(protectedName);
1368 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1369 size_t privateLen = strlen(privateName);
Cary Clark137b8742018-05-30 09:21:49 -04001370 auto childIter = includeDef->fChildren.begin();
1371 std::advance(childIter, publicIndex);
Cary Clark884dd7d2017-10-11 10:37:52 -04001372 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001373 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001374 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001375 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001376 const char* testStart = iter->fStart;
1377 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1378 iter = std::next(iter);
1379 ++keyIndex;
1380 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1381 currentKey = KeyWord::kPublic;
1382 break;
1383 }
1384 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1385 currentKey = KeyWord::kProtected;
1386 break;
1387 }
1388 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1389 currentKey = KeyWord::kPrivate;
1390 break;
1391 }
1392 }
1393 fLastObject = nullptr;
1394 if (KeyWord::kPublic == currentKey) {
1395 if (!this->parseObject(child, markupDef)) {
1396 return false;
1397 }
Cary Clark8032b982017-07-28 11:04:54 -04001398 }
Cary Clark73fa9722017-08-29 17:36:51 -04001399 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001400 childIter = std::next(childIter);
1401 }
Cary Clark137b8742018-05-30 09:21:49 -04001402 while (iter != includeDef->fTokens.end()) {
1403 iter->fPrivate = KeyWord::kPublic != currentKey;
1404 iter = std::next(iter);
1405 }
Cary Clark8032b982017-07-28 11:04:54 -04001406 SkASSERT(fParent->fParent);
1407 fParent = fParent->fParent;
1408 return true;
1409}
1410
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001411bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001412 int lineCount, Definition* markupDef) {
1413 TextParser parser(filename, start, end, lineCount);
1414 // parse doxygen if present
1415 if (parser.startsWith("**")) {
1416 parser.next();
1417 parser.next();
1418 parser.skipWhiteSpace();
1419 if ('\\' == parser.peek()) {
1420 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001421 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1422 if (parser.skipExact("file")) {
1423 if (Definition::Type::kFileType != fParent->fType) {
1424 return reportError<bool>("expected parent is file");
1425 }
1426 string filename = markupDef->fileName();
1427 if (!parser.skipWord(filename.c_str())) {
1428 return reportError<bool>("missing object type");
1429 }
1430 } else if (parser.skipExact("fn")) {
1431 SkASSERT(0); // incomplete
1432 } else {
1433 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1434 return reportError<bool>("missing object type");
1435 }
1436 if (!parser.skipWord(markupDef->fName.c_str()) &&
1437 KeyWord::kEnum != markupDef->fKeyWord) {
1438 return reportError<bool>("missing object name");
1439 }
Cary Clark8032b982017-07-28 11:04:54 -04001440 }
Cary Clark8032b982017-07-28 11:04:54 -04001441 }
1442 }
1443 // remove leading '*' if present
1444 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1445 while (!parser.eof() && parser.skipWhiteSpace()) {
1446 while ('*' == parser.peek()) {
1447 parser.next();
1448 if (parser.eof()) {
1449 break;
1450 }
1451 parser.skipWhiteSpace();
1452 }
1453 if (parser.eof()) {
1454 break;
1455 }
1456 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001457 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001458 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001459 parser.skipToEndBracket('\n');
1460 }
1461 return true;
1462}
1463
Cary Clarkd98f78c2018-04-26 08:32:37 -04001464bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04001465 if (!markupDef) {
1466 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1467 child->fLineCount, fParent, '\0');
1468 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04001469 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04001470 globalMarkupChild->fName = globalUniqueName;
1471 if (!this->findComments(*child, globalMarkupChild)) {
1472 return false;
1473 }
1474 fIConstMap[globalUniqueName] = globalMarkupChild;
1475 return true;
1476 }
1477 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1478 child->fLineCount, markupDef, '\0');
1479 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04001480 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001481 markupChild->fTerminator = markupChild->fContentEnd;
1482 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04001483 classDef.fConsts[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001484 return true;
1485}
1486
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001487bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1488 TextParser parser(child);
1489 if (!parser.skipExact("#define")) {
1490 return false;
1491 }
1492 if (!parser.skipSpace()) {
1493 return false;
1494 }
1495 const char* nameStart = parser.fChar;
1496 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1497 if (parser.eof()) {
1498 return true; // do nothing if #define doesn't define anything
1499 }
1500 string nameStr(nameStart, parser.fChar - nameStart);
1501 struct Param {
1502 const char* fStart;
1503 const char* fEnd;
1504 };
1505 vector<Param> params;
1506 if ('(' == parser.peek()) {
1507 parser.next();
1508 if (!parser.skipSpace()) {
1509 return false;
1510 }
1511 do {
1512 const char* paramStart = parser.fChar;
1513 if (!parser.skipExact("...")) {
1514 parser.skipToNonAlphaNum();
1515 }
1516 if (parser.eof()) {
1517 return false;
1518 }
1519 params.push_back({paramStart, parser.fChar});
1520 if (!parser.skipSpace()) {
1521 return false;
1522 }
1523 if (')' == parser.peek()) {
1524 parser.next();
1525 break;
1526 }
1527 if (',' != parser.next()) {
1528 return false;
1529 }
1530 if (!parser.skipSpace()) {
1531 return false;
1532 }
1533 } while (true);
1534 }
1535 if (!parser.skipSpace()) {
1536 return false;
1537 }
1538 if (!markupDef) {
1539 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1540 child->fLineCount, fParent, '\0');
1541 Definition* globalMarkupChild = &fGlobals.back();
1542 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1543 globalMarkupChild->fName = globalUniqueName;
1544 globalMarkupChild->fTerminator = child->fContentEnd;
1545 if (!this->findComments(*child, globalMarkupChild)) {
1546 return false;
1547 }
1548 fIDefineMap[globalUniqueName] = globalMarkupChild;
1549 for (Param param : params) {
1550 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1551 child->fLineCount, globalMarkupChild, '\0');
1552 Definition* paramChild = &globalMarkupChild->fTokens.back();
1553 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1554 paramChild->fTerminator = param.fEnd;
1555 }
1556 return true;
1557 }
1558 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1559 child->fLineCount, markupDef, '\0');
1560 Definition* markupChild = &markupDef->fTokens.back();
1561 markupChild->fName = nameStr;
1562 markupChild->fTerminator = markupChild->fContentEnd;
1563 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1564 if (!this->findComments(*child, markupChild)) {
1565 return false;
1566 }
1567 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001568 return true;
1569}
1570
1571bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001572 TextParser parser(child);
1573 parser.skipToEndBracket('{');
1574 if (parser.eof()) {
1575 return true; // if enum is a forward declaration, do nothing
1576 }
1577 parser.next();
1578 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001579 if (child->fTokens.size() > 0) {
1580 auto token = child->fTokens.begin();
1581 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1582 token = token->fTokens.begin();
1583 }
1584 if (Definition::Type::kWord == token->fType) {
1585 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1586 }
1587 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001588 Definition* markupChild;
1589 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001590 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1591 child->fLineCount, fParent, '\0');
1592 markupChild = &fGlobals.back();
1593 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1594 markupChild->fName = globalUniqueName;
1595 markupChild->fTerminator = child->fContentEnd;
1596 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001597 } else {
1598 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001599 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001600 markupChild = &markupDef->fTokens.back();
1601 }
Cary Clark8032b982017-07-28 11:04:54 -04001602 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1603 markupChild->fKeyWord = KeyWord::kEnum;
1604 TextParser enumName(child);
1605 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001606 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001607 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001608 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001609 markupChild->fMarkType = MarkType::kEnumClass;
1610 }
Cary Clark8032b982017-07-28 11:04:54 -04001611 const char* nameStart = enumName.fChar;
1612 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001613 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001614 markupChild->fName = markupDef->fName + "::" +
1615 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001616 }
Cary Clark8032b982017-07-28 11:04:54 -04001617 if (!this->findComments(*child, markupChild)) {
1618 return false;
1619 }
Cary Clark8032b982017-07-28 11:04:54 -04001620 const char* dataEnd;
1621 do {
Cary Clark8032b982017-07-28 11:04:54 -04001622 parser.skipWhiteSpace();
1623 if ('}' == parser.peek()) {
1624 break;
1625 }
1626 Definition* comment = nullptr;
1627 // note that comment, if any, can be before or after (on the same line, though) as member
1628 if ('#' == parser.peek()) {
1629 // fixme: handle preprecessor, but just skip it for now
1630 parser.skipToLineStart();
1631 }
1632 while (parser.startsWith("/*") || parser.startsWith("//")) {
1633 parser.next();
1634 const char* start = parser.fChar;
1635 const char* end;
1636 if ('*' == parser.peek()) {
1637 end = parser.strnstr("*/", parser.fEnd);
1638 parser.fChar = end;
1639 parser.next();
1640 parser.next();
1641 } else {
1642 end = parser.trimmedLineEnd();
1643 parser.skipToLineStart();
1644 }
1645 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001646 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001647 comment = &markupChild->fTokens.back();
1648 comment->fTerminator = end;
1649 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1650 return false;
1651 }
1652 parser.skipWhiteSpace();
1653 }
1654 parser.skipWhiteSpace();
1655 const char* memberStart = parser.fChar;
1656 if ('}' == memberStart[0]) {
1657 break;
1658 }
Cary Clark9174bda2017-09-19 17:39:32 -04001659 // if there's comment on same the line as member def, output first as if it was before
1660
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001661 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001662 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001663 if (parser.eof() || !parser.skipWhiteSpace()) {
1664 return this->reportError<bool>("enum member must end with comma 1");
1665 }
Cary Clark8032b982017-07-28 11:04:54 -04001666 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001667 if ('=' == parser.peek()) {
1668 parser.skipToEndBracket(',');
1669 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001670 if (!parser.eof() && '#' == parser.peek()) {
1671 // fixme: handle preprecessor, but just skip it for now
1672 continue;
1673 }
Cary Clark9174bda2017-09-19 17:39:32 -04001674 if (parser.eof() || ',' != parser.peek()) {
1675 return this->reportError<bool>("enum member must end with comma 2");
1676 }
1677 dataEnd = parser.fChar;
1678 const char* start = parser.anyOf("/\n");
1679 SkASSERT(start);
1680 parser.skipTo(start);
1681 if ('/' == parser.next()) {
1682 char slashStar = parser.next();
1683 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001684 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001685 char doxCheck = parser.next();
1686 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1687 save.restore();
1688 }
1689 }
1690 parser.skipWhiteSpace();
1691 const char* commentStart = parser.fChar;
1692 if ('/' == slashStar) {
1693 parser.skipToEndBracket('\n');
1694 } else {
1695 parser.skipToEndBracket("*/");
1696 }
1697 SkASSERT(!parser.eof());
1698 const char* commentEnd = parser.fChar;
1699 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001700 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001701 comment = &markupChild->fTokens.back();
1702 comment->fTerminator = commentEnd;
1703 }
Cary Clark8032b982017-07-28 11:04:54 -04001704 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001705 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001706 Definition* member = &markupChild->fTokens.back();
1707 member->fName = memberName;
1708 if (comment) {
1709 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001710 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001711 }
1712 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001713 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001714 for (auto outsideMember : child->fChildren) {
1715 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001716 continue;
1717 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001718 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1719 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001720 continue;
1721 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001722 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1723 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001724 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001725 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001726 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001727 // FIXME: ? add comment as well ?
1728 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001729 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001730 if (markupDef) {
1731 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1732 SkASSERT(classDef.fStart);
1733 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1734 markupChild->fName = uniqueName;
1735 classDef.fEnums[uniqueName] = markupChild;
1736 }
Cary Clark8032b982017-07-28 11:04:54 -04001737 return true;
1738}
1739
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001740bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001741 fParent = &fIncludeMap[name];
1742 fParent->fName = name;
1743 fParent->fFileName = fFileName;
1744 fParent->fType = Definition::Type::kFileType;
1745 fParent->fContentStart = fChar;
1746 fParent->fContentEnd = fEnd;
1747 // parse include file into tree
1748 while (fChar < fEnd) {
1749 if (!this->parseChar()) {
1750 return false;
1751 }
1752 }
1753 // parse tree and add named objects to maps
1754 fParent = &fIncludeMap[name];
1755 if (!this->parseObjects(fParent, nullptr)) {
1756 return false;
1757 }
1758 return true;
1759}
1760
1761bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1762 const char* typeStart = child->fChildren[0]->fContentStart;
1763 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001764 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001765 Definition* markupChild = &markupDef->fTokens.back();
1766 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001767 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001768 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1769 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1770 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1771 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001772 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001773 classDef.fMembers[uniqueName] = markupChild;
1774 if (child->fParentIndex >= 2) {
1775 auto comment = child->fParent->fTokens.begin();
1776 std::advance(comment, child->fParentIndex - 2);
1777 if (Definition::Type::kBracket == comment->fType
1778 && (Bracket::kSlashStar == comment->fBracket
1779 || Bracket::kSlashSlash == comment->fBracket)) {
1780 TextParser parser(&*comment);
1781 do {
1782 parser.skipToAlpha();
1783 if (parser.eof()) {
1784 break;
1785 }
1786 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001787 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001788 if (Bracket::kSlashStar == comment->fBracket) {
1789 const char* commentEnd = parser.strnstr("*/", end);
1790 if (commentEnd) {
1791 end = commentEnd;
1792 }
1793 }
1794 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001795 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001796 Definition* commentChild = &markupDef->fTokens.back();
1797 markupChild->fChildren.emplace_back(commentChild);
1798 parser.skipTo(end);
1799 } while (!parser.eof());
1800 }
1801 }
1802 return true;
1803}
1804
1805bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1806 auto tokenIter = child->fParent->fTokens.begin();
1807 std::advance(tokenIter, child->fParentIndex);
1808 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001809 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001810 bool addConst = false;
1811 auto operatorCheck = tokenIter;
1812 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1813 operatorCheck = std::prev(tokenIter);
1814 }
1815 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001816 auto closeParen = std::next(tokenIter);
1817 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1818 '(' == closeParen->fContentStart[0]);
1819 nameEnd = closeParen->fContentEnd + 1;
1820 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001821 if (Definition::Type::kKeyWord == closeParen->fType &&
1822 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001823 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001824 }
Cary Clarka560c472017-11-27 10:44:06 -05001825 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001826 }
1827 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001828 if (addConst) {
1829 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001830 }
Cary Clark8032b982017-07-28 11:04:54 -04001831 while (tokenIter != child->fParent->fTokens.begin()) {
1832 auto testIter = std::prev(tokenIter);
1833 switch (testIter->fType) {
1834 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001835 if (testIter == child->fParent->fTokens.begin() &&
1836 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1837 KeyWord::kIfndef == child->fParent->fKeyWord ||
1838 KeyWord::kIf == child->fParent->fKeyWord)) {
1839 std::next(tokenIter);
1840 break;
1841 }
Cary Clark8032b982017-07-28 11:04:54 -04001842 goto keepGoing;
1843 case Definition::Type::kKeyWord: {
1844 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1845 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1846 goto keepGoing;
1847 }
1848 } break;
1849 case Definition::Type::kBracket:
1850 if (Bracket::kAngle == testIter->fBracket) {
1851 goto keepGoing;
1852 }
1853 break;
1854 case Definition::Type::kPunctuation:
1855 if (Punctuation::kSemicolon == testIter->fPunctuation
1856 || Punctuation::kLeftBrace == testIter->fPunctuation
1857 || Punctuation::kColon == testIter->fPunctuation) {
1858 break;
1859 }
1860 keepGoing:
1861 tokenIter = testIter;
1862 continue;
1863 default:
1864 break;
1865 }
1866 break;
1867 }
1868 tokenIter->fName = nameStr;
1869 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001870 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001871 auto testIter = child->fParent->fTokens.begin();
1872 SkASSERT(child->fParentIndex > 0);
1873 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001874 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1875 0 == tokenIter->fParentIndex) {
1876 tokenIter = std::next(tokenIter);
1877 }
Cary Clark8032b982017-07-28 11:04:54 -04001878 const char* start = tokenIter->fContentStart;
1879 const char* end = tokenIter->fContentEnd;
1880 const char kDebugCodeStr[] = "SkDEBUGCODE";
1881 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1882 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1883 std::advance(testIter, 1);
1884 start = testIter->fContentStart + 1;
1885 end = testIter->fContentEnd - 1;
1886 } else {
1887 end = testIter->fContentEnd;
1888 while (testIter != child->fParent->fTokens.end()) {
1889 testIter = std::next(testIter);
1890 switch (testIter->fType) {
1891 case Definition::Type::kPunctuation:
1892 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1893 || Punctuation::kLeftBrace == testIter->fPunctuation
1894 || Punctuation::kColon == testIter->fPunctuation);
1895 end = testIter->fStart;
1896 break;
1897 case Definition::Type::kKeyWord: {
1898 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1899 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1900 continue;
1901 }
1902 } break;
1903 default:
1904 continue;
1905 }
1906 break;
1907 }
1908 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001909 while (end > start && ' ' >= end[-1]) {
1910 --end;
1911 }
Cary Clark9174bda2017-09-19 17:39:32 -04001912 if (!markupDef) {
1913 auto parentIter = child->fParent->fTokens.begin();
1914 SkASSERT(child->fParentIndex > 0);
1915 std::advance(parentIter, child->fParentIndex - 1);
1916 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001917 TextParser nameParser(methodName);
1918 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001919 return true; // expect this is inline class definition outside of class
1920 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001921 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1922 fParent, '\0');
1923 Definition* globalMarkupChild = &fGlobals.back();
1924 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1925 globalMarkupChild->fName = globalUniqueName;
1926 if (!this->findComments(*child, globalMarkupChild)) {
1927 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001928 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001929 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001930 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001931 }
Cary Clark8032b982017-07-28 11:04:54 -04001932 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001933 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001934 Definition* markupChild = &markupDef->fTokens.back();
1935 // do find instead -- I wonder if there is a way to prevent this in c++
1936 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1937 SkASSERT(classDef.fStart);
1938 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1939 markupChild->fName = uniqueName;
1940 if (!this->findComments(*child, markupChild)) {
1941 return false;
1942 }
1943 classDef.fMethods[uniqueName] = markupChild;
1944 return true;
1945}
1946
Cary Clark8032b982017-07-28 11:04:54 -04001947bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04001948 fPriorObject = nullptr;
1949 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04001950 if (!this->parseObject(child, markupDef)) {
1951 return false;
1952 }
Cary Clark0d225392018-06-07 09:59:07 -04001953 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001954 }
1955 return true;
1956}
1957
1958bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1959 // set up for error reporting
1960 fLine = fChar = child->fStart;
1961 fEnd = child->fContentEnd;
1962 // todo: put original line number in child as well
1963 switch (child->fType) {
1964 case Definition::Type::kKeyWord:
1965 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001966 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001967 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001968 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001969 }
1970 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001971 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04001972 case KeyWord::kConst:
1973 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04001974 if (!this->parseConst(child, markupDef)) {
1975 return child->reportError<bool>("failed to parse const or constexpr");
1976 }
1977 break;
Cary Clark8032b982017-07-28 11:04:54 -04001978 case KeyWord::kEnum:
1979 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001980 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001981 }
1982 break;
1983 case KeyWord::kStruct:
1984 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001985 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001986 }
1987 break;
1988 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001989 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001990 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001991 }
1992 break;
1993 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001994 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001995 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001996 }
1997 break;
1998 case KeyWord::kUnion:
1999 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002000 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002001 }
2002 break;
2003 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002004 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002005 }
2006 break;
2007 case Definition::Type::kBracket:
2008 switch (child->fBracket) {
2009 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04002010 if (fLastObject) {
2011 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
2012 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04002013 if (!checkDeprecated.eof()) {
2014 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002015 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2016 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002017 break;
2018 }
2019 }
2020 }
2021 {
2022 auto tokenIter = child->fParent->fTokens.begin();
2023 std::advance(tokenIter, child->fParentIndex);
2024 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002025 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002026 if (previousToken.startsWith(gAttrDeprecated)) {
2027 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002028 break;
2029 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002030 if (Bracket::kPound == child->fParent->fBracket &&
2031 KeyWord::kIf == child->fParent->fKeyWord) {
2032 // TODO: this will skip methods named defined() -- for the
2033 // moment there aren't any
2034 if (previousToken.startsWith("defined")) {
2035 break;
2036 }
2037 }
Cary Clark73fa9722017-08-29 17:36:51 -04002038 }
Cary Clark0d225392018-06-07 09:59:07 -04002039 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2040 break;
2041 }
Cary Clark8032b982017-07-28 11:04:54 -04002042 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002043 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002044 }
Cary Clark89b14562018-03-19 09:04:10 -04002045 if (fAttrDeprecated) {
2046 Definition* lastMethod = &markupDef->fTokens.back();
2047 lastMethod->fDeprecated = true;
2048 fAttrDeprecated = nullptr;
2049 }
Cary Clark73fa9722017-08-29 17:36:51 -04002050 break;
Cary Clark8032b982017-07-28 11:04:54 -04002051 case Bracket::kSlashSlash:
2052 case Bracket::kSlashStar:
2053 // comments are picked up by parsing objects first
2054 break;
2055 case Bracket::kPound:
2056 // special-case the #xxx xxx_DEFINED entries
2057 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002058 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002059 case KeyWord::kIfndef:
2060 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002061 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002062 if (!this->parseObjects(child, markupDef)) {
2063 return false;
2064 }
2065 break;
2066 }
2067 goto preproError;
2068 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002069 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002070 break;
2071 }
2072 goto preproError;
2073 case KeyWord::kEndif:
2074 if (child->boilerplateEndIf()) {
2075 break;
2076 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002077 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002078 case KeyWord::kInclude:
2079 // ignored for now
2080 break;
2081 case KeyWord::kElse:
2082 case KeyWord::kElif:
2083 // todo: handle these
2084 break;
2085 default:
2086 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002087 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002088 }
2089 break;
2090 case Bracket::kAngle:
2091 // pick up templated function pieces when method is found
2092 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002093 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002094 if (!this->parseObjects(child, markupDef)) {
2095 return false;
2096 }
Cary Clark73fa9722017-08-29 17:36:51 -04002097 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002098 case Bracket::kSquare: {
2099 // check to see if parent is operator, the only case we handle so far
2100 auto prev = child->fParent->fTokens.begin();
2101 std::advance(prev, child->fParentIndex - 1);
2102 if (KeyWord::kOperator != prev->fKeyWord) {
2103 return child->reportError<bool>("expected operator overload");
2104 }
2105 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002106 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002107 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002108 }
2109 break;
2110 case Definition::Type::kWord:
2111 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002112 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002113 }
2114 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002115 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002116 }
2117 break;
2118 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002119 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002120 break;
2121 }
2122 return true;
2123}
2124
Cary Clarkbbfda252018-03-09 15:32:01 -05002125bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2126 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002127}
2128
Cary Clark2f466242017-12-11 16:03:17 -05002129bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2130 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002131 typedefParser.skipExact("typedef");
2132 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002133 string nameStr = typedefParser.typedefName();
2134 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002135 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2136 child->fLineCount, fParent, '\0');
2137 Definition* globalMarkupChild = &fGlobals.back();
2138 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2139 globalMarkupChild->fName = globalUniqueName;
2140 if (!this->findComments(*child, globalMarkupChild)) {
2141 return false;
2142 }
2143 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002144 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002145 return true;
2146 }
2147 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002148 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002149 Definition* markupChild = &markupDef->fTokens.back();
2150 markupChild->fName = nameStr;
2151 markupChild->fTerminator = markupChild->fContentEnd;
2152 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2153 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002154 child->fName = markupDef->fName + "::" + nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002155 return true;
2156}
2157
2158bool IncludeParser::parseUnion() {
2159
2160 return true;
2161}
2162
2163bool IncludeParser::parseChar() {
2164 char test = *fChar;
2165 if ('\\' == fPrev) {
2166 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002167// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002168 fLine = fChar + 1;
2169 }
2170 goto done;
2171 }
2172 switch (test) {
2173 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002174// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002175 fLine = fChar + 1;
2176 if (fInChar) {
2177 return reportError<bool>("malformed char");
2178 }
2179 if (fInString) {
2180 return reportError<bool>("malformed string");
2181 }
2182 if (!this->checkForWord()) {
2183 return false;
2184 }
2185 if (Bracket::kPound == this->topBracket()) {
2186 KeyWord keyWord = fParent->fKeyWord;
2187 if (KeyWord::kNone == keyWord) {
2188 return this->reportError<bool>("unhandled preprocessor directive");
2189 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002190 if (fInDefine) {
2191 SkASSERT(KeyWord::kDefine == keyWord);
2192 fInDefine = false;
2193 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002194 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002195 this->popBracket();
2196 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002197 if (fInBrace) {
2198 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2199 fInBrace = nullptr;
2200 }
Cary Clark8032b982017-07-28 11:04:54 -04002201 } else if (Bracket::kSlashSlash == this->topBracket()) {
2202 this->popBracket();
2203 }
2204 break;
2205 case '*':
2206 if (!fInCharCommentString && '/' == fPrev) {
2207 this->pushBracket(Bracket::kSlashStar);
2208 }
2209 if (!this->checkForWord()) {
2210 return false;
2211 }
2212 if (!fInCharCommentString) {
2213 this->addPunctuation(Punctuation::kAsterisk);
2214 }
2215 break;
2216 case '/':
2217 if ('*' == fPrev) {
2218 if (!fInCharCommentString) {
2219 return reportError<bool>("malformed closing comment");
2220 }
2221 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002222 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002223 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002224 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002225 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002226 }
2227 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002228 }
Cary Clark8032b982017-07-28 11:04:54 -04002229 if (!fInCharCommentString && '/' == fPrev) {
2230 this->pushBracket(Bracket::kSlashSlash);
2231 break;
2232 }
2233 if (!this->checkForWord()) {
2234 return false;
2235 }
2236 break;
2237 case '\'':
2238 if (Bracket::kChar == this->topBracket()) {
2239 this->popBracket();
2240 } else if (!fInComment && !fInString) {
2241 if (fIncludeWord) {
2242 return this->reportError<bool>("word then single-quote");
2243 }
2244 this->pushBracket(Bracket::kChar);
2245 }
2246 break;
2247 case '\"':
2248 if (Bracket::kString == this->topBracket()) {
2249 this->popBracket();
2250 } else if (!fInComment && !fInChar) {
2251 if (fIncludeWord) {
2252 return this->reportError<bool>("word then double-quote");
2253 }
2254 this->pushBracket(Bracket::kString);
2255 }
2256 break;
Cary Clark8032b982017-07-28 11:04:54 -04002257 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002258 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002259 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2260 this->pushBracket(Bracket::kDebugCode);
2261 break;
2262 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002263 case ':':
2264 case '[':
2265 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002266 if (fInCharCommentString) {
2267 break;
2268 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002269 if (fInDefine && fInBrace) {
2270 break;
2271 }
Cary Clark8032b982017-07-28 11:04:54 -04002272 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2273 break;
2274 }
Cary Clark0d225392018-06-07 09:59:07 -04002275 if (fConstExpr) {
2276 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2277 fConstExpr = nullptr;
2278 }
Cary Clark8032b982017-07-28 11:04:54 -04002279 if (!fInBrace) {
2280 if (!this->checkForWord()) {
2281 return false;
2282 }
2283 if (':' == test && !fInFunction) {
2284 break;
2285 }
2286 if ('{' == test) {
2287 this->addPunctuation(Punctuation::kLeftBrace);
2288 } else if (':' == test) {
2289 this->addPunctuation(Punctuation::kColon);
2290 }
2291 }
2292 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2293 && Bracket::kColon == fInBrace->fBracket) {
2294 Definition* braceParent = fParent->fParent;
2295 braceParent->fChildren.pop_back();
2296 braceParent->fTokens.pop_back();
2297 fParent = braceParent;
2298 fInBrace = nullptr;
2299 }
2300 this->pushBracket(
2301 '(' == test ? Bracket::kParen :
2302 '[' == test ? Bracket::kSquare :
2303 '{' == test ? Bracket::kBrace :
2304 Bracket::kColon);
2305 if (!fInBrace
2306 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2307 && fInFunction) {
2308 fInBrace = fParent;
2309 }
2310 } break;
2311 case '<':
2312 if (fInCharCommentString || fInBrace) {
2313 break;
2314 }
2315 if (!this->checkForWord()) {
2316 return false;
2317 }
2318 if (fInEnum) {
2319 break;
2320 }
2321 this->pushBracket(Bracket::kAngle);
2322 break;
2323 case ')':
2324 case ']':
2325 case '}': {
2326 if (fInCharCommentString) {
2327 break;
2328 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002329 if (fInDefine && fInBrace) {
2330 break;
2331 }
Cary Clark8032b982017-07-28 11:04:54 -04002332 if (!fInBrace) {
2333 if (!this->checkForWord()) {
2334 return false;
2335 }
2336 }
2337 bool popBraceParent = fInBrace == fParent;
2338 if ((')' == test ? Bracket::kParen :
2339 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2340 this->popBracket();
2341 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002342 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002343 } else {
2344 fInFunction = '}' != test;
2345 }
Cary Clark73fa9722017-08-29 17:36:51 -04002346 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2347 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002348 } else {
2349 return reportError<bool>("malformed close bracket");
2350 }
2351 if (popBraceParent) {
2352 Definition* braceParent = fInBrace->fParent;
2353 braceParent->fChildren.pop_back();
2354 braceParent->fTokens.pop_back();
2355 fInBrace = nullptr;
2356 }
2357 } break;
2358 case '>':
2359 if (fInCharCommentString || fInBrace) {
2360 break;
2361 }
2362 if (!this->checkForWord()) {
2363 return false;
2364 }
2365 if (fInEnum) {
2366 break;
2367 }
Cary Clarka560c472017-11-27 10:44:06 -05002368 if (Bracket::kPound == this->topBracket()) {
2369 break;
2370 }
Cary Clark8032b982017-07-28 11:04:54 -04002371 if (Bracket::kAngle == this->topBracket()) {
2372 this->popBracket();
2373 } else {
2374 return reportError<bool>("malformed close angle bracket");
2375 }
2376 break;
2377 case '#': {
2378 if (fInCharCommentString || fInBrace) {
2379 break;
2380 }
2381 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2382 this->pushBracket(Bracket::kPound);
2383 break;
2384 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002385 case ' ':
2386 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2387 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2388 fInBrace = fParent;
2389 // delimiting brackets are space ... unescaped-linefeed
2390 }
Cary Clark8032b982017-07-28 11:04:54 -04002391 case '&':
2392 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002393 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05002394 case '-':
2395 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002396 if (fInCharCommentString || fInBrace) {
2397 break;
2398 }
2399 if (!this->checkForWord()) {
2400 return false;
2401 }
2402 break;
Cary Clark0d225392018-06-07 09:59:07 -04002403 case '=':
2404 if (fInCharCommentString || fInBrace) {
2405 break;
2406 }
2407 if (!this->checkForWord()) {
2408 return false;
2409 }
2410 {
2411 const Definition& lastToken = fParent->fTokens.back();
2412 if (lastToken.fType != Definition::Type::kWord) {
2413 break;
2414 }
2415 string name(lastToken.fContentStart, lastToken.length());
2416 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
2417 break;
2418 }
2419 // find token on start of line
2420 auto lineIter = fParent->fTokens.end();
2421 do {
2422 --lineIter;
2423 } while (lineIter->fContentStart > fLine);
2424 if (lineIter->fContentStart < fLine) {
2425 ++lineIter;
2426 }
2427 Definition* lineStart = &*lineIter;
2428 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
2429 bool sawConst = false;
2430 bool sawStatic = false;
2431 bool sawTemplate = false;
2432 bool sawType = false;
2433 while (&lastToken != &*lineIter) {
2434 if (KeyWord::kTemplate == lineIter->fKeyWord) {
2435 if (sawConst || sawStatic || sawTemplate) {
2436 sawConst = false;
2437 break;
2438 }
2439 if (&lastToken == &*++lineIter) {
2440 break;
2441 }
2442 if (KeyWord::kTypename != lineIter->fKeyWord) {
2443 break;
2444 }
2445 if (&lastToken == &*++lineIter) {
2446 break;
2447 }
2448 if (Definition::Type::kWord != lineIter->fType) {
2449 break;
2450 }
2451 sawTemplate = true;
2452 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
2453 if (sawConst || sawStatic) {
2454 sawConst = false;
2455 break;
2456 }
2457 sawStatic = true;
2458 } else if (KeyWord::kConst == lineIter->fKeyWord
2459 || KeyWord::kConstExpr == lineIter->fKeyWord) {
2460 if (sawConst) {
2461 sawConst = false;
2462 break;
2463 }
2464 sawConst = true;
2465 } else {
2466 if (sawType) {
2467 sawType = false;
2468 break;
2469 }
2470 if (Definition::Type::kKeyWord == lineIter->fType
2471 && KeyProperty::kNumber
2472 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
2473 sawType = true;
2474 } else if (Definition::Type::kWord == lineIter->fType) {
2475 string typeName(lineIter->fContentStart, lineIter->length());
2476 if ("Sk" != name.substr(0, 2)) {
2477 sawType = true;
2478 }
2479 }
2480 }
2481 ++lineIter;
2482 }
2483 if (sawType && sawConst) {
2484 // if found, name first
2485 lineStart->fName = name;
2486 lineStart->fMarkType = MarkType::kConst;
2487 fParent->fChildren.emplace_back(lineStart);
2488 fConstExpr = lineStart;
2489 }
2490 }
2491 break;
Cary Clark8032b982017-07-28 11:04:54 -04002492 case ';':
2493 if (fInCharCommentString || fInBrace) {
2494 break;
2495 }
2496 if (!this->checkForWord()) {
2497 return false;
2498 }
Cary Clark0d225392018-06-07 09:59:07 -04002499 if (fConstExpr) {
2500 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2501 fConstExpr = nullptr;
2502 }
Cary Clark8032b982017-07-28 11:04:54 -04002503 if (Definition::Type::kKeyWord == fParent->fType
2504 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002505 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2506 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002507 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2508 this->popObject();
2509 }
Cary Clark8032b982017-07-28 11:04:54 -04002510 if (KeyWord::kEnum == fParent->fKeyWord) {
2511 fInEnum = false;
2512 }
2513 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002514 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2515 this->popObject();
2516 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002517 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002518 } else if (Definition::Type::kBracket == fParent->fType
2519 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2520 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2521 list<Definition>::iterator baseIter = fParent->fTokens.end();
2522 list<Definition>::iterator namedIter = fParent->fTokens.end();
2523 for (auto tokenIter = fParent->fTokens.end();
2524 fParent->fTokens.begin() != tokenIter--; ) {
2525 if (tokenIter->fLineCount == fLineCount) {
2526 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2527 if (namedIter != fParent->fTokens.end()) {
2528 return reportError<bool>("found two named member tokens");
2529 }
2530 namedIter = tokenIter;
2531 }
2532 baseIter = tokenIter;
2533 } else {
2534 break;
2535 }
2536 }
2537 // FIXME: if a member definition spans multiple lines, this won't work
2538 if (namedIter != fParent->fTokens.end()) {
2539 if (baseIter == namedIter) {
2540 return this->reportError<bool>("expected type before named token");
2541 }
2542 Definition* member = &*namedIter;
2543 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002544 if (!member->fTerminator) {
2545 member->fTerminator = member->fContentEnd;
2546 }
Cary Clark8032b982017-07-28 11:04:54 -04002547 fParent->fChildren.push_back(member);
2548 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2549 member->fChildren.push_back(&*nameType);
2550 }
Cary Clark8032b982017-07-28 11:04:54 -04002551 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002552 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002553 } else if (fParent->fChildren.size() > 0) {
2554 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002555 Definition* priorEnum = fPriorEnum;
2556 fPriorEnum = nullptr;
2557 if (!priorEnum) {
2558 while (fParent->fChildren.begin() != lastIter) {
2559 std::advance(lastIter, -1);
2560 priorEnum = *lastIter;
2561 if (Definition::Type::kBracket != priorEnum->fType ||
2562 (Bracket::kSlashSlash != priorEnum->fBracket
2563 && Bracket::kSlashStar != priorEnum->fBracket)) {
2564 break;
2565 }
Cary Clark8032b982017-07-28 11:04:54 -04002566 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002567 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002568 }
2569 if (Definition::Type::kKeyWord == priorEnum->fType
2570 && KeyWord::kEnum == priorEnum->fKeyWord) {
2571 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002572 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002573 while (tokenWalker != fParent->fTokens.end()) {
2574 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002575 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002576 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2577 break;
2578 }
2579 }
2580 while (tokenWalker != fParent->fTokens.end()) {
2581 std::advance(tokenWalker, 1);
2582 const Definition* test = &*tokenWalker;
2583 if (Definition::Type::kBracket != test->fType ||
2584 (Bracket::kSlashSlash != test->fBracket
2585 && Bracket::kSlashStar != test->fBracket)) {
2586 break;
2587 }
2588 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002589 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002590 Definition* start = &*tokenWalker;
2591 bool foundExpected = true;
2592 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2593 const Definition* test = &*tokenWalker;
2594 if (expected != test->fKeyWord) {
2595 foundExpected = false;
2596 break;
2597 }
2598 if (tokenWalker == fParent->fTokens.end()) {
2599 break;
2600 }
2601 std::advance(tokenWalker, 1);
2602 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002603 if (!foundExpected) {
2604 foundExpected = true;
2605 tokenWalker = saveTokenWalker;
2606 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2607 const Definition* test = &*tokenWalker;
2608 if (expected != test->fKeyWord) {
2609 foundExpected = false;
2610 break;
2611 }
2612 if (tokenWalker == fParent->fTokens.end()) {
2613 break;
2614 }
2615 if (KeyWord::kNone != expected) {
2616 std::advance(tokenWalker, 1);
2617 }
2618 }
2619 if (foundExpected) {
2620 auto nameToken = priorEnum->fTokens.begin();
2621 string enumName = string(nameToken->fContentStart,
2622 nameToken->fContentEnd - nameToken->fContentStart);
2623 const Definition* test = &*tokenWalker;
2624 string constType = string(test->fContentStart,
2625 test->fContentEnd - test->fContentStart);
2626 if (enumName != constType) {
2627 foundExpected = false;
2628 } else {
2629 std::advance(tokenWalker, 1);
2630 }
2631 }
2632 }
Cary Clark8032b982017-07-28 11:04:54 -04002633 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2634 const char* nameStart = tokenWalker->fStart;
2635 std::advance(tokenWalker, 1);
2636 if (tokenWalker != fParent->fTokens.end()) {
2637 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002638 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002639 start->fName = string(nameStart, tp.fChar - nameStart);
2640 start->fContentEnd = fChar;
2641 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002642 fPriorEnum = priorEnum;
2643 }
Cary Clark8032b982017-07-28 11:04:54 -04002644 }
2645 }
2646 }
2647 this->addPunctuation(Punctuation::kSemicolon);
2648 fInFunction = false;
2649 break;
2650 case '~':
2651 if (fInEnum) {
2652 break;
2653 }
2654 case '0': case '1': case '2': case '3': case '4':
2655 case '5': case '6': case '7': case '8': case '9':
2656 // TODO: don't want to parse numbers, but do need to track for enum defs
2657 // break;
2658 case 'A': case 'B': case 'C': case 'D': case 'E':
2659 case 'F': case 'G': case 'H': case 'I': case 'J':
2660 case 'K': case 'L': case 'M': case 'N': case 'O':
2661 case 'P': case 'Q': case 'R': case 'S': case 'T':
2662 case 'U': case 'V': case 'W': case 'X': case 'Y':
2663 case 'Z': case '_':
2664 case 'a': case 'b': case 'c': case 'd': case 'e':
2665 case 'f': case 'g': case 'h': case 'i': case 'j':
2666 case 'k': case 'l': case 'm': case 'n': case 'o':
2667 case 'p': case 'q': case 'r': case 's': case 't':
2668 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002669 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002670 if (fInCharCommentString || fInBrace) {
2671 break;
2672 }
2673 if (!fIncludeWord) {
2674 fIncludeWord = fChar;
2675 }
2676 break;
2677 }
2678done:
2679 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002680 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002681 return true;
2682}
2683
2684void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04002685 IncludeParser::ValidateKeyWords();
2686}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002687
Cary Clark186d08f2018-04-03 08:43:27 -04002688bool IncludeParser::references(const SkString& file) const {
2689 // if includes weren't passed one at a time, assume all references are valid
2690 if (fIncludeMap.empty()) {
2691 return true;
2692 }
2693 SkASSERT(file.endsWith(".bmh") );
2694 string root(file.c_str(), file.size() - 4);
2695 string kReference("_Reference");
2696 if (string::npos != root.find(kReference)) {
2697 root = root.substr(0, root.length() - kReference.length());
2698 }
2699 if (fIClassMap.end() != fIClassMap.find(root)) {
2700 return true;
2701 }
2702 if (fIStructMap.end() != fIStructMap.find(root)) {
2703 return true;
2704 }
2705 // TODO incomplete: probably need to look in other places for class-less includes like SkColor.h
2706 return false;
2707}
2708
Cary Clark2dc84ad2018-01-26 12:56:22 -05002709void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2710 if (!sk_isdir(includes)) {
2711 IncludeParser::RemoveOneFile(docs, includes);
2712 } else {
2713 SkOSFile::Iter it(includes, ".h");
2714 for (SkString file; it.next(&file); ) {
2715 SkString p = SkOSPath::Join(includes, file.c_str());
2716 const char* hunk = p.c_str();
2717 if (!SkStrEndsWith(hunk, ".h")) {
2718 continue;
2719 }
2720 IncludeParser::RemoveOneFile(docs, hunk);
2721 }
2722 }
2723}
2724
2725void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2726 const char* lastForward = strrchr(includesFile, '/');
2727 const char* lastBackward = strrchr(includesFile, '\\');
2728 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2729 if (!last) {
2730 last = includesFile;
2731 } else {
2732 last += 1;
2733 }
2734 SkString baseName(last);
2735 SkASSERT(baseName.endsWith(".h"));
2736 baseName.remove(baseName.size() - 2, 2);
2737 baseName.append("_Reference.bmh");
2738 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2739 remove(fullName.c_str());
2740}