blob: d6aa447fc40f86e7690756a8b4a466d7373c8838 [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();
Cary Clarkb7a72a52018-06-15 05:11:24 -04001371 while ((*childIter)->fPrivate) {
1372 std::advance(childIter, 1);
1373 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001374 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001375 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001376 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
Cary Clark137b8742018-05-30 09:21:49 -04001377 iter->fPrivate = KeyWord::kPublic != currentKey;
Cary Clark884dd7d2017-10-11 10:37:52 -04001378 const char* testStart = iter->fStart;
1379 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1380 iter = std::next(iter);
1381 ++keyIndex;
1382 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1383 currentKey = KeyWord::kPublic;
1384 break;
1385 }
1386 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1387 currentKey = KeyWord::kProtected;
1388 break;
1389 }
1390 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1391 currentKey = KeyWord::kPrivate;
1392 break;
1393 }
1394 }
1395 fLastObject = nullptr;
1396 if (KeyWord::kPublic == currentKey) {
1397 if (!this->parseObject(child, markupDef)) {
1398 return false;
1399 }
Cary Clark8032b982017-07-28 11:04:54 -04001400 }
Cary Clark73fa9722017-08-29 17:36:51 -04001401 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001402 childIter = std::next(childIter);
1403 }
Cary Clark137b8742018-05-30 09:21:49 -04001404 while (iter != includeDef->fTokens.end()) {
1405 iter->fPrivate = KeyWord::kPublic != currentKey;
1406 iter = std::next(iter);
1407 }
Cary Clark8032b982017-07-28 11:04:54 -04001408 SkASSERT(fParent->fParent);
1409 fParent = fParent->fParent;
1410 return true;
1411}
1412
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001413bool IncludeParser::parseComment(string filename, const char* start, const char* end,
Cary Clark8032b982017-07-28 11:04:54 -04001414 int lineCount, Definition* markupDef) {
1415 TextParser parser(filename, start, end, lineCount);
1416 // parse doxygen if present
1417 if (parser.startsWith("**")) {
1418 parser.next();
1419 parser.next();
1420 parser.skipWhiteSpace();
1421 if ('\\' == parser.peek()) {
1422 parser.next();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001423 // Doxygen tag may be "file" or "fn" in addition to "class", "enum", "struct"
1424 if (parser.skipExact("file")) {
1425 if (Definition::Type::kFileType != fParent->fType) {
1426 return reportError<bool>("expected parent is file");
1427 }
1428 string filename = markupDef->fileName();
1429 if (!parser.skipWord(filename.c_str())) {
1430 return reportError<bool>("missing object type");
1431 }
1432 } else if (parser.skipExact("fn")) {
1433 SkASSERT(0); // incomplete
1434 } else {
1435 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1436 return reportError<bool>("missing object type");
1437 }
1438 if (!parser.skipWord(markupDef->fName.c_str()) &&
1439 KeyWord::kEnum != markupDef->fKeyWord) {
1440 return reportError<bool>("missing object name");
1441 }
Cary Clark8032b982017-07-28 11:04:54 -04001442 }
Cary Clark8032b982017-07-28 11:04:54 -04001443 }
1444 }
1445 // remove leading '*' if present
1446 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1447 while (!parser.eof() && parser.skipWhiteSpace()) {
1448 while ('*' == parser.peek()) {
1449 parser.next();
1450 if (parser.eof()) {
1451 break;
1452 }
1453 parser.skipWhiteSpace();
1454 }
1455 if (parser.eof()) {
1456 break;
1457 }
1458 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001459 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001460 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001461 parser.skipToEndBracket('\n');
1462 }
1463 return true;
1464}
1465
Cary Clarkd98f78c2018-04-26 08:32:37 -04001466bool IncludeParser::parseConst(Definition* child, Definition* markupDef) {
Cary Clarkd98f78c2018-04-26 08:32:37 -04001467 if (!markupDef) {
1468 fGlobals.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1469 child->fLineCount, fParent, '\0');
1470 Definition* globalMarkupChild = &fGlobals.back();
Cary Clark0d225392018-06-07 09:59:07 -04001471 string globalUniqueName = this->uniqueName(fIConstMap, child->fName);
Cary Clarkd98f78c2018-04-26 08:32:37 -04001472 globalMarkupChild->fName = globalUniqueName;
1473 if (!this->findComments(*child, globalMarkupChild)) {
1474 return false;
1475 }
1476 fIConstMap[globalUniqueName] = globalMarkupChild;
1477 return true;
1478 }
1479 markupDef->fTokens.emplace_back(MarkType::kConst, child->fContentStart, child->fContentEnd,
1480 child->fLineCount, markupDef, '\0');
1481 Definition* markupChild = &markupDef->fTokens.back();
Cary Clark0d225392018-06-07 09:59:07 -04001482 markupChild->fName = child->fName;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001483 markupChild->fTerminator = markupChild->fContentEnd;
1484 IClassDefinition& classDef = fIClassMap[markupDef->fName];
Cary Clark0d225392018-06-07 09:59:07 -04001485 classDef.fConsts[child->fName] = markupChild;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001486 return true;
1487}
1488
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001489bool IncludeParser::parseDefine(Definition* child, Definition* markupDef) {
1490 TextParser parser(child);
1491 if (!parser.skipExact("#define")) {
1492 return false;
1493 }
1494 if (!parser.skipSpace()) {
1495 return false;
1496 }
1497 const char* nameStart = parser.fChar;
1498 parser.skipToNonAlphaNum(); // FIXME: just want to skip isalnum() and '_'
1499 if (parser.eof()) {
1500 return true; // do nothing if #define doesn't define anything
1501 }
1502 string nameStr(nameStart, parser.fChar - nameStart);
1503 struct Param {
1504 const char* fStart;
1505 const char* fEnd;
1506 };
1507 vector<Param> params;
1508 if ('(' == parser.peek()) {
1509 parser.next();
1510 if (!parser.skipSpace()) {
1511 return false;
1512 }
1513 do {
1514 const char* paramStart = parser.fChar;
1515 if (!parser.skipExact("...")) {
1516 parser.skipToNonAlphaNum();
1517 }
1518 if (parser.eof()) {
1519 return false;
1520 }
1521 params.push_back({paramStart, parser.fChar});
1522 if (!parser.skipSpace()) {
1523 return false;
1524 }
1525 if (')' == parser.peek()) {
1526 parser.next();
1527 break;
1528 }
1529 if (',' != parser.next()) {
1530 return false;
1531 }
1532 if (!parser.skipSpace()) {
1533 return false;
1534 }
1535 } while (true);
1536 }
1537 if (!parser.skipSpace()) {
1538 return false;
1539 }
1540 if (!markupDef) {
1541 fGlobals.emplace_back(MarkType::kDefine, nameStart, child->fContentEnd,
1542 child->fLineCount, fParent, '\0');
1543 Definition* globalMarkupChild = &fGlobals.back();
1544 string globalUniqueName = this->uniqueName(fIDefineMap, nameStr);
1545 globalMarkupChild->fName = globalUniqueName;
1546 globalMarkupChild->fTerminator = child->fContentEnd;
1547 if (!this->findComments(*child, globalMarkupChild)) {
1548 return false;
1549 }
1550 fIDefineMap[globalUniqueName] = globalMarkupChild;
1551 for (Param param : params) {
1552 globalMarkupChild->fTokens.emplace_back(MarkType::kParam, param.fStart, param.fEnd,
1553 child->fLineCount, globalMarkupChild, '\0');
1554 Definition* paramChild = &globalMarkupChild->fTokens.back();
1555 paramChild->fName = string(param.fStart, param.fEnd - param.fStart);
1556 paramChild->fTerminator = param.fEnd;
1557 }
1558 return true;
1559 }
1560 markupDef->fTokens.emplace_back(MarkType::kDefine, child->fContentStart, child->fContentEnd,
1561 child->fLineCount, markupDef, '\0');
1562 Definition* markupChild = &markupDef->fTokens.back();
1563 markupChild->fName = nameStr;
1564 markupChild->fTerminator = markupChild->fContentEnd;
1565 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1566 if (!this->findComments(*child, markupChild)) {
1567 return false;
1568 }
1569 classDef.fDefines[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001570 return true;
1571}
1572
1573bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001574 TextParser parser(child);
1575 parser.skipToEndBracket('{');
1576 if (parser.eof()) {
1577 return true; // if enum is a forward declaration, do nothing
1578 }
1579 parser.next();
1580 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001581 if (child->fTokens.size() > 0) {
1582 auto token = child->fTokens.begin();
1583 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1584 token = token->fTokens.begin();
1585 }
1586 if (Definition::Type::kWord == token->fType) {
1587 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1588 }
1589 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001590 Definition* markupChild;
1591 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001592 fGlobals.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1593 child->fLineCount, fParent, '\0');
1594 markupChild = &fGlobals.back();
1595 string globalUniqueName = this->uniqueName(fIEnumMap, nameStr);
1596 markupChild->fName = globalUniqueName;
1597 markupChild->fTerminator = child->fContentEnd;
1598 fIEnumMap[globalUniqueName] = markupChild;
Cary Clark2dc84ad2018-01-26 12:56:22 -05001599 } else {
1600 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001601 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001602 markupChild = &markupDef->fTokens.back();
1603 }
Cary Clark8032b982017-07-28 11:04:54 -04001604 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1605 markupChild->fKeyWord = KeyWord::kEnum;
1606 TextParser enumName(child);
1607 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001608 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001609 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001610 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001611 markupChild->fMarkType = MarkType::kEnumClass;
1612 }
Cary Clark8032b982017-07-28 11:04:54 -04001613 const char* nameStart = enumName.fChar;
1614 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001615 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001616 markupChild->fName = markupDef->fName + "::" +
1617 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001618 }
Cary Clark8032b982017-07-28 11:04:54 -04001619 if (!this->findComments(*child, markupChild)) {
1620 return false;
1621 }
Cary Clark8032b982017-07-28 11:04:54 -04001622 const char* dataEnd;
1623 do {
Cary Clark8032b982017-07-28 11:04:54 -04001624 parser.skipWhiteSpace();
1625 if ('}' == parser.peek()) {
1626 break;
1627 }
1628 Definition* comment = nullptr;
1629 // note that comment, if any, can be before or after (on the same line, though) as member
1630 if ('#' == parser.peek()) {
1631 // fixme: handle preprecessor, but just skip it for now
1632 parser.skipToLineStart();
1633 }
1634 while (parser.startsWith("/*") || parser.startsWith("//")) {
1635 parser.next();
1636 const char* start = parser.fChar;
1637 const char* end;
1638 if ('*' == parser.peek()) {
1639 end = parser.strnstr("*/", parser.fEnd);
1640 parser.fChar = end;
1641 parser.next();
1642 parser.next();
1643 } else {
1644 end = parser.trimmedLineEnd();
1645 parser.skipToLineStart();
1646 }
1647 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001648 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001649 comment = &markupChild->fTokens.back();
1650 comment->fTerminator = end;
1651 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1652 return false;
1653 }
1654 parser.skipWhiteSpace();
1655 }
1656 parser.skipWhiteSpace();
1657 const char* memberStart = parser.fChar;
1658 if ('}' == memberStart[0]) {
1659 break;
1660 }
Cary Clark9174bda2017-09-19 17:39:32 -04001661 // if there's comment on same the line as member def, output first as if it was before
1662
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001663 parser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001664 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001665 if (parser.eof() || !parser.skipWhiteSpace()) {
1666 return this->reportError<bool>("enum member must end with comma 1");
1667 }
Cary Clark8032b982017-07-28 11:04:54 -04001668 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001669 if ('=' == parser.peek()) {
1670 parser.skipToEndBracket(',');
1671 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001672 if (!parser.eof() && '#' == parser.peek()) {
1673 // fixme: handle preprecessor, but just skip it for now
1674 continue;
1675 }
Cary Clark9174bda2017-09-19 17:39:32 -04001676 if (parser.eof() || ',' != parser.peek()) {
1677 return this->reportError<bool>("enum member must end with comma 2");
1678 }
1679 dataEnd = parser.fChar;
1680 const char* start = parser.anyOf("/\n");
1681 SkASSERT(start);
1682 parser.skipTo(start);
1683 if ('/' == parser.next()) {
1684 char slashStar = parser.next();
1685 if ('/' == slashStar || '*' == slashStar) {
Cary Clark186d08f2018-04-03 08:43:27 -04001686 TextParserSave save(&parser);
Cary Clark9174bda2017-09-19 17:39:32 -04001687 char doxCheck = parser.next();
1688 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1689 save.restore();
1690 }
1691 }
1692 parser.skipWhiteSpace();
1693 const char* commentStart = parser.fChar;
1694 if ('/' == slashStar) {
1695 parser.skipToEndBracket('\n');
1696 } else {
1697 parser.skipToEndBracket("*/");
1698 }
1699 SkASSERT(!parser.eof());
1700 const char* commentEnd = parser.fChar;
1701 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001702 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001703 comment = &markupChild->fTokens.back();
1704 comment->fTerminator = commentEnd;
1705 }
Cary Clark8032b982017-07-28 11:04:54 -04001706 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001707 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001708 Definition* member = &markupChild->fTokens.back();
1709 member->fName = memberName;
1710 if (comment) {
1711 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001712 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001713 }
1714 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001715 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001716 for (auto outsideMember : child->fChildren) {
1717 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001718 continue;
1719 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001720 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1721 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001722 continue;
1723 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001724 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1725 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001726 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001727 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001728 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001729 // FIXME: ? add comment as well ?
1730 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001731 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001732 if (markupDef) {
1733 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1734 SkASSERT(classDef.fStart);
1735 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1736 markupChild->fName = uniqueName;
1737 classDef.fEnums[uniqueName] = markupChild;
1738 }
Cary Clark8032b982017-07-28 11:04:54 -04001739 return true;
1740}
1741
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001742bool IncludeParser::parseInclude(string name) {
Cary Clark8032b982017-07-28 11:04:54 -04001743 fParent = &fIncludeMap[name];
1744 fParent->fName = name;
1745 fParent->fFileName = fFileName;
1746 fParent->fType = Definition::Type::kFileType;
1747 fParent->fContentStart = fChar;
1748 fParent->fContentEnd = fEnd;
1749 // parse include file into tree
1750 while (fChar < fEnd) {
1751 if (!this->parseChar()) {
1752 return false;
1753 }
1754 }
1755 // parse tree and add named objects to maps
1756 fParent = &fIncludeMap[name];
1757 if (!this->parseObjects(fParent, nullptr)) {
1758 return false;
1759 }
1760 return true;
1761}
1762
1763bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1764 const char* typeStart = child->fChildren[0]->fContentStart;
1765 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001766 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001767 Definition* markupChild = &markupDef->fTokens.back();
1768 TextParser nameParser(child);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001769 nameParser.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04001770 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1771 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1772 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1773 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001774 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001775 classDef.fMembers[uniqueName] = markupChild;
1776 if (child->fParentIndex >= 2) {
1777 auto comment = child->fParent->fTokens.begin();
1778 std::advance(comment, child->fParentIndex - 2);
1779 if (Definition::Type::kBracket == comment->fType
1780 && (Bracket::kSlashStar == comment->fBracket
1781 || Bracket::kSlashSlash == comment->fBracket)) {
1782 TextParser parser(&*comment);
1783 do {
1784 parser.skipToAlpha();
1785 if (parser.eof()) {
1786 break;
1787 }
1788 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001789 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001790 if (Bracket::kSlashStar == comment->fBracket) {
1791 const char* commentEnd = parser.strnstr("*/", end);
1792 if (commentEnd) {
1793 end = commentEnd;
1794 }
1795 }
1796 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001797 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001798 Definition* commentChild = &markupDef->fTokens.back();
1799 markupChild->fChildren.emplace_back(commentChild);
1800 parser.skipTo(end);
1801 } while (!parser.eof());
1802 }
1803 }
1804 return true;
1805}
1806
1807bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1808 auto tokenIter = child->fParent->fTokens.begin();
1809 std::advance(tokenIter, child->fParentIndex);
1810 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001811 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001812 bool addConst = false;
1813 auto operatorCheck = tokenIter;
1814 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1815 operatorCheck = std::prev(tokenIter);
1816 }
1817 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001818 auto closeParen = std::next(tokenIter);
1819 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1820 '(' == closeParen->fContentStart[0]);
1821 nameEnd = closeParen->fContentEnd + 1;
1822 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001823 if (Definition::Type::kKeyWord == closeParen->fType &&
1824 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001825 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001826 }
Cary Clarka560c472017-11-27 10:44:06 -05001827 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001828 }
1829 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001830 if (addConst) {
1831 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001832 }
Cary Clark8032b982017-07-28 11:04:54 -04001833 while (tokenIter != child->fParent->fTokens.begin()) {
1834 auto testIter = std::prev(tokenIter);
1835 switch (testIter->fType) {
1836 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001837 if (testIter == child->fParent->fTokens.begin() &&
1838 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1839 KeyWord::kIfndef == child->fParent->fKeyWord ||
1840 KeyWord::kIf == child->fParent->fKeyWord)) {
1841 std::next(tokenIter);
1842 break;
1843 }
Cary Clark8032b982017-07-28 11:04:54 -04001844 goto keepGoing;
1845 case Definition::Type::kKeyWord: {
1846 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1847 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1848 goto keepGoing;
1849 }
1850 } break;
1851 case Definition::Type::kBracket:
1852 if (Bracket::kAngle == testIter->fBracket) {
1853 goto keepGoing;
1854 }
1855 break;
1856 case Definition::Type::kPunctuation:
1857 if (Punctuation::kSemicolon == testIter->fPunctuation
1858 || Punctuation::kLeftBrace == testIter->fPunctuation
1859 || Punctuation::kColon == testIter->fPunctuation) {
1860 break;
1861 }
1862 keepGoing:
1863 tokenIter = testIter;
1864 continue;
1865 default:
1866 break;
1867 }
1868 break;
1869 }
1870 tokenIter->fName = nameStr;
1871 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001872 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001873 auto testIter = child->fParent->fTokens.begin();
1874 SkASSERT(child->fParentIndex > 0);
1875 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001876 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1877 0 == tokenIter->fParentIndex) {
1878 tokenIter = std::next(tokenIter);
1879 }
Cary Clark8032b982017-07-28 11:04:54 -04001880 const char* start = tokenIter->fContentStart;
1881 const char* end = tokenIter->fContentEnd;
1882 const char kDebugCodeStr[] = "SkDEBUGCODE";
1883 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1884 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1885 std::advance(testIter, 1);
1886 start = testIter->fContentStart + 1;
1887 end = testIter->fContentEnd - 1;
1888 } else {
1889 end = testIter->fContentEnd;
1890 while (testIter != child->fParent->fTokens.end()) {
1891 testIter = std::next(testIter);
1892 switch (testIter->fType) {
1893 case Definition::Type::kPunctuation:
1894 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1895 || Punctuation::kLeftBrace == testIter->fPunctuation
1896 || Punctuation::kColon == testIter->fPunctuation);
1897 end = testIter->fStart;
1898 break;
1899 case Definition::Type::kKeyWord: {
1900 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1901 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1902 continue;
1903 }
1904 } break;
1905 default:
1906 continue;
1907 }
1908 break;
1909 }
1910 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001911 while (end > start && ' ' >= end[-1]) {
1912 --end;
1913 }
Cary Clark9174bda2017-09-19 17:39:32 -04001914 if (!markupDef) {
1915 auto parentIter = child->fParent->fTokens.begin();
1916 SkASSERT(child->fParentIndex > 0);
1917 std::advance(parentIter, child->fParentIndex - 1);
1918 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001919 TextParser nameParser(methodName);
1920 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001921 return true; // expect this is inline class definition outside of class
1922 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001923 fGlobals.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1924 fParent, '\0');
1925 Definition* globalMarkupChild = &fGlobals.back();
1926 string globalUniqueName = this->uniqueName(fIFunctionMap, nameStr);
1927 globalMarkupChild->fName = globalUniqueName;
1928 if (!this->findComments(*child, globalMarkupChild)) {
1929 return false;
Cary Clarka560c472017-11-27 10:44:06 -05001930 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001931 fIFunctionMap[globalUniqueName] = globalMarkupChild;
Cary Clarka560c472017-11-27 10:44:06 -05001932 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001933 }
Cary Clark8032b982017-07-28 11:04:54 -04001934 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001935 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001936 Definition* markupChild = &markupDef->fTokens.back();
1937 // do find instead -- I wonder if there is a way to prevent this in c++
1938 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1939 SkASSERT(classDef.fStart);
1940 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1941 markupChild->fName = uniqueName;
1942 if (!this->findComments(*child, markupChild)) {
1943 return false;
1944 }
1945 classDef.fMethods[uniqueName] = markupChild;
1946 return true;
1947}
1948
Cary Clark8032b982017-07-28 11:04:54 -04001949bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
Cary Clark0d225392018-06-07 09:59:07 -04001950 fPriorObject = nullptr;
1951 for (auto child : parent->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04001952 if (!this->parseObject(child, markupDef)) {
1953 return false;
1954 }
Cary Clark0d225392018-06-07 09:59:07 -04001955 fPriorObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001956 }
1957 return true;
1958}
1959
1960bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1961 // set up for error reporting
1962 fLine = fChar = child->fStart;
1963 fEnd = child->fContentEnd;
1964 // todo: put original line number in child as well
1965 switch (child->fType) {
1966 case Definition::Type::kKeyWord:
1967 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001968 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001969 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001970 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001971 }
1972 break;
Cary Clarkd98f78c2018-04-26 08:32:37 -04001973 case KeyWord::kStatic:
Cary Clark0d225392018-06-07 09:59:07 -04001974 case KeyWord::kConst:
1975 case KeyWord::kConstExpr:
Cary Clarkd98f78c2018-04-26 08:32:37 -04001976 if (!this->parseConst(child, markupDef)) {
1977 return child->reportError<bool>("failed to parse const or constexpr");
1978 }
1979 break;
Cary Clark8032b982017-07-28 11:04:54 -04001980 case KeyWord::kEnum:
1981 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001982 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001983 }
1984 break;
1985 case KeyWord::kStruct:
1986 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001987 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001988 }
1989 break;
1990 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001991 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001992 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001993 }
1994 break;
1995 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001996 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001997 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001998 }
1999 break;
2000 case KeyWord::kUnion:
2001 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04002002 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04002003 }
2004 break;
2005 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002006 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04002007 }
2008 break;
2009 case Definition::Type::kBracket:
2010 switch (child->fBracket) {
2011 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04002012 if (fLastObject) {
2013 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
2014 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04002015 if (!checkDeprecated.eof()) {
2016 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04002017 if (checkDeprecated.startsWith(gAttrDeprecated)) {
2018 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04002019 break;
2020 }
2021 }
2022 }
2023 {
2024 auto tokenIter = child->fParent->fTokens.begin();
2025 std::advance(tokenIter, child->fParentIndex);
2026 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05002027 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04002028 if (previousToken.startsWith(gAttrDeprecated)) {
2029 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04002030 break;
2031 }
Cary Clarka7401902018-06-14 15:34:14 -04002032 if ('f' == previousToken.fStart[0] && isupper(previousToken.fStart[1])) {
2033 break;
2034 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05002035 if (Bracket::kPound == child->fParent->fBracket &&
2036 KeyWord::kIf == child->fParent->fKeyWord) {
2037 // TODO: this will skip methods named defined() -- for the
2038 // moment there aren't any
2039 if (previousToken.startsWith("defined")) {
2040 break;
2041 }
2042 }
Cary Clark73fa9722017-08-29 17:36:51 -04002043 }
Cary Clark0d225392018-06-07 09:59:07 -04002044 if (fPriorObject && MarkType::kConst == fPriorObject->fMarkType) {
2045 break;
2046 }
Cary Clark8032b982017-07-28 11:04:54 -04002047 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002048 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04002049 }
Cary Clark89b14562018-03-19 09:04:10 -04002050 if (fAttrDeprecated) {
2051 Definition* lastMethod = &markupDef->fTokens.back();
2052 lastMethod->fDeprecated = true;
2053 fAttrDeprecated = nullptr;
2054 }
Cary Clark73fa9722017-08-29 17:36:51 -04002055 break;
Cary Clark8032b982017-07-28 11:04:54 -04002056 case Bracket::kSlashSlash:
2057 case Bracket::kSlashStar:
2058 // comments are picked up by parsing objects first
2059 break;
2060 case Bracket::kPound:
2061 // special-case the #xxx xxx_DEFINED entries
2062 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05002063 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04002064 case KeyWord::kIfndef:
2065 case KeyWord::kIfdef:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002066 if (child->boilerplateIfDef()) {
Cary Clark8032b982017-07-28 11:04:54 -04002067 if (!this->parseObjects(child, markupDef)) {
2068 return false;
2069 }
2070 break;
2071 }
2072 goto preproError;
2073 case KeyWord::kDefine:
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002074 if (this->parseDefine(child, markupDef)) {
Cary Clark8032b982017-07-28 11:04:54 -04002075 break;
2076 }
2077 goto preproError;
2078 case KeyWord::kEndif:
2079 if (child->boilerplateEndIf()) {
2080 break;
2081 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002082 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04002083 case KeyWord::kInclude:
2084 // ignored for now
2085 break;
2086 case KeyWord::kElse:
2087 case KeyWord::kElif:
2088 // todo: handle these
2089 break;
2090 default:
2091 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04002092 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04002093 }
2094 break;
2095 case Bracket::kAngle:
2096 // pick up templated function pieces when method is found
2097 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002098 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04002099 if (!this->parseObjects(child, markupDef)) {
2100 return false;
2101 }
Cary Clark73fa9722017-08-29 17:36:51 -04002102 break;
Cary Clark9174bda2017-09-19 17:39:32 -04002103 case Bracket::kSquare: {
2104 // check to see if parent is operator, the only case we handle so far
2105 auto prev = child->fParent->fTokens.begin();
2106 std::advance(prev, child->fParentIndex - 1);
2107 if (KeyWord::kOperator != prev->fKeyWord) {
2108 return child->reportError<bool>("expected operator overload");
2109 }
2110 } break;
Cary Clark8032b982017-07-28 11:04:54 -04002111 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002112 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04002113 }
2114 break;
2115 case Definition::Type::kWord:
2116 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04002117 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04002118 }
2119 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04002120 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04002121 }
2122 break;
2123 default:
Cary Clark9174bda2017-09-19 17:39:32 -04002124 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04002125 break;
2126 }
2127 return true;
2128}
2129
Cary Clarkbbfda252018-03-09 15:32:01 -05002130bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
2131 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04002132}
2133
Cary Clark2f466242017-12-11 16:03:17 -05002134bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
2135 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05002136 typedefParser.skipExact("typedef");
2137 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05002138 string nameStr = typedefParser.typedefName();
2139 if (!markupDef) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002140 fGlobals.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
2141 child->fLineCount, fParent, '\0');
2142 Definition* globalMarkupChild = &fGlobals.back();
2143 string globalUniqueName = this->uniqueName(fITypedefMap, nameStr);
2144 globalMarkupChild->fName = globalUniqueName;
2145 if (!this->findComments(*child, globalMarkupChild)) {
2146 return false;
2147 }
2148 fITypedefMap[globalUniqueName] = globalMarkupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002149 child->fName = nameStr;
Cary Clark2f466242017-12-11 16:03:17 -05002150 return true;
2151 }
2152 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05002153 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05002154 Definition* markupChild = &markupDef->fTokens.back();
2155 markupChild->fName = nameStr;
2156 markupChild->fTerminator = markupChild->fContentEnd;
2157 IClassDefinition& classDef = fIClassMap[markupDef->fName];
2158 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark0d225392018-06-07 09:59:07 -04002159 child->fName = markupDef->fName + "::" + nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04002160 return true;
2161}
2162
2163bool IncludeParser::parseUnion() {
2164
2165 return true;
2166}
2167
2168bool IncludeParser::parseChar() {
2169 char test = *fChar;
2170 if ('\\' == fPrev) {
2171 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04002172// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002173 fLine = fChar + 1;
2174 }
2175 goto done;
2176 }
2177 switch (test) {
2178 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04002179// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04002180 fLine = fChar + 1;
2181 if (fInChar) {
2182 return reportError<bool>("malformed char");
2183 }
2184 if (fInString) {
2185 return reportError<bool>("malformed string");
2186 }
2187 if (!this->checkForWord()) {
2188 return false;
2189 }
2190 if (Bracket::kPound == this->topBracket()) {
2191 KeyWord keyWord = fParent->fKeyWord;
2192 if (KeyWord::kNone == keyWord) {
2193 return this->reportError<bool>("unhandled preprocessor directive");
2194 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002195 if (fInDefine) {
2196 SkASSERT(KeyWord::kDefine == keyWord);
2197 fInDefine = false;
2198 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05002199 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04002200 this->popBracket();
2201 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002202 if (fInBrace) {
2203 SkASSERT(KeyWord::kDefine == fInBrace->fKeyWord);
2204 fInBrace = nullptr;
2205 }
Cary Clark8032b982017-07-28 11:04:54 -04002206 } else if (Bracket::kSlashSlash == this->topBracket()) {
2207 this->popBracket();
2208 }
2209 break;
2210 case '*':
2211 if (!fInCharCommentString && '/' == fPrev) {
2212 this->pushBracket(Bracket::kSlashStar);
2213 }
2214 if (!this->checkForWord()) {
2215 return false;
2216 }
2217 if (!fInCharCommentString) {
2218 this->addPunctuation(Punctuation::kAsterisk);
2219 }
2220 break;
2221 case '/':
2222 if ('*' == fPrev) {
2223 if (!fInCharCommentString) {
2224 return reportError<bool>("malformed closing comment");
2225 }
2226 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clark186d08f2018-04-03 08:43:27 -04002227 TextParserSave save(this);
Cary Clarka560c472017-11-27 10:44:06 -05002228 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04002229 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05002230 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04002231 }
2232 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04002233 }
Cary Clark8032b982017-07-28 11:04:54 -04002234 if (!fInCharCommentString && '/' == fPrev) {
2235 this->pushBracket(Bracket::kSlashSlash);
2236 break;
2237 }
2238 if (!this->checkForWord()) {
2239 return false;
2240 }
2241 break;
2242 case '\'':
2243 if (Bracket::kChar == this->topBracket()) {
2244 this->popBracket();
2245 } else if (!fInComment && !fInString) {
2246 if (fIncludeWord) {
2247 return this->reportError<bool>("word then single-quote");
2248 }
2249 this->pushBracket(Bracket::kChar);
2250 }
2251 break;
2252 case '\"':
2253 if (Bracket::kString == this->topBracket()) {
2254 this->popBracket();
2255 } else if (!fInComment && !fInChar) {
2256 if (fIncludeWord) {
2257 return this->reportError<bool>("word then double-quote");
2258 }
2259 this->pushBracket(Bracket::kString);
2260 }
2261 break;
Cary Clark8032b982017-07-28 11:04:54 -04002262 case '(':
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002263 if (fIncludeWord && fChar - fIncludeWord >= 10 &&
Cary Clark73fa9722017-08-29 17:36:51 -04002264 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
2265 this->pushBracket(Bracket::kDebugCode);
2266 break;
2267 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002268 case ':':
2269 case '[':
2270 case '{': {
Cary Clark8032b982017-07-28 11:04:54 -04002271 if (fInCharCommentString) {
2272 break;
2273 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002274 if (fInDefine && fInBrace) {
2275 break;
2276 }
Cary Clark8032b982017-07-28 11:04:54 -04002277 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2278 break;
2279 }
Cary Clark0d225392018-06-07 09:59:07 -04002280 if (fConstExpr) {
2281 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2282 fConstExpr = nullptr;
2283 }
Cary Clark8032b982017-07-28 11:04:54 -04002284 if (!fInBrace) {
2285 if (!this->checkForWord()) {
2286 return false;
2287 }
2288 if (':' == test && !fInFunction) {
2289 break;
2290 }
2291 if ('{' == test) {
2292 this->addPunctuation(Punctuation::kLeftBrace);
2293 } else if (':' == test) {
2294 this->addPunctuation(Punctuation::kColon);
2295 }
2296 }
2297 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2298 && Bracket::kColon == fInBrace->fBracket) {
2299 Definition* braceParent = fParent->fParent;
2300 braceParent->fChildren.pop_back();
2301 braceParent->fTokens.pop_back();
2302 fParent = braceParent;
2303 fInBrace = nullptr;
2304 }
2305 this->pushBracket(
2306 '(' == test ? Bracket::kParen :
2307 '[' == test ? Bracket::kSquare :
2308 '{' == test ? Bracket::kBrace :
2309 Bracket::kColon);
2310 if (!fInBrace
2311 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2312 && fInFunction) {
2313 fInBrace = fParent;
2314 }
2315 } break;
2316 case '<':
2317 if (fInCharCommentString || fInBrace) {
2318 break;
2319 }
2320 if (!this->checkForWord()) {
2321 return false;
2322 }
2323 if (fInEnum) {
2324 break;
2325 }
2326 this->pushBracket(Bracket::kAngle);
2327 break;
2328 case ')':
2329 case ']':
2330 case '}': {
2331 if (fInCharCommentString) {
2332 break;
2333 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002334 if (fInDefine && fInBrace) {
2335 break;
2336 }
Cary Clark8032b982017-07-28 11:04:54 -04002337 if (!fInBrace) {
2338 if (!this->checkForWord()) {
2339 return false;
2340 }
2341 }
2342 bool popBraceParent = fInBrace == fParent;
2343 if ((')' == test ? Bracket::kParen :
2344 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2345 this->popBracket();
2346 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002347 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002348 } else {
2349 fInFunction = '}' != test;
2350 }
Cary Clark73fa9722017-08-29 17:36:51 -04002351 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2352 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002353 } else {
2354 return reportError<bool>("malformed close bracket");
2355 }
2356 if (popBraceParent) {
2357 Definition* braceParent = fInBrace->fParent;
2358 braceParent->fChildren.pop_back();
2359 braceParent->fTokens.pop_back();
2360 fInBrace = nullptr;
2361 }
2362 } break;
2363 case '>':
2364 if (fInCharCommentString || fInBrace) {
2365 break;
2366 }
2367 if (!this->checkForWord()) {
2368 return false;
2369 }
2370 if (fInEnum) {
2371 break;
2372 }
Cary Clarka560c472017-11-27 10:44:06 -05002373 if (Bracket::kPound == this->topBracket()) {
2374 break;
2375 }
Cary Clark8032b982017-07-28 11:04:54 -04002376 if (Bracket::kAngle == this->topBracket()) {
2377 this->popBracket();
2378 } else {
2379 return reportError<bool>("malformed close angle bracket");
2380 }
2381 break;
2382 case '#': {
2383 if (fInCharCommentString || fInBrace) {
2384 break;
2385 }
2386 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2387 this->pushBracket(Bracket::kPound);
2388 break;
2389 }
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002390 case ' ':
2391 if (fInDefine && !fInBrace && Bracket::kPound == this->topBracket()) {
2392 SkASSERT(KeyWord::kDefine == fParent->fKeyWord);
2393 fInBrace = fParent;
2394 // delimiting brackets are space ... unescaped-linefeed
2395 }
Cary Clark8032b982017-07-28 11:04:54 -04002396 case '&':
2397 case ',':
Cary Clarka560c472017-11-27 10:44:06 -05002398 case '+':
Cary Clarka560c472017-11-27 10:44:06 -05002399 case '-':
2400 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002401 if (fInCharCommentString || fInBrace) {
2402 break;
2403 }
2404 if (!this->checkForWord()) {
2405 return false;
2406 }
2407 break;
Cary Clark0d225392018-06-07 09:59:07 -04002408 case '=':
2409 if (fInCharCommentString || fInBrace) {
2410 break;
2411 }
2412 if (!this->checkForWord()) {
2413 return false;
2414 }
2415 {
2416 const Definition& lastToken = fParent->fTokens.back();
2417 if (lastToken.fType != Definition::Type::kWord) {
2418 break;
2419 }
2420 string name(lastToken.fContentStart, lastToken.length());
2421 if ("SK_" != name.substr(0, 3) && 'k' != name[0]) {
2422 break;
2423 }
2424 // find token on start of line
2425 auto lineIter = fParent->fTokens.end();
2426 do {
2427 --lineIter;
2428 } while (lineIter->fContentStart > fLine);
2429 if (lineIter->fContentStart < fLine) {
2430 ++lineIter;
2431 }
2432 Definition* lineStart = &*lineIter;
2433 // walk tokens looking for [template <typename T>] [static] [const | constexpr]
2434 bool sawConst = false;
2435 bool sawStatic = false;
2436 bool sawTemplate = false;
2437 bool sawType = false;
2438 while (&lastToken != &*lineIter) {
2439 if (KeyWord::kTemplate == lineIter->fKeyWord) {
2440 if (sawConst || sawStatic || sawTemplate) {
2441 sawConst = false;
2442 break;
2443 }
2444 if (&lastToken == &*++lineIter) {
2445 break;
2446 }
2447 if (KeyWord::kTypename != lineIter->fKeyWord) {
2448 break;
2449 }
2450 if (&lastToken == &*++lineIter) {
2451 break;
2452 }
2453 if (Definition::Type::kWord != lineIter->fType) {
2454 break;
2455 }
2456 sawTemplate = true;
2457 } else if (KeyWord::kStatic == lineIter->fKeyWord) {
2458 if (sawConst || sawStatic) {
2459 sawConst = false;
2460 break;
2461 }
2462 sawStatic = true;
2463 } else if (KeyWord::kConst == lineIter->fKeyWord
2464 || KeyWord::kConstExpr == lineIter->fKeyWord) {
2465 if (sawConst) {
2466 sawConst = false;
2467 break;
2468 }
2469 sawConst = true;
2470 } else {
2471 if (sawType) {
2472 sawType = false;
2473 break;
2474 }
2475 if (Definition::Type::kKeyWord == lineIter->fType
2476 && KeyProperty::kNumber
2477 == kKeyWords[(int) lineIter->fKeyWord].fProperty) {
2478 sawType = true;
2479 } else if (Definition::Type::kWord == lineIter->fType) {
2480 string typeName(lineIter->fContentStart, lineIter->length());
2481 if ("Sk" != name.substr(0, 2)) {
2482 sawType = true;
2483 }
2484 }
2485 }
2486 ++lineIter;
2487 }
2488 if (sawType && sawConst) {
2489 // if found, name first
2490 lineStart->fName = name;
2491 lineStart->fMarkType = MarkType::kConst;
2492 fParent->fChildren.emplace_back(lineStart);
2493 fConstExpr = lineStart;
2494 }
2495 }
2496 break;
Cary Clark8032b982017-07-28 11:04:54 -04002497 case ';':
2498 if (fInCharCommentString || fInBrace) {
2499 break;
2500 }
2501 if (!this->checkForWord()) {
2502 return false;
2503 }
Cary Clark0d225392018-06-07 09:59:07 -04002504 if (fConstExpr) {
2505 fConstExpr->fContentEnd = fParent->fTokens.back().fContentEnd;
2506 fConstExpr = nullptr;
2507 }
Cary Clark8032b982017-07-28 11:04:54 -04002508 if (Definition::Type::kKeyWord == fParent->fType
2509 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002510 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2511 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002512 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2513 this->popObject();
2514 }
Cary Clark8032b982017-07-28 11:04:54 -04002515 if (KeyWord::kEnum == fParent->fKeyWord) {
2516 fInEnum = false;
2517 }
2518 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002519 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2520 this->popObject();
2521 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002522 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002523 } else if (Definition::Type::kBracket == fParent->fType
2524 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2525 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2526 list<Definition>::iterator baseIter = fParent->fTokens.end();
2527 list<Definition>::iterator namedIter = fParent->fTokens.end();
2528 for (auto tokenIter = fParent->fTokens.end();
2529 fParent->fTokens.begin() != tokenIter--; ) {
2530 if (tokenIter->fLineCount == fLineCount) {
2531 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2532 if (namedIter != fParent->fTokens.end()) {
2533 return reportError<bool>("found two named member tokens");
2534 }
2535 namedIter = tokenIter;
2536 }
2537 baseIter = tokenIter;
2538 } else {
2539 break;
2540 }
2541 }
2542 // FIXME: if a member definition spans multiple lines, this won't work
2543 if (namedIter != fParent->fTokens.end()) {
2544 if (baseIter == namedIter) {
2545 return this->reportError<bool>("expected type before named token");
2546 }
2547 Definition* member = &*namedIter;
2548 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002549 if (!member->fTerminator) {
2550 member->fTerminator = member->fContentEnd;
2551 }
Cary Clark8032b982017-07-28 11:04:54 -04002552 fParent->fChildren.push_back(member);
2553 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2554 member->fChildren.push_back(&*nameType);
2555 }
Cary Clark8032b982017-07-28 11:04:54 -04002556 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002557 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002558 } else if (fParent->fChildren.size() > 0) {
2559 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002560 Definition* priorEnum = fPriorEnum;
2561 fPriorEnum = nullptr;
2562 if (!priorEnum) {
2563 while (fParent->fChildren.begin() != lastIter) {
2564 std::advance(lastIter, -1);
2565 priorEnum = *lastIter;
2566 if (Definition::Type::kBracket != priorEnum->fType ||
2567 (Bracket::kSlashSlash != priorEnum->fBracket
2568 && Bracket::kSlashStar != priorEnum->fBracket)) {
2569 break;
2570 }
Cary Clark8032b982017-07-28 11:04:54 -04002571 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002572 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002573 }
2574 if (Definition::Type::kKeyWord == priorEnum->fType
2575 && KeyWord::kEnum == priorEnum->fKeyWord) {
2576 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002577 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002578 while (tokenWalker != fParent->fTokens.end()) {
2579 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002580 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002581 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2582 break;
2583 }
2584 }
2585 while (tokenWalker != fParent->fTokens.end()) {
2586 std::advance(tokenWalker, 1);
2587 const Definition* test = &*tokenWalker;
2588 if (Definition::Type::kBracket != test->fType ||
2589 (Bracket::kSlashSlash != test->fBracket
2590 && Bracket::kSlashStar != test->fBracket)) {
2591 break;
2592 }
2593 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002594 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002595 Definition* start = &*tokenWalker;
2596 bool foundExpected = true;
2597 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2598 const Definition* test = &*tokenWalker;
2599 if (expected != test->fKeyWord) {
2600 foundExpected = false;
2601 break;
2602 }
2603 if (tokenWalker == fParent->fTokens.end()) {
2604 break;
2605 }
2606 std::advance(tokenWalker, 1);
2607 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002608 if (!foundExpected) {
2609 foundExpected = true;
2610 tokenWalker = saveTokenWalker;
2611 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2612 const Definition* test = &*tokenWalker;
2613 if (expected != test->fKeyWord) {
2614 foundExpected = false;
2615 break;
2616 }
2617 if (tokenWalker == fParent->fTokens.end()) {
2618 break;
2619 }
2620 if (KeyWord::kNone != expected) {
2621 std::advance(tokenWalker, 1);
2622 }
2623 }
2624 if (foundExpected) {
2625 auto nameToken = priorEnum->fTokens.begin();
2626 string enumName = string(nameToken->fContentStart,
2627 nameToken->fContentEnd - nameToken->fContentStart);
2628 const Definition* test = &*tokenWalker;
2629 string constType = string(test->fContentStart,
2630 test->fContentEnd - test->fContentStart);
2631 if (enumName != constType) {
2632 foundExpected = false;
2633 } else {
2634 std::advance(tokenWalker, 1);
2635 }
2636 }
2637 }
Cary Clark8032b982017-07-28 11:04:54 -04002638 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2639 const char* nameStart = tokenWalker->fStart;
2640 std::advance(tokenWalker, 1);
2641 if (tokenWalker != fParent->fTokens.end()) {
2642 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04002643 tp.skipToNonName();
Cary Clark8032b982017-07-28 11:04:54 -04002644 start->fName = string(nameStart, tp.fChar - nameStart);
2645 start->fContentEnd = fChar;
2646 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002647 fPriorEnum = priorEnum;
2648 }
Cary Clark8032b982017-07-28 11:04:54 -04002649 }
2650 }
2651 }
2652 this->addPunctuation(Punctuation::kSemicolon);
2653 fInFunction = false;
2654 break;
2655 case '~':
2656 if (fInEnum) {
2657 break;
2658 }
2659 case '0': case '1': case '2': case '3': case '4':
2660 case '5': case '6': case '7': case '8': case '9':
2661 // TODO: don't want to parse numbers, but do need to track for enum defs
2662 // break;
2663 case 'A': case 'B': case 'C': case 'D': case 'E':
2664 case 'F': case 'G': case 'H': case 'I': case 'J':
2665 case 'K': case 'L': case 'M': case 'N': case 'O':
2666 case 'P': case 'Q': case 'R': case 'S': case 'T':
2667 case 'U': case 'V': case 'W': case 'X': case 'Y':
2668 case 'Z': case '_':
2669 case 'a': case 'b': case 'c': case 'd': case 'e':
2670 case 'f': case 'g': case 'h': case 'i': case 'j':
2671 case 'k': case 'l': case 'm': case 'n': case 'o':
2672 case 'p': case 'q': case 'r': case 's': case 't':
2673 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002674 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002675 if (fInCharCommentString || fInBrace) {
2676 break;
2677 }
2678 if (!fIncludeWord) {
2679 fIncludeWord = fChar;
2680 }
2681 break;
2682 }
2683done:
2684 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002685 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002686 return true;
2687}
2688
2689void IncludeParser::validate() const {
Cary Clark8032b982017-07-28 11:04:54 -04002690 IncludeParser::ValidateKeyWords();
2691}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002692
Cary Clark186d08f2018-04-03 08:43:27 -04002693bool IncludeParser::references(const SkString& file) const {
2694 // if includes weren't passed one at a time, assume all references are valid
2695 if (fIncludeMap.empty()) {
2696 return true;
2697 }
2698 SkASSERT(file.endsWith(".bmh") );
2699 string root(file.c_str(), file.size() - 4);
2700 string kReference("_Reference");
2701 if (string::npos != root.find(kReference)) {
2702 root = root.substr(0, root.length() - kReference.length());
2703 }
2704 if (fIClassMap.end() != fIClassMap.find(root)) {
2705 return true;
2706 }
2707 if (fIStructMap.end() != fIStructMap.find(root)) {
2708 return true;
2709 }
2710 // TODO incomplete: probably need to look in other places for class-less includes like SkColor.h
2711 return false;
2712}
2713
Cary Clark2dc84ad2018-01-26 12:56:22 -05002714void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2715 if (!sk_isdir(includes)) {
2716 IncludeParser::RemoveOneFile(docs, includes);
2717 } else {
2718 SkOSFile::Iter it(includes, ".h");
2719 for (SkString file; it.next(&file); ) {
2720 SkString p = SkOSPath::Join(includes, file.c_str());
2721 const char* hunk = p.c_str();
2722 if (!SkStrEndsWith(hunk, ".h")) {
2723 continue;
2724 }
2725 IncludeParser::RemoveOneFile(docs, hunk);
2726 }
2727 }
2728}
2729
2730void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2731 const char* lastForward = strrchr(includesFile, '/');
2732 const char* lastBackward = strrchr(includesFile, '\\');
2733 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2734 if (!last) {
2735 last = includesFile;
2736 } else {
2737 last += 1;
2738 }
2739 SkString baseName(last);
2740 SkASSERT(baseName.endsWith(".h"));
2741 baseName.remove(baseName.size() - 2, 2);
2742 baseName.append("_Reference.bmh");
2743 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2744 remove(fullName.c_str());
2745}