blob: 8365a82d1c6901f6054a4de8d68bf45492b756fa [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
Cary Clark2dc84ad2018-01-26 12:56:22 -05009#include "SkOSFile.h"
10#include "SkOSPath.h"
Cary Clark8032b982017-07-28 11:04:54 -040011
Cary Clark89b14562018-03-19 09:04:10 -040012const char IncludeParser::gAttrDeprecated[] = "SK_ATTR_DEPRECATED";
13const size_t IncludeParser::kAttrDeprecatedLen = sizeof(gAttrDeprecated) - 1;
14
Cary Clark8032b982017-07-28 11:04:54 -040015const IncludeKey kKeyWords[] = {
16 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040017 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040018 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040019 { "bool", KeyWord::kBool, KeyProperty::kNumber },
20 { "char", KeyWord::kChar, KeyProperty::kNumber },
21 { "class", KeyWord::kClass, KeyProperty::kObject },
22 { "const", KeyWord::kConst, KeyProperty::kModifier },
23 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
24 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
25 { "double", KeyWord::kDouble, KeyProperty::kNumber },
26 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
27 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
28 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
29 { "enum", KeyWord::kEnum, KeyProperty::kObject },
Cary Clark2dc84ad2018-01-26 12:56:22 -050030 { "error", KeyWord::kError, KeyProperty::kPreprocessor },
Cary Clark8032b982017-07-28 11:04:54 -040031 { "float", KeyWord::kFloat, KeyProperty::kNumber },
32 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
33 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
34 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
35 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
36 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
37 { "inline", KeyWord::kInline, KeyProperty::kModifier },
38 { "int", KeyWord::kInt, KeyProperty::kNumber },
39 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
40 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
41 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
42 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
43 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
44 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
45 { "static", KeyWord::kStatic, KeyProperty::kModifier },
46 { "struct", KeyWord::kStruct, KeyProperty::kObject },
47 { "template", KeyWord::kTemplate, KeyProperty::kObject },
48 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040049 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040050 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040051 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
52 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040053 { "union", KeyWord::kUnion, KeyProperty::kObject },
54 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
55 { "void", KeyWord::kVoid, KeyProperty::kNumber },
56};
57
58const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
59
60KeyWord IncludeParser::FindKey(const char* start, const char* end) {
61 int ch = 0;
62 for (size_t index = 0; index < kKeyWordCount; ) {
63 if (start[ch] > kKeyWords[index].fName[ch]) {
64 ++index;
65 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
66 return KeyWord::kNone;
67 }
68 continue;
69 }
70 if (start[ch] < kKeyWords[index].fName[ch]) {
71 return KeyWord::kNone;
72 }
73 ++ch;
74 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040075 if (end - start < (int) strlen(kKeyWords[index].fName)) {
76 return KeyWord::kNone;
77 }
Cary Clark8032b982017-07-28 11:04:54 -040078 return kKeyWords[index].fKeyWord;
79 }
80 }
81 return KeyWord::kNone;
82}
83
84void IncludeParser::ValidateKeyWords() {
85 for (size_t index = 1; index < kKeyWordCount; ++index) {
86 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
87 == (int) kKeyWords[index].fKeyWord);
88 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
89 }
90}
91
92void IncludeParser::addKeyword(KeyWord keyWord) {
Cary Clark1a8d7622018-03-05 13:26:16 -050093 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -040094 fIncludeWord = nullptr;
95 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
96 Definition* def = &fParent->fTokens.back();
97 this->addDefinition(def);
98 if (KeyWord::kEnum == fParent->fKeyWord) {
99 fInEnum = true;
100 }
101 }
102}
103
Ben Wagner63fd7602017-10-09 15:45:33 -0400104void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -0400105 const vector<string>& foundParams) {
106 for (auto& methodParam : methodParams) {
107 bool found = false;
108 for (auto& foundParam : foundParams) {
109 if (methodParam == foundParam) {
110 found = true;
111 break;
112 }
113 }
114 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400115 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400116 }
117 }
118 for (auto& foundParam : foundParams) {
119 bool found = false;
120 for (auto& methodParam : methodParams) {
121 if (methodParam == foundParam) {
122 found = true;
123 break;
124 }
125 }
126 if (!found) {
127 this->reportError("doxygen param does not match method declaration");
128 }
129 }
130}
131
132bool IncludeParser::checkForWord() {
133 if (!fIncludeWord) {
134 return true;
135 }
136 KeyWord keyWord = FindKey(fIncludeWord, fChar);
137 if (KeyWord::kNone != keyWord) {
138 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
139 this->addKeyword(keyWord);
140 return true;
141 }
142 } else {
143 this->addWord();
144 return true;
145 }
146 Definition* poundDef = fParent;
147 if (!fParent) {
148 return reportError<bool>("expected parent");
149 }
150 if (Definition::Type::kBracket != poundDef->fType) {
151 return reportError<bool>("expected bracket");
152 }
153 if (Bracket::kPound != poundDef->fBracket) {
154 return reportError<bool>("expected preprocessor");
155 }
156 if (KeyWord::kNone != poundDef->fKeyWord) {
157 return reportError<bool>("already found keyword");
158 }
159 poundDef->fKeyWord = keyWord;
160 fIncludeWord = nullptr;
161 switch (keyWord) {
162 // these do not link to other # directives
163 case KeyWord::kDefine:
164 case KeyWord::kInclude:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500165 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -0400166 break;
167 // these start a # directive link
168 case KeyWord::kIf:
169 case KeyWord::kIfdef:
170 case KeyWord::kIfndef:
171 break;
172 // these continue a # directive link
173 case KeyWord::kElif:
174 case KeyWord::kElse: {
175 this->popObject(); // pop elif
176 if (Bracket::kPound != fParent->fBracket) {
177 return this->reportError<bool>("expected preprocessor directive");
178 }
179 this->popBracket(); // pop if
180 poundDef->fParent = fParent;
181 this->addDefinition(poundDef); // push elif back
182 } break;
183 // this ends a # directive link
184 case KeyWord::kEndif:
185 // FIXME : should this be calling popBracket() instead?
186 this->popObject(); // pop endif
187 if (Bracket::kPound != fParent->fBracket) {
188 return this->reportError<bool>("expected preprocessor directive");
189 }
190 this->popBracket(); // pop if/else
191 break;
192 default:
193 SkASSERT(0);
194 }
195 return true;
196}
197
198string IncludeParser::className() const {
199 string name(fParent->fName);
200 size_t slash = name.find_last_of("/");
201 if (string::npos == slash) {
202 slash = name.find_last_of("\\");
203 }
204 SkASSERT(string::npos != slash);
205 string result = name.substr(slash);
206 result = result.substr(1, result.size() - 3);
207 return result;
208}
209
Cary Clark884dd7d2017-10-11 10:37:52 -0400210#include <sstream>
211#include <iostream>
212
Cary Clark8032b982017-07-28 11:04:54 -0400213bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400214 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400215 string className = classMapper.first;
216 auto finder = bmhParser.fClassMap.find(className);
217 if (bmhParser.fClassMap.end() == finder) {
218 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400219 continue;
220 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400221 RootDefinition* root = &finder->second;
222 root->clearVisited();
223 }
224 for (auto& classMapper : fIClassMap) {
225 string className = classMapper.first;
226 std::istringstream iss(className);
227 string classStr;
228 string classBase;
229 RootDefinition* root = nullptr;
230 while (std::getline(iss, classStr, ':')) {
231 if (root) {
232 if (!classStr.length()) {
233 continue;
234 }
235 classBase += "::" + classStr;
236 auto finder = root->fBranches.find(classBase);
237 if (root->fBranches.end() != finder) {
238 root = finder->second;
239 } else {
240 SkASSERT(0);
241 }
242 } else {
243 classBase = classStr;
244 auto finder = bmhParser.fClassMap.find(classBase);
245 if (bmhParser.fClassMap.end() != finder) {
246 root = &finder->second;
247 } else {
248 SkASSERT(0);
249 }
250 }
251 }
Cary Clark8032b982017-07-28 11:04:54 -0400252 auto& classMap = classMapper.second;
253 auto& tokens = classMap.fTokens;
254 for (const auto& token : tokens) {
255 if (token.fPrivate) {
256 continue;
257 }
258 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400259 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400260 switch (token.fMarkType) {
261 case MarkType::kMethod: {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400262 if (this->internalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400263 continue;
264 }
Cary Clark8032b982017-07-28 11:04:54 -0400265 if (!def) {
266 string paramName = className + "::";
267 paramName += string(token.fContentStart,
268 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400269 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400270 if (!def && 0 == token.fName.find("operator")) {
271 string operatorName = className + "::";
272 TextParser oper("", token.fStart, token.fContentEnd, 0);
273 const char* start = oper.strnstr("operator", token.fContentEnd);
274 SkASSERT(start);
275 oper.skipTo(start);
276 oper.skipToEndBracket('(');
277 int parens = 0;
278 do {
279 if ('(' == oper.peek()) {
280 ++parens;
281 } else if (')' == oper.peek()) {
282 --parens;
283 }
284 } while (!oper.eof() && oper.next() && parens > 0);
285 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400286 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400287 }
288 }
289 if (!def) {
290 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
291 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
292 string constructorName = className + "::";
293 constructorName += string(token.fContentStart + skip,
294 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400295 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400296 }
297 if (!def && 0 == token.fName.find("SK_")) {
298 string incName = token.fName + "()";
299 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400300 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400301 if (def) {
302 if (def->fName == incName) {
303 def->fVisited = true;
304 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400305 def = root->find(className + "::toString",
306 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400307 if (def) {
308 def->fVisited = true;
309 } else {
310 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500311 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400312 }
313 }
314 break;
315 } else {
316 SkDebugf("method macro differs from 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 }
321 if (!def) {
322 bool allLower = true;
323 for (size_t index = 0; index < token.fName.length(); ++index) {
324 if (!islower(token.fName[index])) {
325 allLower = false;
326 break;
327 }
328 }
329 if (allLower) {
330 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400331 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400332 }
333 }
334 if (!def) {
Cary Clark89b14562018-03-19 09:04:10 -0400335 if (gAttrDeprecated == token.fName) {
336 fAttrDeprecated = &token;
Cary Clark73fa9722017-08-29 17:36:51 -0400337 break;
338 }
339 if (0 == token.fName.find("SkDEBUGCODE")) {
340 break;
341 }
342 }
343 if (!def) {
344 // simple method names inside nested classes have a bug and are missing trailing parens
345 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400346 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400347 }
348 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500349 if (!root->fDeprecated) {
350 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
351 fFailed = true;
352 }
Cary Clark8032b982017-07-28 11:04:54 -0400353 break;
354 }
Cary Clark73fa9722017-08-29 17:36:51 -0400355 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400356 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400357 if (MarkType::kDefinedBy == def->fMarkType) {
358 def->fParent->fVisited = true;
359 }
Cary Clark89b14562018-03-19 09:04:10 -0400360 if (token.fDeprecated && !def->fDeprecated) {
361 fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
362 }
Cary Clark8032b982017-07-28 11:04:54 -0400363 } else {
364 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500365 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400366 }
367 } break;
368 case MarkType::kComment:
369 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400370 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400371 case MarkType::kEnum: {
372 if (!def) {
373 // work backwards from first word to deduce #Enum name
374 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
375 SkAssertResult(firstMember.skipName("enum"));
376 SkAssertResult(firstMember.skipToEndBracket('{'));
377 firstMember.next();
378 firstMember.skipWhiteSpace();
379 SkASSERT('k' == firstMember.peek());
380 const char* savePos = firstMember.fChar;
381 firstMember.skipToNonAlphaNum();
382 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400383 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400384 const char* lastUnderscore = nullptr;
385 do {
386 if (!firstMember.skipToEndBracket('_')) {
387 break;
388 }
389 if (firstMember.fChar > wordEnd) {
390 break;
391 }
392 lastUnderscore = firstMember.fChar;
393 } while (firstMember.next());
394 if (lastUnderscore) {
395 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400396 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400397 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400398 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400399 }
400 if (!def) {
Cary Clark56356312018-02-08 14:45:18 -0500401 if (!root->fDeprecated) {
402 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
403 fFailed = true;
404 }
Cary Clark8032b982017-07-28 11:04:54 -0400405 break;
406 }
407 }
408 def->fVisited = true;
409 for (auto& child : def->fChildren) {
410 if (MarkType::kCode == child->fMarkType) {
411 def = child;
412 break;
413 }
414 }
415 if (MarkType::kCode != def->fMarkType) {
Cary Clark56356312018-02-08 14:45:18 -0500416 if (!root->fDeprecated) {
417 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
418 fFailed = true;
419 }
Cary Clark8032b982017-07-28 11:04:54 -0400420 break;
421 }
422 if (def->crossCheck(token)) {
423 def->fVisited = true;
424 } else {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500425 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
426 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400427 }
428 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400429 string constName = MarkType::kEnumClass == token.fMarkType ?
430 fullName : className;
431 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400432 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400433 if (!def) {
434 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400435 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400436 }
437 if (!def) {
438 if (string::npos == child->fName.find("Legacy_")) {
Cary Clark56356312018-02-08 14:45:18 -0500439 if (!root->fDeprecated) {
440 SkDebugf("const missing from bmh: %s\n", constName.c_str());
441 fFailed = true;
442 }
Cary Clark8032b982017-07-28 11:04:54 -0400443 }
444 } else {
445 def->fVisited = true;
446 }
447 }
448 } break;
449 case MarkType::kMember:
450 if (def) {
451 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500452 } else if (!root->fDeprecated) {
Cary Clark8032b982017-07-28 11:04:54 -0400453 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500454 fFailed = true;
Cary Clark8032b982017-07-28 11:04:54 -0400455 }
456 break;
Cary Clark2f466242017-12-11 16:03:17 -0500457 case MarkType::kTypedef:
458 if (def) {
459 def->fVisited = true;
Cary Clark56356312018-02-08 14:45:18 -0500460 } else if (!root->fDeprecated) {
Cary Clark2f466242017-12-11 16:03:17 -0500461 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
Cary Clarkf059e7c2017-12-20 14:53:21 -0500462 fFailed = true;
Cary Clark2f466242017-12-11 16:03:17 -0500463 }
464 break;
Cary Clark8032b982017-07-28 11:04:54 -0400465 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400466 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400467 break;
468 }
469 }
470 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500471 int crossChecks = 0;
472 string firstCheck;
Cary Clark884dd7d2017-10-11 10:37:52 -0400473 for (auto& classMapper : fIClassMap) {
474 string className = classMapper.first;
475 auto finder = bmhParser.fClassMap.find(className);
476 if (bmhParser.fClassMap.end() == finder) {
477 continue;
478 }
479 RootDefinition* root = &finder->second;
Cary Clarkf5404bb2018-01-05 12:10:09 -0500480 if (!root->dumpUnVisited()) {
Cary Clarkf059e7c2017-12-20 14:53:21 -0500481 fFailed = true;
Cary Clark884dd7d2017-10-11 10:37:52 -0400482 }
Cary Clark7cfcbca2018-01-04 16:11:51 -0500483 if (crossChecks) {
484 SkDebugf(".");
485 } else {
486 SkDebugf("cross-check");
487 firstCheck = className;
488 }
489 ++crossChecks;
490 }
491 if (crossChecks) {
492 if (1 == crossChecks) {
Cary Clark56356312018-02-08 14:45:18 -0500493 SkDebugf(" %s", firstCheck.c_str());
Cary Clark7cfcbca2018-01-04 16:11:51 -0500494 }
495 SkDebugf("\n");
Cary Clark8032b982017-07-28 11:04:54 -0400496 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400497 bmhParser.fWroteOut = true;
Cary Clarkf059e7c2017-12-20 14:53:21 -0500498 return !fFailed;
Cary Clark8032b982017-07-28 11:04:54 -0400499}
500
501IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
502 const string& name) {
503 string className;
504 const Definition* test = fParent;
505 while (Definition::Type::kFileType != test->fType) {
506 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
507 className = test->fName + "::";
508 break;
509 }
510 test = test->fParent;
511 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400512 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400513 unordered_map<string, IClassDefinition>& map = fIClassMap;
514 IClassDefinition& markupDef = map[className];
515 if (markupDef.fStart) {
516 typedef IClassDefinition* IClassDefPtr;
517 return INHERITED::reportError<IClassDefPtr>("class already defined");
518 }
519 markupDef.fFileName = fFileName;
520 markupDef.fStart = includeDef.fStart;
521 markupDef.fContentStart = includeDef.fStart;
522 markupDef.fName = className;
523 markupDef.fContentEnd = includeDef.fContentEnd;
524 markupDef.fTerminator = includeDef.fTerminator;
525 markupDef.fParent = fParent;
526 markupDef.fLineCount = fLineCount;
527 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
528 MarkType::kStruct : MarkType::kClass;
529 markupDef.fKeyWord = includeDef.fKeyWord;
530 markupDef.fType = Definition::Type::kMark;
531 fParent = &markupDef;
532 return &markupDef;
533}
534
535void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
536 auto& tokens = classDef.fTokens;
537 for (auto& token : tokens) {
538 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
539 continue;
540 }
541 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400542 this->writeString(
543 "# ------------------------------------------------------------------------------");
544 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400545 }
546 switch (token.fMarkType) {
547 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500548 case MarkType::kEnumClass:
Cary Clark2dc84ad2018-01-26 12:56:22 -0500549 this->dumpEnum(token, token.fName);
Cary Clark8032b982017-07-28 11:04:54 -0400550 break;
551 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400552 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400553 break;
554 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400555 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400556 continue;
557 break;
558 default:
559 SkASSERT(0);
560 }
561 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400562 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400563 this->lf(1);
564 this->writeString("// incomplete");
565 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400566 this->writeEndTag();
567 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400568 this->writeTag("SeeAlso");
569 this->writeSpace();
570 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400571 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500572 switch (token.fMarkType) {
573 case MarkType::kEnum:
574 case MarkType::kEnumClass:
575 this->writeEndTag("Enum");
576 break;
577 case MarkType::kMethod:
578 this->writeEndTag("Method");
579 break;
580 case MarkType::kMember:
581 this->writeEndTag("Member");
582 continue;
583 break;
584 default:
585 SkASSERT(0);
586 }
Cary Clark9174bda2017-09-19 17:39:32 -0400587 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400588 }
589}
Cary Clark9174bda2017-09-19 17:39:32 -0400590void IncludeParser::dumpComment(const Definition& token) {
591 fLineCount = token.fLineCount;
592 fChar = fLine = token.fContentStart;
593 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400594 bool sawParam = false;
595 bool multiline = false;
596 bool sawReturn = false;
597 bool sawComment = false;
598 bool methodHasReturn = false;
599 vector<string> methodParams;
600 vector<string> foundParams;
601 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400602 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
603 token.fLineCount);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500604 bool debugCode = methodParser.skipExact("SkDEBUGCODE(");
Cary Clark9174bda2017-09-19 17:39:32 -0400605 if (MarkType::kMethod == token.fMarkType) {
Cary Clark2dc84ad2018-01-26 12:56:22 -0500606 methodName.fName = debugCode ? token.fName : string(token.fContentStart,
Cary Clark9174bda2017-09-19 17:39:32 -0400607 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400608 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500609 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400610 && !methodParser.strnchr('~', methodParser.fEnd);
611 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
612 const char* nextEnd = paren;
613 do {
614 string paramName;
615 methodParser.fChar = nextEnd + 1;
616 methodParser.skipSpace();
617 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
618 continue;
619 }
620 methodParams.push_back(paramName);
621 } while (')' != nextEnd[0]);
622 }
Cary Clark9174bda2017-09-19 17:39:32 -0400623 for (const auto& child : token.fTokens) {
624 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
625 break;
626 }
Cary Clark8032b982017-07-28 11:04:54 -0400627 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400628 if (child.fPrivate) {
629 break;
630 }
Cary Clark8032b982017-07-28 11:04:54 -0400631 if ('@' == child.fContentStart[0]) {
632 TextParser parser(&child);
633 do {
634 parser.next();
635 if (parser.startsWith("param ")) {
636 parser.skipWord("param");
637 const char* parmStart = parser.fChar;
638 parser.skipToSpace();
639 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
640 parser.skipWhiteSpace();
641 do {
642 size_t nextComma = parmName.find(',');
643 string piece;
644 if (string::npos == nextComma) {
645 piece = parmName;
646 parmName = "";
647 } else {
648 piece = parmName.substr(0, nextComma);
649 parmName = parmName.substr(nextComma + 1);
650 }
651 if (sawParam) {
652 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400653 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400654 }
Cary Clark9174bda2017-09-19 17:39:32 -0400655 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400656 } else {
657 if (sawComment) {
658 this->nl();
659 }
660 this->lf(2);
661 }
662 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400663 this->writeTag("Param", piece);
664 this->writeSpace(2);
665 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
666 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400667 sawParam = true;
668 sawComment = false;
669 } while (parmName.length());
670 parser.skipTo(parser.fEnd);
671 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
672 parser.skipWord("return");
673 if ('s' == parser.peek()) {
674 parser.next();
675 }
676 if (sawParam) {
677 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400678 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400679 }
Cary Clark9174bda2017-09-19 17:39:32 -0400680 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400681 }
682 this->checkForMissingParams(methodParams, foundParams);
683 sawParam = false;
684 sawComment = false;
685 multiline = false;
686 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400687 this->writeTag("Return");
688 this->writeSpace(2);
689 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
690 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400691 sawReturn = true;
692 parser.skipTo(parser.fEnd);
693 } else {
694 this->reportError("unexpected doxygen directive");
695 }
696 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400697 } else if (child.length() > 1) {
698 const char* start = child.fContentStart;
699 ptrdiff_t length = child.fContentEnd - start;
700 SkASSERT(length >= 0);
701 while (length && '/' == start[0]) {
702 start += 1;
703 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400704 }
Cary Clark9174bda2017-09-19 17:39:32 -0400705 while (length && '/' == start[length - 1]) {
706 length -= 1;
707 if (length && '*' == start[length - 1]) {
708 length -= 1;
709 }
710 }
711 if (length) {
712 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
713 if (sawParam || sawReturn) {
714 this->indentToColumn(8);
715 }
716 this->writeBlock(length, start);
717 this->writeSpace();
718 sawComment = true;
719 if (sawParam || sawReturn) {
720 multiline = true;
721 }
Cary Clark8032b982017-07-28 11:04:54 -0400722 }
723 }
724 }
725 }
726 if (sawParam || sawReturn) {
727 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400728 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400729 }
Cary Clark9174bda2017-09-19 17:39:32 -0400730 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400731 }
732 if (!sawReturn) {
733 if (!sawParam) {
734 if (sawComment) {
735 this->nl();
736 }
737 this->lf(2);
738 }
739 this->checkForMissingParams(methodParams, foundParams);
740 }
741 if (methodHasReturn != sawReturn) {
742 if (!methodHasReturn) {
743 this->reportError("unexpected doxygen return");
744 } else {
745 if (sawComment) {
746 this->nl();
747 }
748 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400749 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400750 }
751 }
752}
753
Cary Clark2dc84ad2018-01-26 12:56:22 -0500754void IncludeParser::dumpEnum(const Definition& token, const string& name) {
755 this->writeTag("Enum", name);
Cary Clark9174bda2017-09-19 17:39:32 -0400756 this->lf(2);
757 this->writeString("#Code");
758 this->lfAlways(1);
759 this->indentToColumn(4);
760 this->writeString("enum");
761 this->writeSpace();
762 if ("_anonymous" != token.fName.substr(0, 10)) {
763 this->writeString(token.fName);
764 this->writeSpace();
765 }
766 this->writeString("{");
767 this->lfAlways(1);
768 for (auto& child : token.fChildren) {
769 this->indentToColumn(8);
770 this->writeString(child->fName);
771 if (child->length()) {
772 this->writeSpace();
773 this->writeBlock(child->length(), child->fContentStart);
774 }
775 if (',' != fLastChar) {
776 this->writeString(",");
777 }
778 this->lfAlways(1);
779 }
780 this->indentToColumn(4);
781 this->writeString("};");
782 this->lf(1);
783 this->writeString("##");
784 this->lf(2);
785 this->dumpComment(token);
786 for (auto& child : token.fChildren) {
787 // start here;
788 // get comments before
789 // or after const values
790 this->writeString("#Const");
791 this->writeSpace();
792 this->writeString(child->fName);
793 TextParser val(child);
794 if (!val.eof()) {
795 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
796 val.next();
797 val.skipSpace();
798 const char* valEnd = val.anyOf(",\n");
799 if (!valEnd) {
800 valEnd = val.fEnd;
801 }
802 this->writeSpace();
803 this->writeBlock(valEnd - val.fStart, val.fStart);
804 } else {
805 this->writeSpace();
806 this->writeDefinition(*child);
807 }
808 }
809 this->lf(1);
810 for (auto comment : child->fChildren) {
811 if (MarkType::kComment == comment->fMarkType) {
812 TextParser parser(comment);
813 parser.skipExact("*");
814 parser.skipExact("*");
815 while (!parser.eof() && parser.skipWhiteSpace()) {
816 parser.skipExact("*");
817 parser.skipWhiteSpace();
818 const char* start = parser.fChar;
819 parser.skipToEndBracket('\n');
820 this->lf(1);
821 this->writeBlock(parser.fChar - start, start);
822 }
823 }
824 }
825 this->writeEndTag();
826 }
827 this->lf(2);
828}
829
830void IncludeParser::dumpMethod(const Definition& token) {
831 this->writeString("#Method");
832 this->writeSpace();
833 if ("SK_TO_STRING_NONVIRT" == token.fName) {
834 this->writeString("void toString(SkString* str) const;");
835 this->lf(2);
836 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
837 this->lf(2);
838 this->writeTag("Private");
839 this->lf(1);
840 this->writeString("macro expands to: void toString(SkString* str) const;");
841 this->writeEndTag();
842 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400843 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400844 "Creates string representation. The representation is read by\n"
845 "internal debugging tools. The interface and implementation may be\n"
846 "suppressed by defining SK_IGNORE_TO_STRING.";
847 this->writeBlock(sizeof(desc) - 1, desc);
848 this->lf(2);
849 this->writeTag("Param", "str");
850 this->writeSpace(2);
851 this->writeString("storage for string representation");
852 this->writeSpace();
853 this->writeString("##");
854 this->lf(2);
855 return;
856 }
857 this->writeBlock(token.length(), token.fStart);
858 this->lf(1);
859 this->dumpComment(token);
860}
861
862void IncludeParser::dumpMember(const Definition& token) {
863 this->writeTag("Member");
864 this->writeSpace();
865 this->writeDefinition(token, token.fName, 2);
866 lf(1);
867 for (auto child : token.fChildren) {
868 this->writeDefinition(*child);
869 }
870 this->writeEndTag();
871 lf(2);
872}
873
Cary Clarkd0530ba2017-09-14 11:25:39 -0400874bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400875 for (const auto& member : fIClassMap) {
876 if (string::npos != member.first.find("::")) {
877 continue;
878 }
879 if (!this->dumpTokens(dir, member.first)) {
880 return false;
881 }
882 }
883 return true;
884}
885
Ben Wagner63fd7602017-10-09 15:45:33 -0400886 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400887bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400888 string fileName = dir;
889 if (dir.length() && '/' != dir[dir.length() - 1]) {
890 fileName += '/';
891 }
892 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400893 fOut = fopen(fileName.c_str(), "wb");
894 if (!fOut) {
895 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400896 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400897 }
898 string prefixName = skClassName.substr(0, 2);
899 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
900 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400901 this->writeTagNoLF("Topic", topicName);
902 this->writeTag("Alias", topicName + "_Reference");
903 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400904 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500905 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
906 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400907 this->writeTag(containerType, skClassName);
908 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400909 auto& tokens = classMap.fTokens;
910 for (auto& token : tokens) {
911 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
912 continue;
913 }
Cary Clark9174bda2017-09-19 17:39:32 -0400914 this->writeDefinition(token);
915 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400916 }
917 this->lf(2);
918 string className(skClassName.substr(2));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500919 vector<string> classNames;
920 vector<string> constNames;
921 vector<string> constructorNames;
922 vector<string> memberNames;
923 vector<string> operatorNames;
924 size_t classMaxLen = 0;
925 size_t constMaxLen = 0;
926 size_t constructorMaxLen = 0;
927 size_t memberMaxLen = 0;
928 size_t operatorMaxLen = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400929 for (const auto& oneClass : fIClassMap) {
930 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
931 continue;
932 }
933 string structName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark2dc84ad2018-01-26 12:56:22 -0500934 classMaxLen = SkTMax(classMaxLen, structName.length());
935 classNames.emplace_back(structName);
Cary Clark8032b982017-07-28 11:04:54 -0400936 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500937 for (const auto& oneEnum : fIEnumMap) {
938 string enumName = oneEnum.first;
939 constMaxLen = SkTMax(constMaxLen, enumName.length());
940 constNames.emplace_back(enumName);
Cary Clark8032b982017-07-28 11:04:54 -0400941 }
Cary Clark8032b982017-07-28 11:04:54 -0400942 for (const auto& token : classMap.fTokens) {
943 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
944 continue;
945 }
Cary Clark9174bda2017-09-19 17:39:32 -0400946 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400947 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
948 continue;
949 }
Cary Clark9174bda2017-09-19 17:39:32 -0400950 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
951 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500952 constructorMaxLen = SkTMax(constructorMaxLen, name.length());
Cary Clark9174bda2017-09-19 17:39:32 -0400953 constructorNames.emplace_back(name);
954 continue;
955 }
956 if (name.substr(0, 8) == "operator") {
957 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500958 operatorMaxLen = SkTMax(operatorMaxLen, name.length());
Cary Clark9174bda2017-09-19 17:39:32 -0400959 operatorNames.emplace_back(name);
960 continue;
961 }
Cary Clark8032b982017-07-28 11:04:54 -0400962 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
963 continue;
964 }
Cary Clark9174bda2017-09-19 17:39:32 -0400965 if ("SK_TO_STRING_NONVIRT" == name) {
966 name = "toString";
967 }
Cary Clark8032b982017-07-28 11:04:54 -0400968 size_t paren = name.find('(');
969 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark2dc84ad2018-01-26 12:56:22 -0500970 memberMaxLen = SkTMax(memberMaxLen, funcLen);
971 memberNames.emplace_back(name);
Cary Clark8032b982017-07-28 11:04:54 -0400972 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500973 this->writeTag("Topic", "Overview");
974 this->lf(2);
975 this->writeTag("Subtopic", "Subtopics");
976 string classesName = classMaxLen ? "Classes_and_Structs" : "";
977 string constsName = constructorMaxLen ? "Constants" : "";
978 string constructorsName = constructorMaxLen ? "Constructors" : "";
979 string membersName = memberMaxLen ? "Member_Functions" : "";
980 string operatorsName = operatorMaxLen ? "Operators" : "";
981 size_t nameLen = SkTMax(classesName.size(), SkTMax(constsName.size(),
982 SkTMax(constructorsName.size(), SkTMax(membersName.size(), operatorsName.size()))));
983 this->writeTableHeader("name", nameLen, "description");
984 string classDesc = classMaxLen ? "embedded struct and class members" : "";
985 string constDesc = constMaxLen ? "enum and enum class, const values" : "";
986 string constructorDesc = constructorMaxLen ? "functions that construct " + className : "";
987 string memberDesc = memberMaxLen ? "static functions and member methods" : "";
988 string operatorDesc = operatorMaxLen ? "operator overloading methods" : "";
989 size_t descLen = SkTMax(classDesc.size(), SkTMax(constDesc.size(), SkTMax(constructorDesc.size(),
990 SkTMax(memberDesc.size(), operatorDesc.size()))));
991 if (classMaxLen) {
992 this->writeTableRow(nameLen, classesName, descLen, classDesc);
Cary Clark9174bda2017-09-19 17:39:32 -0400993 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500994 if (constMaxLen) {
995 this->writeTableRow(nameLen, constsName, descLen, constDesc);
Cary Clark9174bda2017-09-19 17:39:32 -0400996 }
Cary Clark2dc84ad2018-01-26 12:56:22 -0500997 if (constructorMaxLen) {
998 this->writeTableRow(nameLen, constructorsName, descLen, constructorDesc);
999 }
1000 if (memberMaxLen) {
1001 this->writeTableRow(nameLen, membersName, descLen, memberDesc);
1002 }
1003 if (operatorMaxLen) {
1004 this->writeTableRow(nameLen, operatorsName, descLen, operatorDesc);
Cary Clark8032b982017-07-28 11:04:54 -04001005 }
Cary Clark9174bda2017-09-19 17:39:32 -04001006 this->writeTableTrailer();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001007 this->writeEndTag();
Cary Clark9174bda2017-09-19 17:39:32 -04001008 this->lf(2);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001009 if (classMaxLen) {
1010 std::sort(classNames.begin(), classNames.end());
1011 this->writeTag("Subtopic", "Classes_and_Structs");
1012 this->writeTableHeader("name", classMaxLen, "description");
1013 for (auto& name : classNames) {
1014 this->writeTableRow(classMaxLen, name);
1015 }
1016 this->writeTableTrailer();
1017 this->writeEndTag("Subtopic");
1018 this->lf(2);
1019 }
1020 if (constMaxLen) {
1021 std::sort(constNames.begin(), constNames.end());
1022 this->writeTag("Subtopic", "Constants");
1023 this->writeTableHeader("name", constMaxLen, "description");
1024 for (auto& name : constNames) {
1025 this->writeTableRow(constMaxLen, name);
1026 }
1027 this->writeTableTrailer();
1028 this->writeEndTag("Subtopic");
1029 this->lf(2);
1030 }
1031 if (constructorMaxLen) {
1032 std::sort(constructorNames.begin(), constructorNames.end());
1033 this->writeTag("Subtopic", "Constructors");
1034 this->writeTableHeader("name", constructorMaxLen, "description");
1035 for (auto& name : constructorNames) {
1036 this->writeTableRow(constructorMaxLen, name);
1037 }
1038 this->writeTableTrailer();
1039 this->writeEndTag("Subtopic");
1040 this->lf(2);
1041 }
1042 if (operatorMaxLen) {
1043 std::sort(operatorNames.begin(), operatorNames.end());
1044 this->writeTag("Subtopic", "Operators");
1045 this->writeTableHeader("name", operatorMaxLen, "description");
1046 for (auto& name : operatorNames) {
1047 this->writeTableRow(operatorMaxLen, name);
1048 }
1049 this->writeTableTrailer();
1050 this->writeEndTag("Subtopic");
1051 this->lf(2);
1052 }
1053 if (memberMaxLen) {
1054 std::sort(memberNames.begin(), memberNames.end());
1055 this->writeTag("Subtopic", "Member_Functions");
1056 this->writeTableHeader("name", memberMaxLen, "description");
1057 for (auto& name : memberNames) {
1058 size_t paren = name.find('(');
1059 size_t funcLen = string::npos == paren ? name.length() : paren;
1060 this->writeTableRow(memberMaxLen, name.substr(0, funcLen));
1061 }
1062 this->writeTableTrailer();
1063 this->writeEndTag("Subtopic");
1064 this->lf(2);
1065 }
Cary Clark9174bda2017-09-19 17:39:32 -04001066 this->writeEndTag("Topic");
1067 this->lf(2);
Cary Clark2dc84ad2018-01-26 12:56:22 -05001068 for (auto& oneEnum : fIEnumMap) {
1069 this->writeString(
1070 "# ------------------------------------------------------------------------------");
1071 this->dumpEnum(oneEnum.second, oneEnum.first);
1072 this->lf(2);
1073 this->writeTag("Example");
1074 this->lfcr();
1075 this->writeString("// incomplete");
1076 this->writeEndTag();
1077 this->lf(2);
1078 this->writeTag("SeeAlso", "incomplete");
1079 this->lf(2);
1080 this->writeEndTag("Enum", oneEnum.first);
1081 this->lf(2);
1082 }
Cary Clark8032b982017-07-28 11:04:54 -04001083 for (auto& oneClass : fIClassMap) {
1084 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
1085 continue;
1086 }
1087 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -04001088 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -04001089 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -04001090 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -05001091 KeyWord keyword = oneClass.second.fKeyWord;
1092 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
1093 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -04001094 this->writeTag(containerType, innerName);
1095 this->lf(2);
1096 this->writeTag("Code");
1097 this->writeEndTag("ToDo", "fill this in manually");
1098 this->writeEndTag();
1099 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001100 for (auto& token : oneClass.second.fTokens) {
1101 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
1102 continue;
1103 }
Cary Clark9174bda2017-09-19 17:39:32 -04001104 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -04001105 }
1106 this->lf(2);
1107 this->dumpClassTokens(oneClass.second);
1108 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001109 this->writeEndTag(containerType, innerName);
1110 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001111 }
1112 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001113 this->writeEndTag(containerType, skClassName);
1114 this->lf(2);
1115 this->writeEndTag("Topic", topicName);
1116 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001117 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001118 SkDebugf("wrote %s\n", fileName.c_str());
1119 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001120}
1121
1122bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1123 // add comment preceding class, if any
1124 const Definition* parent = includeDef.fParent;
1125 int index = includeDef.fParentIndex;
1126 auto wordIter = parent->fTokens.begin();
1127 std::advance(wordIter, index);
1128 SkASSERT(&*wordIter == &includeDef);
1129 while (parent->fTokens.begin() != wordIter) {
1130 auto testIter = std::prev(wordIter);
1131 if (Definition::Type::kWord != testIter->fType
1132 && Definition::Type::kKeyWord != testIter->fType
1133 && (Definition::Type::kBracket != testIter->fType
1134 || Bracket::kAngle != testIter->fBracket)
1135 && (Definition::Type::kPunctuation != testIter->fType
1136 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1137 break;
1138 }
1139 wordIter = testIter;
1140 }
1141 auto commentIter = wordIter;
1142 while (parent->fTokens.begin() != commentIter) {
1143 auto testIter = std::prev(commentIter);
1144 bool isComment = Definition::Type::kBracket == testIter->fType
1145 && (Bracket::kSlashSlash == testIter->fBracket
1146 || Bracket::kSlashStar == testIter->fBracket);
1147 if (!isComment) {
1148 break;
1149 }
1150 commentIter = testIter;
1151 }
1152 while (commentIter != wordIter) {
1153 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1154 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1155 return false;
1156 }
1157 commentIter = std::next(commentIter);
1158 }
1159 return true;
1160}
1161
Cary Clarkbad5ad72017-08-03 17:14:08 -04001162bool IncludeParser::internalName(const Definition& token) const {
1163 return 0 == token.fName.find("internal_")
1164 || 0 == token.fName.find("Internal_")
1165 || 0 == token.fName.find("legacy_")
1166 || 0 == token.fName.find("temporary_")
1167 || 0 == token.fName.find("private_");
1168}
1169
Cary Clark8032b982017-07-28 11:04:54 -04001170// caller calls reportError, so just return false here
1171bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1172 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001173 // parse class header
1174 auto iter = includeDef->fTokens.begin();
1175 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1176 // todo : documentation is ignoring this for now
1177 iter = std::next(iter);
1178 }
1179 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1180 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001181 iter = std::next(iter);
1182 if (iter == includeDef->fTokens.end()) {
1183 return true; // forward declaration only
1184 }
Cary Clark8032b982017-07-28 11:04:54 -04001185 do {
1186 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001187 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001188 }
1189 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1190 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001191 }
Cary Clark8032b982017-07-28 11:04:54 -04001192 } while (static_cast<void>(iter = std::next(iter)), true);
1193 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001194 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001195 }
1196 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1197 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001198 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001199 }
1200 markupDef->fStart = iter->fStart;
1201 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001202 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001203 }
1204// if (1 != includeDef->fChildren.size()) {
1205// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1206// }
1207 includeDef = includeDef->fChildren.front();
1208 iter = includeDef->fTokens.begin();
1209 // skip until public
1210 int publicIndex = 0;
1211 if (IsStruct::kNo == isStruct) {
1212 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1213 size_t publicLen = strlen(publicName);
1214 while (iter != includeDef->fTokens.end()
1215 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1216 || strncmp(iter->fStart, publicName, publicLen))) {
1217 iter = std::next(iter);
1218 ++publicIndex;
1219 }
1220 }
1221 auto childIter = includeDef->fChildren.begin();
1222 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1223 (*childIter)->fPrivate = true;
1224 childIter = std::next(childIter);
1225 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001226 int keyIndex = publicIndex;
1227 KeyWord currentKey = KeyWord::kPublic;
1228 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1229 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001230 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1231 size_t protectedLen = strlen(protectedName);
1232 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1233 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001234 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001235 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001236 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1237 const char* testStart = iter->fStart;
1238 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1239 iter = std::next(iter);
1240 ++keyIndex;
1241 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1242 currentKey = KeyWord::kPublic;
1243 break;
1244 }
1245 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1246 currentKey = KeyWord::kProtected;
1247 break;
1248 }
1249 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1250 currentKey = KeyWord::kPrivate;
1251 break;
1252 }
1253 }
1254 fLastObject = nullptr;
1255 if (KeyWord::kPublic == currentKey) {
1256 if (!this->parseObject(child, markupDef)) {
1257 return false;
1258 }
1259 } else {
1260 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001261 }
Cary Clark73fa9722017-08-29 17:36:51 -04001262 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001263 childIter = std::next(childIter);
1264 }
Cary Clark8032b982017-07-28 11:04:54 -04001265 SkASSERT(fParent->fParent);
1266 fParent = fParent->fParent;
1267 return true;
1268}
1269
1270bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1271 int lineCount, Definition* markupDef) {
1272 TextParser parser(filename, start, end, lineCount);
1273 // parse doxygen if present
1274 if (parser.startsWith("**")) {
1275 parser.next();
1276 parser.next();
1277 parser.skipWhiteSpace();
1278 if ('\\' == parser.peek()) {
1279 parser.next();
1280 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1281 return reportError<bool>("missing object type");
1282 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001283 if (!parser.skipWord(markupDef->fName.c_str()) &&
1284 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001285 return reportError<bool>("missing object name");
1286 }
1287
1288 }
1289 }
1290 // remove leading '*' if present
1291 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1292 while (!parser.eof() && parser.skipWhiteSpace()) {
1293 while ('*' == parser.peek()) {
1294 parser.next();
1295 if (parser.eof()) {
1296 break;
1297 }
1298 parser.skipWhiteSpace();
1299 }
1300 if (parser.eof()) {
1301 break;
1302 }
1303 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001304 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001305 parser.fLineCount, parent, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001306 parser.skipToEndBracket('\n');
1307 }
1308 return true;
1309}
1310
1311bool IncludeParser::parseDefine() {
1312
1313 return true;
1314}
1315
1316bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001317 TextParser parser(child);
1318 parser.skipToEndBracket('{');
1319 if (parser.eof()) {
1320 return true; // if enum is a forward declaration, do nothing
1321 }
1322 parser.next();
1323 string nameStr;
Cary Clark8032b982017-07-28 11:04:54 -04001324 if (child->fTokens.size() > 0) {
1325 auto token = child->fTokens.begin();
1326 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1327 token = token->fTokens.begin();
1328 }
1329 if (Definition::Type::kWord == token->fType) {
1330 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1331 }
1332 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001333 Definition* markupChild;
1334 if (!markupDef) {
1335 auto finder = fIEnumMap.find(nameStr);
1336 if (fIEnumMap.end() != finder) {
1337 return child->reportError<bool>("duplicate global enum name");
1338 }
1339 markupChild = &fIEnumMap[nameStr];
1340 markupChild->fContentStart = child->fContentStart;
1341 markupChild->fName = nameStr;
1342 markupChild->fFiddle = nameStr;
1343 markupChild->fContentEnd = child->fContentEnd;
1344 markupChild->fFileName = child->fFileName;
1345 markupChild->fLineCount = child->fLineCount;
1346 } else {
1347 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001348 child->fLineCount, markupDef, '\0');
Cary Clark2dc84ad2018-01-26 12:56:22 -05001349 markupChild = &markupDef->fTokens.back();
1350 }
Cary Clark8032b982017-07-28 11:04:54 -04001351 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1352 markupChild->fKeyWord = KeyWord::kEnum;
1353 TextParser enumName(child);
1354 enumName.skipExact("enum ");
Cary Clark2dc84ad2018-01-26 12:56:22 -05001355 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001356 if (enumName.skipExact("class ")) {
Cary Clark2dc84ad2018-01-26 12:56:22 -05001357 enumName.skipWhiteSpace();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001358 markupChild->fMarkType = MarkType::kEnumClass;
1359 }
Cary Clark8032b982017-07-28 11:04:54 -04001360 const char* nameStart = enumName.fChar;
1361 enumName.skipToSpace();
Cary Clark2dc84ad2018-01-26 12:56:22 -05001362 if (markupDef) {
Cary Clark06c20f32018-03-20 15:53:27 -04001363 markupChild->fName = markupDef->fName + "::" +
1364 string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -05001365 }
Cary Clark06c20f32018-03-20 15:53:27 -04001366 if (string::npos != markupChild->fName.find("SkColorType")) {
1367 SkDebugf("");
1368 }
Cary Clark8032b982017-07-28 11:04:54 -04001369 if (!this->findComments(*child, markupChild)) {
1370 return false;
1371 }
Cary Clark8032b982017-07-28 11:04:54 -04001372 const char* dataEnd;
1373 do {
Cary Clark8032b982017-07-28 11:04:54 -04001374 parser.skipWhiteSpace();
1375 if ('}' == parser.peek()) {
1376 break;
1377 }
1378 Definition* comment = nullptr;
1379 // note that comment, if any, can be before or after (on the same line, though) as member
1380 if ('#' == parser.peek()) {
1381 // fixme: handle preprecessor, but just skip it for now
1382 parser.skipToLineStart();
1383 }
1384 while (parser.startsWith("/*") || parser.startsWith("//")) {
1385 parser.next();
1386 const char* start = parser.fChar;
1387 const char* end;
1388 if ('*' == parser.peek()) {
1389 end = parser.strnstr("*/", parser.fEnd);
1390 parser.fChar = end;
1391 parser.next();
1392 parser.next();
1393 } else {
1394 end = parser.trimmedLineEnd();
1395 parser.skipToLineStart();
1396 }
1397 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001398 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001399 comment = &markupChild->fTokens.back();
1400 comment->fTerminator = end;
1401 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1402 return false;
1403 }
1404 parser.skipWhiteSpace();
1405 }
1406 parser.skipWhiteSpace();
1407 const char* memberStart = parser.fChar;
1408 if ('}' == memberStart[0]) {
1409 break;
1410 }
Cary Clark9174bda2017-09-19 17:39:32 -04001411 // if there's comment on same the line as member def, output first as if it was before
1412
Cary Clark8032b982017-07-28 11:04:54 -04001413 parser.skipToNonAlphaNum();
1414 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001415 if (parser.eof() || !parser.skipWhiteSpace()) {
1416 return this->reportError<bool>("enum member must end with comma 1");
1417 }
Cary Clark8032b982017-07-28 11:04:54 -04001418 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001419 if ('=' == parser.peek()) {
1420 parser.skipToEndBracket(',');
1421 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001422 if (!parser.eof() && '#' == parser.peek()) {
1423 // fixme: handle preprecessor, but just skip it for now
1424 continue;
1425 }
Cary Clark9174bda2017-09-19 17:39:32 -04001426 if (parser.eof() || ',' != parser.peek()) {
1427 return this->reportError<bool>("enum member must end with comma 2");
1428 }
1429 dataEnd = parser.fChar;
1430 const char* start = parser.anyOf("/\n");
1431 SkASSERT(start);
1432 parser.skipTo(start);
1433 if ('/' == parser.next()) {
1434 char slashStar = parser.next();
1435 if ('/' == slashStar || '*' == slashStar) {
1436 TextParser::Save save(&parser);
1437 char doxCheck = parser.next();
1438 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1439 save.restore();
1440 }
1441 }
1442 parser.skipWhiteSpace();
1443 const char* commentStart = parser.fChar;
1444 if ('/' == slashStar) {
1445 parser.skipToEndBracket('\n');
1446 } else {
1447 parser.skipToEndBracket("*/");
1448 }
1449 SkASSERT(!parser.eof());
1450 const char* commentEnd = parser.fChar;
1451 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001452 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001453 comment = &markupChild->fTokens.back();
1454 comment->fTerminator = commentEnd;
1455 }
Cary Clark8032b982017-07-28 11:04:54 -04001456 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001457 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001458 Definition* member = &markupChild->fTokens.back();
1459 member->fName = memberName;
1460 if (comment) {
1461 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001462 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001463 }
1464 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001465 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001466 for (auto outsideMember : child->fChildren) {
1467 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001468 continue;
1469 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001470 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1471 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001472 continue;
1473 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001474 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1475 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001476 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001477 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001478 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001479 // FIXME: ? add comment as well ?
1480 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001481 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001482 if (markupDef) {
1483 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1484 SkASSERT(classDef.fStart);
1485 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1486 markupChild->fName = uniqueName;
1487 classDef.fEnums[uniqueName] = markupChild;
1488 }
Cary Clark8032b982017-07-28 11:04:54 -04001489 return true;
1490}
1491
1492bool IncludeParser::parseInclude(const string& name) {
1493 fParent = &fIncludeMap[name];
1494 fParent->fName = name;
1495 fParent->fFileName = fFileName;
1496 fParent->fType = Definition::Type::kFileType;
1497 fParent->fContentStart = fChar;
1498 fParent->fContentEnd = fEnd;
1499 // parse include file into tree
1500 while (fChar < fEnd) {
1501 if (!this->parseChar()) {
1502 return false;
1503 }
1504 }
1505 // parse tree and add named objects to maps
1506 fParent = &fIncludeMap[name];
1507 if (!this->parseObjects(fParent, nullptr)) {
1508 return false;
1509 }
1510 return true;
1511}
1512
1513bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1514 const char* typeStart = child->fChildren[0]->fContentStart;
1515 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001516 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001517 Definition* markupChild = &markupDef->fTokens.back();
1518 TextParser nameParser(child);
1519 nameParser.skipToNonAlphaNum();
1520 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1521 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1522 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1523 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001524 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001525 classDef.fMembers[uniqueName] = markupChild;
1526 if (child->fParentIndex >= 2) {
1527 auto comment = child->fParent->fTokens.begin();
1528 std::advance(comment, child->fParentIndex - 2);
1529 if (Definition::Type::kBracket == comment->fType
1530 && (Bracket::kSlashStar == comment->fBracket
1531 || Bracket::kSlashSlash == comment->fBracket)) {
1532 TextParser parser(&*comment);
1533 do {
1534 parser.skipToAlpha();
1535 if (parser.eof()) {
1536 break;
1537 }
1538 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001539 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001540 if (Bracket::kSlashStar == comment->fBracket) {
1541 const char* commentEnd = parser.strnstr("*/", end);
1542 if (commentEnd) {
1543 end = commentEnd;
1544 }
1545 }
1546 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001547 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001548 Definition* commentChild = &markupDef->fTokens.back();
1549 markupChild->fChildren.emplace_back(commentChild);
1550 parser.skipTo(end);
1551 } while (!parser.eof());
1552 }
1553 }
1554 return true;
1555}
1556
1557bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1558 auto tokenIter = child->fParent->fTokens.begin();
1559 std::advance(tokenIter, child->fParentIndex);
1560 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001561 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001562 bool addConst = false;
1563 auto operatorCheck = tokenIter;
1564 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1565 operatorCheck = std::prev(tokenIter);
1566 }
1567 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001568 auto closeParen = std::next(tokenIter);
1569 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1570 '(' == closeParen->fContentStart[0]);
1571 nameEnd = closeParen->fContentEnd + 1;
1572 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001573 if (Definition::Type::kKeyWord == closeParen->fType &&
1574 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001575 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001576 }
Cary Clarka560c472017-11-27 10:44:06 -05001577 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001578 }
1579 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001580 if (addConst) {
1581 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001582 }
Cary Clark8032b982017-07-28 11:04:54 -04001583 while (tokenIter != child->fParent->fTokens.begin()) {
1584 auto testIter = std::prev(tokenIter);
1585 switch (testIter->fType) {
1586 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001587 if (testIter == child->fParent->fTokens.begin() &&
1588 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1589 KeyWord::kIfndef == child->fParent->fKeyWord ||
1590 KeyWord::kIf == child->fParent->fKeyWord)) {
1591 std::next(tokenIter);
1592 break;
1593 }
Cary Clark8032b982017-07-28 11:04:54 -04001594 goto keepGoing;
1595 case Definition::Type::kKeyWord: {
1596 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1597 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1598 goto keepGoing;
1599 }
1600 } break;
1601 case Definition::Type::kBracket:
1602 if (Bracket::kAngle == testIter->fBracket) {
1603 goto keepGoing;
1604 }
1605 break;
1606 case Definition::Type::kPunctuation:
1607 if (Punctuation::kSemicolon == testIter->fPunctuation
1608 || Punctuation::kLeftBrace == testIter->fPunctuation
1609 || Punctuation::kColon == testIter->fPunctuation) {
1610 break;
1611 }
1612 keepGoing:
1613 tokenIter = testIter;
1614 continue;
1615 default:
1616 break;
1617 }
1618 break;
1619 }
1620 tokenIter->fName = nameStr;
1621 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001622 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001623 auto testIter = child->fParent->fTokens.begin();
1624 SkASSERT(child->fParentIndex > 0);
1625 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001626 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1627 0 == tokenIter->fParentIndex) {
1628 tokenIter = std::next(tokenIter);
1629 }
Cary Clark8032b982017-07-28 11:04:54 -04001630 const char* start = tokenIter->fContentStart;
1631 const char* end = tokenIter->fContentEnd;
1632 const char kDebugCodeStr[] = "SkDEBUGCODE";
1633 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1634 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1635 std::advance(testIter, 1);
1636 start = testIter->fContentStart + 1;
1637 end = testIter->fContentEnd - 1;
1638 } else {
1639 end = testIter->fContentEnd;
1640 while (testIter != child->fParent->fTokens.end()) {
1641 testIter = std::next(testIter);
1642 switch (testIter->fType) {
1643 case Definition::Type::kPunctuation:
1644 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1645 || Punctuation::kLeftBrace == testIter->fPunctuation
1646 || Punctuation::kColon == testIter->fPunctuation);
1647 end = testIter->fStart;
1648 break;
1649 case Definition::Type::kKeyWord: {
1650 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1651 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1652 continue;
1653 }
1654 } break;
1655 default:
1656 continue;
1657 }
1658 break;
1659 }
1660 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001661 while (end > start && ' ' >= end[-1]) {
1662 --end;
1663 }
Cary Clark9174bda2017-09-19 17:39:32 -04001664 if (!markupDef) {
1665 auto parentIter = child->fParent->fTokens.begin();
1666 SkASSERT(child->fParentIndex > 0);
1667 std::advance(parentIter, child->fParentIndex - 1);
1668 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001669 TextParser nameParser(methodName);
1670 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001671 return true; // expect this is inline class definition outside of class
1672 }
Cary Clarka560c472017-11-27 10:44:06 -05001673 string name(nameParser.fLine, nameParser.lineLength());
1674 auto finder = fIFunctionMap.find(name);
1675 if (fIFunctionMap.end() != finder) {
1676 // create unique name
1677 SkASSERT(0); // incomplete
1678 }
1679 auto globalFunction = &fIFunctionMap[name];
1680 globalFunction->fContentStart = start;
1681 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001682 globalFunction->fFiddle = name;
1683 globalFunction->fContentEnd = end;
1684 globalFunction->fMarkType = MarkType::kMethod;
1685 globalFunction->fLineCount = tokenIter->fLineCount;
1686 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001687 }
Cary Clark8032b982017-07-28 11:04:54 -04001688 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001689 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001690 Definition* markupChild = &markupDef->fTokens.back();
1691 // do find instead -- I wonder if there is a way to prevent this in c++
1692 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1693 SkASSERT(classDef.fStart);
1694 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1695 markupChild->fName = uniqueName;
1696 if (!this->findComments(*child, markupChild)) {
1697 return false;
1698 }
1699 classDef.fMethods[uniqueName] = markupChild;
1700 return true;
1701}
1702
Cary Clark8032b982017-07-28 11:04:54 -04001703bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1704 for (auto& child : parent->fChildren) {
1705 if (!this->parseObject(child, markupDef)) {
1706 return false;
1707 }
1708 }
1709 return true;
1710}
1711
1712bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1713 // set up for error reporting
1714 fLine = fChar = child->fStart;
1715 fEnd = child->fContentEnd;
1716 // todo: put original line number in child as well
1717 switch (child->fType) {
1718 case Definition::Type::kKeyWord:
1719 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001720 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001721 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001722 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001723 }
1724 break;
1725 case KeyWord::kEnum:
1726 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001727 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001728 }
1729 break;
1730 case KeyWord::kStruct:
1731 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001732 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001733 }
1734 break;
1735 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001736 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001737 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001738 }
1739 break;
1740 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001741 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001742 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001743 }
1744 break;
1745 case KeyWord::kUnion:
1746 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001747 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001748 }
1749 break;
1750 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001751 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001752 }
1753 break;
1754 case Definition::Type::kBracket:
1755 switch (child->fBracket) {
1756 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001757 if (fLastObject) {
1758 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1759 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001760 if (!checkDeprecated.eof()) {
1761 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04001762 if (checkDeprecated.startsWith(gAttrDeprecated)) {
1763 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04001764 break;
1765 }
1766 }
1767 }
1768 {
1769 auto tokenIter = child->fParent->fTokens.begin();
1770 std::advance(tokenIter, child->fParentIndex);
1771 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05001772 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04001773 if (previousToken.startsWith(gAttrDeprecated)) {
1774 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04001775 break;
1776 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05001777 if (Bracket::kPound == child->fParent->fBracket &&
1778 KeyWord::kIf == child->fParent->fKeyWord) {
1779 // TODO: this will skip methods named defined() -- for the
1780 // moment there aren't any
1781 if (previousToken.startsWith("defined")) {
1782 break;
1783 }
1784 }
Cary Clark73fa9722017-08-29 17:36:51 -04001785 }
Cary Clark8032b982017-07-28 11:04:54 -04001786 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001787 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001788 }
Cary Clark89b14562018-03-19 09:04:10 -04001789 if (fAttrDeprecated) {
1790 Definition* lastMethod = &markupDef->fTokens.back();
1791 lastMethod->fDeprecated = true;
1792 fAttrDeprecated = nullptr;
1793 }
Cary Clark73fa9722017-08-29 17:36:51 -04001794 break;
Cary Clark8032b982017-07-28 11:04:54 -04001795 case Bracket::kSlashSlash:
1796 case Bracket::kSlashStar:
1797 // comments are picked up by parsing objects first
1798 break;
1799 case Bracket::kPound:
1800 // special-case the #xxx xxx_DEFINED entries
1801 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001802 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001803 case KeyWord::kIfndef:
1804 case KeyWord::kIfdef:
1805 if (child->boilerplateIfDef(fParent)) {
1806 if (!this->parseObjects(child, markupDef)) {
1807 return false;
1808 }
1809 break;
1810 }
1811 goto preproError;
1812 case KeyWord::kDefine:
1813 if (child->boilerplateDef(fParent)) {
1814 break;
1815 }
1816 goto preproError;
1817 case KeyWord::kEndif:
1818 if (child->boilerplateEndIf()) {
1819 break;
1820 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001821 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04001822 case KeyWord::kInclude:
1823 // ignored for now
1824 break;
1825 case KeyWord::kElse:
1826 case KeyWord::kElif:
1827 // todo: handle these
1828 break;
1829 default:
1830 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001831 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001832 }
1833 break;
1834 case Bracket::kAngle:
1835 // pick up templated function pieces when method is found
1836 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001837 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001838 if (!this->parseObjects(child, markupDef)) {
1839 return false;
1840 }
Cary Clark73fa9722017-08-29 17:36:51 -04001841 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001842 case Bracket::kSquare: {
1843 // check to see if parent is operator, the only case we handle so far
1844 auto prev = child->fParent->fTokens.begin();
1845 std::advance(prev, child->fParentIndex - 1);
1846 if (KeyWord::kOperator != prev->fKeyWord) {
1847 return child->reportError<bool>("expected operator overload");
1848 }
1849 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001850 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001851 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001852 }
1853 break;
1854 case Definition::Type::kWord:
1855 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001856 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001857 }
1858 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001859 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001860 }
1861 break;
1862 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001863 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001864 break;
1865 }
1866 return true;
1867}
1868
Cary Clarkbbfda252018-03-09 15:32:01 -05001869bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
1870 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04001871}
1872
Cary Clark2f466242017-12-11 16:03:17 -05001873bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1874 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05001875 typedefParser.skipExact("typedef");
1876 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05001877 string nameStr = typedefParser.typedefName();
1878 if (!markupDef) {
1879 Definition& typedefDef = fITypedefMap[nameStr];
1880 SkASSERT(!typedefDef.fStart);
1881 typedefDef.fStart = child->fContentStart;
1882 typedefDef.fContentStart = child->fContentStart;
1883 typedefDef.fName = nameStr;
1884 typedefDef.fFiddle = nameStr;
1885 typedefDef.fContentEnd = child->fContentEnd;
1886 typedefDef.fTerminator = child->fContentEnd;
1887 typedefDef.fMarkType = MarkType::kTypedef;
1888 typedefDef.fLineCount = child->fLineCount;
1889 return true;
1890 }
1891 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001892 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05001893 Definition* markupChild = &markupDef->fTokens.back();
1894 markupChild->fName = nameStr;
1895 markupChild->fTerminator = markupChild->fContentEnd;
1896 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1897 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001898 return true;
1899}
1900
1901bool IncludeParser::parseUnion() {
1902
1903 return true;
1904}
1905
1906bool IncludeParser::parseChar() {
1907 char test = *fChar;
1908 if ('\\' == fPrev) {
1909 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001910// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001911 fLine = fChar + 1;
1912 }
1913 goto done;
1914 }
1915 switch (test) {
1916 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001917// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001918 fLine = fChar + 1;
1919 if (fInChar) {
1920 return reportError<bool>("malformed char");
1921 }
1922 if (fInString) {
1923 return reportError<bool>("malformed string");
1924 }
1925 if (!this->checkForWord()) {
1926 return false;
1927 }
1928 if (Bracket::kPound == this->topBracket()) {
1929 KeyWord keyWord = fParent->fKeyWord;
1930 if (KeyWord::kNone == keyWord) {
1931 return this->reportError<bool>("unhandled preprocessor directive");
1932 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001933 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001934 this->popBracket();
1935 }
1936 } else if (Bracket::kSlashSlash == this->topBracket()) {
1937 this->popBracket();
1938 }
1939 break;
1940 case '*':
1941 if (!fInCharCommentString && '/' == fPrev) {
1942 this->pushBracket(Bracket::kSlashStar);
1943 }
1944 if (!this->checkForWord()) {
1945 return false;
1946 }
1947 if (!fInCharCommentString) {
1948 this->addPunctuation(Punctuation::kAsterisk);
1949 }
1950 break;
1951 case '/':
1952 if ('*' == fPrev) {
1953 if (!fInCharCommentString) {
1954 return reportError<bool>("malformed closing comment");
1955 }
1956 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001957 TextParser::Save save(this);
1958 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001959 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001960 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001961 }
1962 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001963 }
Cary Clark8032b982017-07-28 11:04:54 -04001964 if (!fInCharCommentString && '/' == fPrev) {
1965 this->pushBracket(Bracket::kSlashSlash);
1966 break;
1967 }
1968 if (!this->checkForWord()) {
1969 return false;
1970 }
1971 break;
1972 case '\'':
1973 if (Bracket::kChar == this->topBracket()) {
1974 this->popBracket();
1975 } else if (!fInComment && !fInString) {
1976 if (fIncludeWord) {
1977 return this->reportError<bool>("word then single-quote");
1978 }
1979 this->pushBracket(Bracket::kChar);
1980 }
1981 break;
1982 case '\"':
1983 if (Bracket::kString == this->topBracket()) {
1984 this->popBracket();
1985 } else if (!fInComment && !fInChar) {
1986 if (fIncludeWord) {
1987 return this->reportError<bool>("word then double-quote");
1988 }
1989 this->pushBracket(Bracket::kString);
1990 }
1991 break;
1992 case ':':
1993 case '(':
1994 case '[':
1995 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001996 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1997 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1998 this->pushBracket(Bracket::kDebugCode);
1999 break;
2000 }
Cary Clark8032b982017-07-28 11:04:54 -04002001 if (fInCharCommentString) {
2002 break;
2003 }
2004 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2005 break;
2006 }
2007 if (!fInBrace) {
2008 if (!this->checkForWord()) {
2009 return false;
2010 }
2011 if (':' == test && !fInFunction) {
2012 break;
2013 }
2014 if ('{' == test) {
2015 this->addPunctuation(Punctuation::kLeftBrace);
2016 } else if (':' == test) {
2017 this->addPunctuation(Punctuation::kColon);
2018 }
2019 }
2020 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2021 && Bracket::kColon == fInBrace->fBracket) {
2022 Definition* braceParent = fParent->fParent;
2023 braceParent->fChildren.pop_back();
2024 braceParent->fTokens.pop_back();
2025 fParent = braceParent;
2026 fInBrace = nullptr;
2027 }
2028 this->pushBracket(
2029 '(' == test ? Bracket::kParen :
2030 '[' == test ? Bracket::kSquare :
2031 '{' == test ? Bracket::kBrace :
2032 Bracket::kColon);
2033 if (!fInBrace
2034 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2035 && fInFunction) {
2036 fInBrace = fParent;
2037 }
2038 } break;
2039 case '<':
2040 if (fInCharCommentString || fInBrace) {
2041 break;
2042 }
2043 if (!this->checkForWord()) {
2044 return false;
2045 }
2046 if (fInEnum) {
2047 break;
2048 }
2049 this->pushBracket(Bracket::kAngle);
2050 break;
2051 case ')':
2052 case ']':
2053 case '}': {
2054 if (fInCharCommentString) {
2055 break;
2056 }
2057 if (!fInBrace) {
2058 if (!this->checkForWord()) {
2059 return false;
2060 }
2061 }
2062 bool popBraceParent = fInBrace == fParent;
2063 if ((')' == test ? Bracket::kParen :
2064 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2065 this->popBracket();
2066 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002067 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002068 } else {
2069 fInFunction = '}' != test;
2070 }
Cary Clark73fa9722017-08-29 17:36:51 -04002071 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2072 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002073 } else {
2074 return reportError<bool>("malformed close bracket");
2075 }
2076 if (popBraceParent) {
2077 Definition* braceParent = fInBrace->fParent;
2078 braceParent->fChildren.pop_back();
2079 braceParent->fTokens.pop_back();
2080 fInBrace = nullptr;
2081 }
2082 } break;
2083 case '>':
2084 if (fInCharCommentString || fInBrace) {
2085 break;
2086 }
2087 if (!this->checkForWord()) {
2088 return false;
2089 }
2090 if (fInEnum) {
2091 break;
2092 }
Cary Clarka560c472017-11-27 10:44:06 -05002093 if (Bracket::kPound == this->topBracket()) {
2094 break;
2095 }
Cary Clark8032b982017-07-28 11:04:54 -04002096 if (Bracket::kAngle == this->topBracket()) {
2097 this->popBracket();
2098 } else {
2099 return reportError<bool>("malformed close angle bracket");
2100 }
2101 break;
2102 case '#': {
2103 if (fInCharCommentString || fInBrace) {
2104 break;
2105 }
2106 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2107 this->pushBracket(Bracket::kPound);
2108 break;
2109 }
2110 case '&':
2111 case ',':
2112 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05002113 case '+':
2114 case '=':
2115 case '-':
2116 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002117 if (fInCharCommentString || fInBrace) {
2118 break;
2119 }
2120 if (!this->checkForWord()) {
2121 return false;
2122 }
2123 break;
2124 case ';':
2125 if (fInCharCommentString || fInBrace) {
2126 break;
2127 }
2128 if (!this->checkForWord()) {
2129 return false;
2130 }
2131 if (Definition::Type::kKeyWord == fParent->fType
2132 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002133 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2134 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002135 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2136 this->popObject();
2137 }
Cary Clark8032b982017-07-28 11:04:54 -04002138 if (KeyWord::kEnum == fParent->fKeyWord) {
2139 fInEnum = false;
2140 }
2141 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002142 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2143 this->popObject();
2144 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002145 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002146 } else if (Definition::Type::kBracket == fParent->fType
2147 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2148 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2149 list<Definition>::iterator baseIter = fParent->fTokens.end();
2150 list<Definition>::iterator namedIter = fParent->fTokens.end();
2151 for (auto tokenIter = fParent->fTokens.end();
2152 fParent->fTokens.begin() != tokenIter--; ) {
2153 if (tokenIter->fLineCount == fLineCount) {
2154 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2155 if (namedIter != fParent->fTokens.end()) {
2156 return reportError<bool>("found two named member tokens");
2157 }
2158 namedIter = tokenIter;
2159 }
2160 baseIter = tokenIter;
2161 } else {
2162 break;
2163 }
2164 }
2165 // FIXME: if a member definition spans multiple lines, this won't work
2166 if (namedIter != fParent->fTokens.end()) {
2167 if (baseIter == namedIter) {
2168 return this->reportError<bool>("expected type before named token");
2169 }
2170 Definition* member = &*namedIter;
2171 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002172 if (!member->fTerminator) {
2173 member->fTerminator = member->fContentEnd;
2174 }
Cary Clark8032b982017-07-28 11:04:54 -04002175 fParent->fChildren.push_back(member);
2176 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2177 member->fChildren.push_back(&*nameType);
2178 }
Cary Clark8032b982017-07-28 11:04:54 -04002179 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002180 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002181 } else if (fParent->fChildren.size() > 0) {
2182 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002183 Definition* priorEnum = fPriorEnum;
2184 fPriorEnum = nullptr;
2185 if (!priorEnum) {
2186 while (fParent->fChildren.begin() != lastIter) {
2187 std::advance(lastIter, -1);
2188 priorEnum = *lastIter;
2189 if (Definition::Type::kBracket != priorEnum->fType ||
2190 (Bracket::kSlashSlash != priorEnum->fBracket
2191 && Bracket::kSlashStar != priorEnum->fBracket)) {
2192 break;
2193 }
Cary Clark8032b982017-07-28 11:04:54 -04002194 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002195 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002196 }
2197 if (Definition::Type::kKeyWord == priorEnum->fType
2198 && KeyWord::kEnum == priorEnum->fKeyWord) {
2199 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002200 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002201 while (tokenWalker != fParent->fTokens.end()) {
2202 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002203 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002204 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2205 break;
2206 }
2207 }
2208 while (tokenWalker != fParent->fTokens.end()) {
2209 std::advance(tokenWalker, 1);
2210 const Definition* test = &*tokenWalker;
2211 if (Definition::Type::kBracket != test->fType ||
2212 (Bracket::kSlashSlash != test->fBracket
2213 && Bracket::kSlashStar != test->fBracket)) {
2214 break;
2215 }
2216 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002217 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002218 Definition* start = &*tokenWalker;
2219 bool foundExpected = true;
2220 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2221 const Definition* test = &*tokenWalker;
2222 if (expected != test->fKeyWord) {
2223 foundExpected = false;
2224 break;
2225 }
2226 if (tokenWalker == fParent->fTokens.end()) {
2227 break;
2228 }
2229 std::advance(tokenWalker, 1);
2230 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002231 if (!foundExpected) {
2232 foundExpected = true;
2233 tokenWalker = saveTokenWalker;
2234 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2235 const Definition* test = &*tokenWalker;
2236 if (expected != test->fKeyWord) {
2237 foundExpected = false;
2238 break;
2239 }
2240 if (tokenWalker == fParent->fTokens.end()) {
2241 break;
2242 }
2243 if (KeyWord::kNone != expected) {
2244 std::advance(tokenWalker, 1);
2245 }
2246 }
2247 if (foundExpected) {
2248 auto nameToken = priorEnum->fTokens.begin();
2249 string enumName = string(nameToken->fContentStart,
2250 nameToken->fContentEnd - nameToken->fContentStart);
2251 const Definition* test = &*tokenWalker;
2252 string constType = string(test->fContentStart,
2253 test->fContentEnd - test->fContentStart);
2254 if (enumName != constType) {
2255 foundExpected = false;
2256 } else {
2257 std::advance(tokenWalker, 1);
2258 }
2259 }
2260 }
Cary Clark8032b982017-07-28 11:04:54 -04002261 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2262 const char* nameStart = tokenWalker->fStart;
2263 std::advance(tokenWalker, 1);
2264 if (tokenWalker != fParent->fTokens.end()) {
2265 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2266 tp.skipToNonAlphaNum();
2267 start->fName = string(nameStart, tp.fChar - nameStart);
2268 start->fContentEnd = fChar;
2269 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002270 fPriorEnum = priorEnum;
2271 }
Cary Clark8032b982017-07-28 11:04:54 -04002272 }
2273 }
2274 }
2275 this->addPunctuation(Punctuation::kSemicolon);
2276 fInFunction = false;
2277 break;
2278 case '~':
2279 if (fInEnum) {
2280 break;
2281 }
2282 case '0': case '1': case '2': case '3': case '4':
2283 case '5': case '6': case '7': case '8': case '9':
2284 // TODO: don't want to parse numbers, but do need to track for enum defs
2285 // break;
2286 case 'A': case 'B': case 'C': case 'D': case 'E':
2287 case 'F': case 'G': case 'H': case 'I': case 'J':
2288 case 'K': case 'L': case 'M': case 'N': case 'O':
2289 case 'P': case 'Q': case 'R': case 'S': case 'T':
2290 case 'U': case 'V': case 'W': case 'X': case 'Y':
2291 case 'Z': case '_':
2292 case 'a': case 'b': case 'c': case 'd': case 'e':
2293 case 'f': case 'g': case 'h': case 'i': case 'j':
2294 case 'k': case 'l': case 'm': case 'n': case 'o':
2295 case 'p': case 'q': case 'r': case 's': case 't':
2296 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002297 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002298 if (fInCharCommentString || fInBrace) {
2299 break;
2300 }
2301 if (!fIncludeWord) {
2302 fIncludeWord = fChar;
2303 }
2304 break;
2305 }
2306done:
2307 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002308 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002309 return true;
2310}
2311
2312void IncludeParser::validate() const {
2313 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2314 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2315 }
2316 IncludeParser::ValidateKeyWords();
2317}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002318
2319void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2320 if (!sk_isdir(includes)) {
2321 IncludeParser::RemoveOneFile(docs, includes);
2322 } else {
2323 SkOSFile::Iter it(includes, ".h");
2324 for (SkString file; it.next(&file); ) {
2325 SkString p = SkOSPath::Join(includes, file.c_str());
2326 const char* hunk = p.c_str();
2327 if (!SkStrEndsWith(hunk, ".h")) {
2328 continue;
2329 }
2330 IncludeParser::RemoveOneFile(docs, hunk);
2331 }
2332 }
2333}
2334
2335void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2336 const char* lastForward = strrchr(includesFile, '/');
2337 const char* lastBackward = strrchr(includesFile, '\\');
2338 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2339 if (!last) {
2340 last = includesFile;
2341 } else {
2342 last += 1;
2343 }
2344 SkString baseName(last);
2345 SkASSERT(baseName.endsWith(".h"));
2346 baseName.remove(baseName.size() - 2, 2);
2347 baseName.append("_Reference.bmh");
2348 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2349 remove(fullName.c_str());
2350}