blob: 900617667ca722aaee85e465ccf30d3380532207 [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) {
1363 markupChild->fName = markupDef->fName + "::";
1364 }
1365 markupChild->fName += string(nameStart, (size_t) (enumName.fChar - nameStart));
Cary Clark8032b982017-07-28 11:04:54 -04001366 if (!this->findComments(*child, markupChild)) {
1367 return false;
1368 }
Cary Clark8032b982017-07-28 11:04:54 -04001369 const char* dataEnd;
1370 do {
Cary Clark8032b982017-07-28 11:04:54 -04001371 parser.skipWhiteSpace();
1372 if ('}' == parser.peek()) {
1373 break;
1374 }
1375 Definition* comment = nullptr;
1376 // note that comment, if any, can be before or after (on the same line, though) as member
1377 if ('#' == parser.peek()) {
1378 // fixme: handle preprecessor, but just skip it for now
1379 parser.skipToLineStart();
1380 }
1381 while (parser.startsWith("/*") || parser.startsWith("//")) {
1382 parser.next();
1383 const char* start = parser.fChar;
1384 const char* end;
1385 if ('*' == parser.peek()) {
1386 end = parser.strnstr("*/", parser.fEnd);
1387 parser.fChar = end;
1388 parser.next();
1389 parser.next();
1390 } else {
1391 end = parser.trimmedLineEnd();
1392 parser.skipToLineStart();
1393 }
1394 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001395 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001396 comment = &markupChild->fTokens.back();
1397 comment->fTerminator = end;
1398 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1399 return false;
1400 }
1401 parser.skipWhiteSpace();
1402 }
1403 parser.skipWhiteSpace();
1404 const char* memberStart = parser.fChar;
1405 if ('}' == memberStart[0]) {
1406 break;
1407 }
Cary Clark9174bda2017-09-19 17:39:32 -04001408 // if there's comment on same the line as member def, output first as if it was before
1409
Cary Clark8032b982017-07-28 11:04:54 -04001410 parser.skipToNonAlphaNum();
1411 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001412 if (parser.eof() || !parser.skipWhiteSpace()) {
1413 return this->reportError<bool>("enum member must end with comma 1");
1414 }
Cary Clark8032b982017-07-28 11:04:54 -04001415 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001416 if ('=' == parser.peek()) {
1417 parser.skipToEndBracket(',');
1418 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001419 if (!parser.eof() && '#' == parser.peek()) {
1420 // fixme: handle preprecessor, but just skip it for now
1421 continue;
1422 }
Cary Clark9174bda2017-09-19 17:39:32 -04001423 if (parser.eof() || ',' != parser.peek()) {
1424 return this->reportError<bool>("enum member must end with comma 2");
1425 }
1426 dataEnd = parser.fChar;
1427 const char* start = parser.anyOf("/\n");
1428 SkASSERT(start);
1429 parser.skipTo(start);
1430 if ('/' == parser.next()) {
1431 char slashStar = parser.next();
1432 if ('/' == slashStar || '*' == slashStar) {
1433 TextParser::Save save(&parser);
1434 char doxCheck = parser.next();
1435 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1436 save.restore();
1437 }
1438 }
1439 parser.skipWhiteSpace();
1440 const char* commentStart = parser.fChar;
1441 if ('/' == slashStar) {
1442 parser.skipToEndBracket('\n');
1443 } else {
1444 parser.skipToEndBracket("*/");
1445 }
1446 SkASSERT(!parser.eof());
1447 const char* commentEnd = parser.fChar;
1448 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001449 parser.fLineCount, markupChild, '\0');
Cary Clark9174bda2017-09-19 17:39:32 -04001450 comment = &markupChild->fTokens.back();
1451 comment->fTerminator = commentEnd;
1452 }
Cary Clark8032b982017-07-28 11:04:54 -04001453 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001454 markupChild, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001455 Definition* member = &markupChild->fTokens.back();
1456 member->fName = memberName;
1457 if (comment) {
1458 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001459 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001460 }
1461 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001462 } while (true);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001463 for (auto outsideMember : child->fChildren) {
1464 if (Definition::Type::kBracket == outsideMember->fType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001465 continue;
1466 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001467 SkASSERT(Definition::Type::kKeyWord == outsideMember->fType);
1468 if (KeyWord::kClass == outsideMember->fKeyWord) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001469 continue;
1470 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05001471 SkASSERT(KeyWord::kStatic == outsideMember->fKeyWord);
1472 markupChild->fTokens.emplace_back(MarkType::kMember, outsideMember->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001473 outsideMember->fContentEnd, outsideMember->fLineCount, markupChild, '\0');
Cary Clark884dd7d2017-10-11 10:37:52 -04001474 Definition* member = &markupChild->fTokens.back();
Cary Clark7cfcbca2018-01-04 16:11:51 -05001475 member->fName = outsideMember->fName;
Cary Clark884dd7d2017-10-11 10:37:52 -04001476 // FIXME: ? add comment as well ?
1477 markupChild->fChildren.push_back(member);
Cary Clark884dd7d2017-10-11 10:37:52 -04001478 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001479 if (markupDef) {
1480 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1481 SkASSERT(classDef.fStart);
1482 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1483 markupChild->fName = uniqueName;
1484 classDef.fEnums[uniqueName] = markupChild;
1485 }
Cary Clark8032b982017-07-28 11:04:54 -04001486 return true;
1487}
1488
1489bool IncludeParser::parseInclude(const string& name) {
1490 fParent = &fIncludeMap[name];
1491 fParent->fName = name;
1492 fParent->fFileName = fFileName;
1493 fParent->fType = Definition::Type::kFileType;
1494 fParent->fContentStart = fChar;
1495 fParent->fContentEnd = fEnd;
1496 // parse include file into tree
1497 while (fChar < fEnd) {
1498 if (!this->parseChar()) {
1499 return false;
1500 }
1501 }
1502 // parse tree and add named objects to maps
1503 fParent = &fIncludeMap[name];
1504 if (!this->parseObjects(fParent, nullptr)) {
1505 return false;
1506 }
1507 return true;
1508}
1509
1510bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1511 const char* typeStart = child->fChildren[0]->fContentStart;
1512 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
Cary Clark1a8d7622018-03-05 13:26:16 -05001513 child->fLineCount, markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001514 Definition* markupChild = &markupDef->fTokens.back();
1515 TextParser nameParser(child);
1516 nameParser.skipToNonAlphaNum();
1517 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1518 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1519 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1520 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001521 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001522 classDef.fMembers[uniqueName] = markupChild;
1523 if (child->fParentIndex >= 2) {
1524 auto comment = child->fParent->fTokens.begin();
1525 std::advance(comment, child->fParentIndex - 2);
1526 if (Definition::Type::kBracket == comment->fType
1527 && (Bracket::kSlashStar == comment->fBracket
1528 || Bracket::kSlashSlash == comment->fBracket)) {
1529 TextParser parser(&*comment);
1530 do {
1531 parser.skipToAlpha();
1532 if (parser.eof()) {
1533 break;
1534 }
1535 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001536 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001537 if (Bracket::kSlashStar == comment->fBracket) {
1538 const char* commentEnd = parser.strnstr("*/", end);
1539 if (commentEnd) {
1540 end = commentEnd;
1541 }
1542 }
1543 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001544 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001545 Definition* commentChild = &markupDef->fTokens.back();
1546 markupChild->fChildren.emplace_back(commentChild);
1547 parser.skipTo(end);
1548 } while (!parser.eof());
1549 }
1550 }
1551 return true;
1552}
1553
1554bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1555 auto tokenIter = child->fParent->fTokens.begin();
1556 std::advance(tokenIter, child->fParentIndex);
1557 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001558 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001559 bool addConst = false;
1560 auto operatorCheck = tokenIter;
1561 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1562 operatorCheck = std::prev(tokenIter);
1563 }
1564 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001565 auto closeParen = std::next(tokenIter);
1566 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1567 '(' == closeParen->fContentStart[0]);
1568 nameEnd = closeParen->fContentEnd + 1;
1569 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001570 if (Definition::Type::kKeyWord == closeParen->fType &&
1571 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001572 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001573 }
Cary Clarka560c472017-11-27 10:44:06 -05001574 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001575 }
1576 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001577 if (addConst) {
1578 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001579 }
Cary Clark8032b982017-07-28 11:04:54 -04001580 while (tokenIter != child->fParent->fTokens.begin()) {
1581 auto testIter = std::prev(tokenIter);
1582 switch (testIter->fType) {
1583 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001584 if (testIter == child->fParent->fTokens.begin() &&
1585 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1586 KeyWord::kIfndef == child->fParent->fKeyWord ||
1587 KeyWord::kIf == child->fParent->fKeyWord)) {
1588 std::next(tokenIter);
1589 break;
1590 }
Cary Clark8032b982017-07-28 11:04:54 -04001591 goto keepGoing;
1592 case Definition::Type::kKeyWord: {
1593 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1594 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1595 goto keepGoing;
1596 }
1597 } break;
1598 case Definition::Type::kBracket:
1599 if (Bracket::kAngle == testIter->fBracket) {
1600 goto keepGoing;
1601 }
1602 break;
1603 case Definition::Type::kPunctuation:
1604 if (Punctuation::kSemicolon == testIter->fPunctuation
1605 || Punctuation::kLeftBrace == testIter->fPunctuation
1606 || Punctuation::kColon == testIter->fPunctuation) {
1607 break;
1608 }
1609 keepGoing:
1610 tokenIter = testIter;
1611 continue;
1612 default:
1613 break;
1614 }
1615 break;
1616 }
1617 tokenIter->fName = nameStr;
1618 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001619 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001620 auto testIter = child->fParent->fTokens.begin();
1621 SkASSERT(child->fParentIndex > 0);
1622 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001623 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1624 0 == tokenIter->fParentIndex) {
1625 tokenIter = std::next(tokenIter);
1626 }
Cary Clark8032b982017-07-28 11:04:54 -04001627 const char* start = tokenIter->fContentStart;
1628 const char* end = tokenIter->fContentEnd;
1629 const char kDebugCodeStr[] = "SkDEBUGCODE";
1630 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1631 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1632 std::advance(testIter, 1);
1633 start = testIter->fContentStart + 1;
1634 end = testIter->fContentEnd - 1;
1635 } else {
1636 end = testIter->fContentEnd;
1637 while (testIter != child->fParent->fTokens.end()) {
1638 testIter = std::next(testIter);
1639 switch (testIter->fType) {
1640 case Definition::Type::kPunctuation:
1641 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1642 || Punctuation::kLeftBrace == testIter->fPunctuation
1643 || Punctuation::kColon == testIter->fPunctuation);
1644 end = testIter->fStart;
1645 break;
1646 case Definition::Type::kKeyWord: {
1647 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1648 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1649 continue;
1650 }
1651 } break;
1652 default:
1653 continue;
1654 }
1655 break;
1656 }
1657 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001658 while (end > start && ' ' >= end[-1]) {
1659 --end;
1660 }
Cary Clark9174bda2017-09-19 17:39:32 -04001661 if (!markupDef) {
1662 auto parentIter = child->fParent->fTokens.begin();
1663 SkASSERT(child->fParentIndex > 0);
1664 std::advance(parentIter, child->fParentIndex - 1);
1665 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001666 TextParser nameParser(methodName);
1667 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001668 return true; // expect this is inline class definition outside of class
1669 }
Cary Clarka560c472017-11-27 10:44:06 -05001670 string name(nameParser.fLine, nameParser.lineLength());
1671 auto finder = fIFunctionMap.find(name);
1672 if (fIFunctionMap.end() != finder) {
1673 // create unique name
1674 SkASSERT(0); // incomplete
1675 }
1676 auto globalFunction = &fIFunctionMap[name];
1677 globalFunction->fContentStart = start;
1678 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001679 globalFunction->fFiddle = name;
1680 globalFunction->fContentEnd = end;
1681 globalFunction->fMarkType = MarkType::kMethod;
1682 globalFunction->fLineCount = tokenIter->fLineCount;
1683 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001684 }
Cary Clark8032b982017-07-28 11:04:54 -04001685 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
Cary Clark1a8d7622018-03-05 13:26:16 -05001686 markupDef, '\0');
Cary Clark8032b982017-07-28 11:04:54 -04001687 Definition* markupChild = &markupDef->fTokens.back();
1688 // do find instead -- I wonder if there is a way to prevent this in c++
1689 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1690 SkASSERT(classDef.fStart);
1691 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1692 markupChild->fName = uniqueName;
1693 if (!this->findComments(*child, markupChild)) {
1694 return false;
1695 }
1696 classDef.fMethods[uniqueName] = markupChild;
1697 return true;
1698}
1699
Cary Clark8032b982017-07-28 11:04:54 -04001700bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1701 for (auto& child : parent->fChildren) {
1702 if (!this->parseObject(child, markupDef)) {
1703 return false;
1704 }
1705 }
1706 return true;
1707}
1708
1709bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1710 // set up for error reporting
1711 fLine = fChar = child->fStart;
1712 fEnd = child->fContentEnd;
1713 // todo: put original line number in child as well
1714 switch (child->fType) {
1715 case Definition::Type::kKeyWord:
1716 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001717 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001718 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001719 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001720 }
1721 break;
1722 case KeyWord::kEnum:
1723 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001724 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001725 }
1726 break;
1727 case KeyWord::kStruct:
1728 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001729 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001730 }
1731 break;
1732 case KeyWord::kTemplate:
Cary Clarkbbfda252018-03-09 15:32:01 -05001733 if (!this->parseTemplate(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001734 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001735 }
1736 break;
1737 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001738 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001739 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001740 }
1741 break;
1742 case KeyWord::kUnion:
1743 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001744 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001745 }
1746 break;
1747 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001748 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001749 }
1750 break;
1751 case Definition::Type::kBracket:
1752 switch (child->fBracket) {
1753 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001754 if (fLastObject) {
1755 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1756 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001757 if (!checkDeprecated.eof()) {
1758 checkDeprecated.skipWhiteSpace();
Cary Clark89b14562018-03-19 09:04:10 -04001759 if (checkDeprecated.startsWith(gAttrDeprecated)) {
1760 fAttrDeprecated = child;
Cary Clark9174bda2017-09-19 17:39:32 -04001761 break;
1762 }
1763 }
1764 }
1765 {
1766 auto tokenIter = child->fParent->fTokens.begin();
1767 std::advance(tokenIter, child->fParentIndex);
1768 tokenIter = std::prev(tokenIter);
Cary Clark61dfc3a2018-01-03 08:37:53 -05001769 TextParser previousToken(&*tokenIter);
Cary Clark89b14562018-03-19 09:04:10 -04001770 if (previousToken.startsWith(gAttrDeprecated)) {
1771 fAttrDeprecated = &*tokenIter;
Cary Clark73fa9722017-08-29 17:36:51 -04001772 break;
1773 }
Cary Clark61dfc3a2018-01-03 08:37:53 -05001774 if (Bracket::kPound == child->fParent->fBracket &&
1775 KeyWord::kIf == child->fParent->fKeyWord) {
1776 // TODO: this will skip methods named defined() -- for the
1777 // moment there aren't any
1778 if (previousToken.startsWith("defined")) {
1779 break;
1780 }
1781 }
Cary Clark73fa9722017-08-29 17:36:51 -04001782 }
Cary Clark8032b982017-07-28 11:04:54 -04001783 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001784 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001785 }
Cary Clark89b14562018-03-19 09:04:10 -04001786 if (fAttrDeprecated) {
1787 Definition* lastMethod = &markupDef->fTokens.back();
1788 lastMethod->fDeprecated = true;
1789 fAttrDeprecated = nullptr;
1790 }
Cary Clark73fa9722017-08-29 17:36:51 -04001791 break;
Cary Clark8032b982017-07-28 11:04:54 -04001792 case Bracket::kSlashSlash:
1793 case Bracket::kSlashStar:
1794 // comments are picked up by parsing objects first
1795 break;
1796 case Bracket::kPound:
1797 // special-case the #xxx xxx_DEFINED entries
1798 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001799 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001800 case KeyWord::kIfndef:
1801 case KeyWord::kIfdef:
1802 if (child->boilerplateIfDef(fParent)) {
1803 if (!this->parseObjects(child, markupDef)) {
1804 return false;
1805 }
1806 break;
1807 }
1808 goto preproError;
1809 case KeyWord::kDefine:
1810 if (child->boilerplateDef(fParent)) {
1811 break;
1812 }
1813 goto preproError;
1814 case KeyWord::kEndif:
1815 if (child->boilerplateEndIf()) {
1816 break;
1817 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001818 case KeyWord::kError:
Cary Clark8032b982017-07-28 11:04:54 -04001819 case KeyWord::kInclude:
1820 // ignored for now
1821 break;
1822 case KeyWord::kElse:
1823 case KeyWord::kElif:
1824 // todo: handle these
1825 break;
1826 default:
1827 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001828 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001829 }
1830 break;
1831 case Bracket::kAngle:
1832 // pick up templated function pieces when method is found
1833 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001834 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001835 if (!this->parseObjects(child, markupDef)) {
1836 return false;
1837 }
Cary Clark73fa9722017-08-29 17:36:51 -04001838 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001839 case Bracket::kSquare: {
1840 // check to see if parent is operator, the only case we handle so far
1841 auto prev = child->fParent->fTokens.begin();
1842 std::advance(prev, child->fParentIndex - 1);
1843 if (KeyWord::kOperator != prev->fKeyWord) {
1844 return child->reportError<bool>("expected operator overload");
1845 }
1846 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001847 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001848 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001849 }
1850 break;
1851 case Definition::Type::kWord:
1852 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001853 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001854 }
1855 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001856 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001857 }
1858 break;
1859 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001860 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001861 break;
1862 }
1863 return true;
1864}
1865
Cary Clarkbbfda252018-03-09 15:32:01 -05001866bool IncludeParser::parseTemplate(Definition* child, Definition* markupDef) {
1867 return this->parseObjects(child, markupDef);
Cary Clark8032b982017-07-28 11:04:54 -04001868}
1869
Cary Clark2f466242017-12-11 16:03:17 -05001870bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1871 TextParser typedefParser(child);
Cary Clark61ca7c52018-01-02 11:34:14 -05001872 typedefParser.skipExact("typedef");
1873 typedefParser.skipWhiteSpace();
Cary Clark2f466242017-12-11 16:03:17 -05001874 string nameStr = typedefParser.typedefName();
1875 if (!markupDef) {
1876 Definition& typedefDef = fITypedefMap[nameStr];
1877 SkASSERT(!typedefDef.fStart);
1878 typedefDef.fStart = child->fContentStart;
1879 typedefDef.fContentStart = child->fContentStart;
1880 typedefDef.fName = nameStr;
1881 typedefDef.fFiddle = nameStr;
1882 typedefDef.fContentEnd = child->fContentEnd;
1883 typedefDef.fTerminator = child->fContentEnd;
1884 typedefDef.fMarkType = MarkType::kTypedef;
1885 typedefDef.fLineCount = child->fLineCount;
1886 return true;
1887 }
1888 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
Cary Clark1a8d7622018-03-05 13:26:16 -05001889 child->fLineCount, markupDef, '\0');
Cary Clark2f466242017-12-11 16:03:17 -05001890 Definition* markupChild = &markupDef->fTokens.back();
1891 markupChild->fName = nameStr;
1892 markupChild->fTerminator = markupChild->fContentEnd;
1893 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1894 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001895 return true;
1896}
1897
1898bool IncludeParser::parseUnion() {
1899
1900 return true;
1901}
1902
1903bool IncludeParser::parseChar() {
1904 char test = *fChar;
1905 if ('\\' == fPrev) {
1906 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001907// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001908 fLine = fChar + 1;
1909 }
1910 goto done;
1911 }
1912 switch (test) {
1913 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001914// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001915 fLine = fChar + 1;
1916 if (fInChar) {
1917 return reportError<bool>("malformed char");
1918 }
1919 if (fInString) {
1920 return reportError<bool>("malformed string");
1921 }
1922 if (!this->checkForWord()) {
1923 return false;
1924 }
1925 if (Bracket::kPound == this->topBracket()) {
1926 KeyWord keyWord = fParent->fKeyWord;
1927 if (KeyWord::kNone == keyWord) {
1928 return this->reportError<bool>("unhandled preprocessor directive");
1929 }
Cary Clark2dc84ad2018-01-26 12:56:22 -05001930 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord || KeyWord::kError == keyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001931 this->popBracket();
1932 }
1933 } else if (Bracket::kSlashSlash == this->topBracket()) {
1934 this->popBracket();
1935 }
1936 break;
1937 case '*':
1938 if (!fInCharCommentString && '/' == fPrev) {
1939 this->pushBracket(Bracket::kSlashStar);
1940 }
1941 if (!this->checkForWord()) {
1942 return false;
1943 }
1944 if (!fInCharCommentString) {
1945 this->addPunctuation(Punctuation::kAsterisk);
1946 }
1947 break;
1948 case '/':
1949 if ('*' == fPrev) {
1950 if (!fInCharCommentString) {
1951 return reportError<bool>("malformed closing comment");
1952 }
1953 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001954 TextParser::Save save(this);
1955 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001956 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001957 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001958 }
1959 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001960 }
Cary Clark8032b982017-07-28 11:04:54 -04001961 if (!fInCharCommentString && '/' == fPrev) {
1962 this->pushBracket(Bracket::kSlashSlash);
1963 break;
1964 }
1965 if (!this->checkForWord()) {
1966 return false;
1967 }
1968 break;
1969 case '\'':
1970 if (Bracket::kChar == this->topBracket()) {
1971 this->popBracket();
1972 } else if (!fInComment && !fInString) {
1973 if (fIncludeWord) {
1974 return this->reportError<bool>("word then single-quote");
1975 }
1976 this->pushBracket(Bracket::kChar);
1977 }
1978 break;
1979 case '\"':
1980 if (Bracket::kString == this->topBracket()) {
1981 this->popBracket();
1982 } else if (!fInComment && !fInChar) {
1983 if (fIncludeWord) {
1984 return this->reportError<bool>("word then double-quote");
1985 }
1986 this->pushBracket(Bracket::kString);
1987 }
1988 break;
1989 case ':':
1990 case '(':
1991 case '[':
1992 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001993 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1994 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1995 this->pushBracket(Bracket::kDebugCode);
1996 break;
1997 }
Cary Clark8032b982017-07-28 11:04:54 -04001998 if (fInCharCommentString) {
1999 break;
2000 }
2001 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
2002 break;
2003 }
2004 if (!fInBrace) {
2005 if (!this->checkForWord()) {
2006 return false;
2007 }
2008 if (':' == test && !fInFunction) {
2009 break;
2010 }
2011 if ('{' == test) {
2012 this->addPunctuation(Punctuation::kLeftBrace);
2013 } else if (':' == test) {
2014 this->addPunctuation(Punctuation::kColon);
2015 }
2016 }
2017 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
2018 && Bracket::kColon == fInBrace->fBracket) {
2019 Definition* braceParent = fParent->fParent;
2020 braceParent->fChildren.pop_back();
2021 braceParent->fTokens.pop_back();
2022 fParent = braceParent;
2023 fInBrace = nullptr;
2024 }
2025 this->pushBracket(
2026 '(' == test ? Bracket::kParen :
2027 '[' == test ? Bracket::kSquare :
2028 '{' == test ? Bracket::kBrace :
2029 Bracket::kColon);
2030 if (!fInBrace
2031 && ('{' == test || (':' == test && ' ' >= fChar[1]))
2032 && fInFunction) {
2033 fInBrace = fParent;
2034 }
2035 } break;
2036 case '<':
2037 if (fInCharCommentString || fInBrace) {
2038 break;
2039 }
2040 if (!this->checkForWord()) {
2041 return false;
2042 }
2043 if (fInEnum) {
2044 break;
2045 }
2046 this->pushBracket(Bracket::kAngle);
2047 break;
2048 case ')':
2049 case ']':
2050 case '}': {
2051 if (fInCharCommentString) {
2052 break;
2053 }
2054 if (!fInBrace) {
2055 if (!this->checkForWord()) {
2056 return false;
2057 }
2058 }
2059 bool popBraceParent = fInBrace == fParent;
2060 if ((')' == test ? Bracket::kParen :
2061 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
2062 this->popBracket();
2063 if (!fInFunction) {
Cary Clark89b14562018-03-19 09:04:10 -04002064 fInFunction = ')' == test;
Cary Clark8032b982017-07-28 11:04:54 -04002065 } else {
2066 fInFunction = '}' != test;
2067 }
Cary Clark73fa9722017-08-29 17:36:51 -04002068 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
2069 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04002070 } else {
2071 return reportError<bool>("malformed close bracket");
2072 }
2073 if (popBraceParent) {
2074 Definition* braceParent = fInBrace->fParent;
2075 braceParent->fChildren.pop_back();
2076 braceParent->fTokens.pop_back();
2077 fInBrace = nullptr;
2078 }
2079 } break;
2080 case '>':
2081 if (fInCharCommentString || fInBrace) {
2082 break;
2083 }
2084 if (!this->checkForWord()) {
2085 return false;
2086 }
2087 if (fInEnum) {
2088 break;
2089 }
Cary Clarka560c472017-11-27 10:44:06 -05002090 if (Bracket::kPound == this->topBracket()) {
2091 break;
2092 }
Cary Clark8032b982017-07-28 11:04:54 -04002093 if (Bracket::kAngle == this->topBracket()) {
2094 this->popBracket();
2095 } else {
2096 return reportError<bool>("malformed close angle bracket");
2097 }
2098 break;
2099 case '#': {
2100 if (fInCharCommentString || fInBrace) {
2101 break;
2102 }
2103 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
2104 this->pushBracket(Bracket::kPound);
2105 break;
2106 }
2107 case '&':
2108 case ',':
2109 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05002110 case '+':
2111 case '=':
2112 case '-':
2113 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04002114 if (fInCharCommentString || fInBrace) {
2115 break;
2116 }
2117 if (!this->checkForWord()) {
2118 return false;
2119 }
2120 break;
2121 case ';':
2122 if (fInCharCommentString || fInBrace) {
2123 break;
2124 }
2125 if (!this->checkForWord()) {
2126 return false;
2127 }
2128 if (Definition::Type::kKeyWord == fParent->fType
2129 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbbfda252018-03-09 15:32:01 -05002130 bool parentIsClass = KeyWord::kClass == fParent->fKeyWord;
2131 if (parentIsClass && fParent->fParent &&
Cary Clarkbad5ad72017-08-03 17:14:08 -04002132 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2133 this->popObject();
2134 }
Cary Clark8032b982017-07-28 11:04:54 -04002135 if (KeyWord::kEnum == fParent->fKeyWord) {
2136 fInEnum = false;
2137 }
2138 this->popObject();
Cary Clarkbbfda252018-03-09 15:32:01 -05002139 if (parentIsClass && fParent && KeyWord::kTemplate == fParent->fKeyWord) {
2140 this->popObject();
2141 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002142 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002143 } else if (Definition::Type::kBracket == fParent->fType
2144 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2145 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2146 list<Definition>::iterator baseIter = fParent->fTokens.end();
2147 list<Definition>::iterator namedIter = fParent->fTokens.end();
2148 for (auto tokenIter = fParent->fTokens.end();
2149 fParent->fTokens.begin() != tokenIter--; ) {
2150 if (tokenIter->fLineCount == fLineCount) {
2151 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2152 if (namedIter != fParent->fTokens.end()) {
2153 return reportError<bool>("found two named member tokens");
2154 }
2155 namedIter = tokenIter;
2156 }
2157 baseIter = tokenIter;
2158 } else {
2159 break;
2160 }
2161 }
2162 // FIXME: if a member definition spans multiple lines, this won't work
2163 if (namedIter != fParent->fTokens.end()) {
2164 if (baseIter == namedIter) {
2165 return this->reportError<bool>("expected type before named token");
2166 }
2167 Definition* member = &*namedIter;
2168 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002169 if (!member->fTerminator) {
2170 member->fTerminator = member->fContentEnd;
2171 }
Cary Clark8032b982017-07-28 11:04:54 -04002172 fParent->fChildren.push_back(member);
2173 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2174 member->fChildren.push_back(&*nameType);
2175 }
Cary Clark8032b982017-07-28 11:04:54 -04002176 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002177 fPriorEnum = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04002178 } else if (fParent->fChildren.size() > 0) {
2179 auto lastIter = fParent->fChildren.end();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002180 Definition* priorEnum = fPriorEnum;
2181 fPriorEnum = nullptr;
2182 if (!priorEnum) {
2183 while (fParent->fChildren.begin() != lastIter) {
2184 std::advance(lastIter, -1);
2185 priorEnum = *lastIter;
2186 if (Definition::Type::kBracket != priorEnum->fType ||
2187 (Bracket::kSlashSlash != priorEnum->fBracket
2188 && Bracket::kSlashStar != priorEnum->fBracket)) {
2189 break;
2190 }
Cary Clark8032b982017-07-28 11:04:54 -04002191 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002192 fPriorIndex = priorEnum->fParentIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002193 }
2194 if (Definition::Type::kKeyWord == priorEnum->fType
2195 && KeyWord::kEnum == priorEnum->fKeyWord) {
2196 auto tokenWalker = fParent->fTokens.begin();
Cary Clark7cfcbca2018-01-04 16:11:51 -05002197 std::advance(tokenWalker, fPriorIndex);
Cary Clark8032b982017-07-28 11:04:54 -04002198 while (tokenWalker != fParent->fTokens.end()) {
2199 std::advance(tokenWalker, 1);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002200 ++fPriorIndex;
Cary Clark8032b982017-07-28 11:04:54 -04002201 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2202 break;
2203 }
2204 }
2205 while (tokenWalker != fParent->fTokens.end()) {
2206 std::advance(tokenWalker, 1);
2207 const Definition* test = &*tokenWalker;
2208 if (Definition::Type::kBracket != test->fType ||
2209 (Bracket::kSlashSlash != test->fBracket
2210 && Bracket::kSlashStar != test->fBracket)) {
2211 break;
2212 }
2213 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002214 auto saveTokenWalker = tokenWalker;
Cary Clark8032b982017-07-28 11:04:54 -04002215 Definition* start = &*tokenWalker;
2216 bool foundExpected = true;
2217 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2218 const Definition* test = &*tokenWalker;
2219 if (expected != test->fKeyWord) {
2220 foundExpected = false;
2221 break;
2222 }
2223 if (tokenWalker == fParent->fTokens.end()) {
2224 break;
2225 }
2226 std::advance(tokenWalker, 1);
2227 }
Cary Clark7cfcbca2018-01-04 16:11:51 -05002228 if (!foundExpected) {
2229 foundExpected = true;
2230 tokenWalker = saveTokenWalker;
2231 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConst, KeyWord::kNone}){
2232 const Definition* test = &*tokenWalker;
2233 if (expected != test->fKeyWord) {
2234 foundExpected = false;
2235 break;
2236 }
2237 if (tokenWalker == fParent->fTokens.end()) {
2238 break;
2239 }
2240 if (KeyWord::kNone != expected) {
2241 std::advance(tokenWalker, 1);
2242 }
2243 }
2244 if (foundExpected) {
2245 auto nameToken = priorEnum->fTokens.begin();
2246 string enumName = string(nameToken->fContentStart,
2247 nameToken->fContentEnd - nameToken->fContentStart);
2248 const Definition* test = &*tokenWalker;
2249 string constType = string(test->fContentStart,
2250 test->fContentEnd - test->fContentStart);
2251 if (enumName != constType) {
2252 foundExpected = false;
2253 } else {
2254 std::advance(tokenWalker, 1);
2255 }
2256 }
2257 }
Cary Clark8032b982017-07-28 11:04:54 -04002258 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2259 const char* nameStart = tokenWalker->fStart;
2260 std::advance(tokenWalker, 1);
2261 if (tokenWalker != fParent->fTokens.end()) {
2262 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2263 tp.skipToNonAlphaNum();
2264 start->fName = string(nameStart, tp.fChar - nameStart);
2265 start->fContentEnd = fChar;
2266 priorEnum->fChildren.emplace_back(start);
Cary Clark7cfcbca2018-01-04 16:11:51 -05002267 fPriorEnum = priorEnum;
2268 }
Cary Clark8032b982017-07-28 11:04:54 -04002269 }
2270 }
2271 }
2272 this->addPunctuation(Punctuation::kSemicolon);
2273 fInFunction = false;
2274 break;
2275 case '~':
2276 if (fInEnum) {
2277 break;
2278 }
2279 case '0': case '1': case '2': case '3': case '4':
2280 case '5': case '6': case '7': case '8': case '9':
2281 // TODO: don't want to parse numbers, but do need to track for enum defs
2282 // break;
2283 case 'A': case 'B': case 'C': case 'D': case 'E':
2284 case 'F': case 'G': case 'H': case 'I': case 'J':
2285 case 'K': case 'L': case 'M': case 'N': case 'O':
2286 case 'P': case 'Q': case 'R': case 'S': case 'T':
2287 case 'U': case 'V': case 'W': case 'X': case 'Y':
2288 case 'Z': case '_':
2289 case 'a': case 'b': case 'c': case 'd': case 'e':
2290 case 'f': case 'g': case 'h': case 'i': case 'j':
2291 case 'k': case 'l': case 'm': case 'n': case 'o':
2292 case 'p': case 'q': case 'r': case 's': case 't':
2293 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002294 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002295 if (fInCharCommentString || fInBrace) {
2296 break;
2297 }
2298 if (!fIncludeWord) {
2299 fIncludeWord = fChar;
2300 }
2301 break;
2302 }
2303done:
2304 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002305 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002306 return true;
2307}
2308
2309void IncludeParser::validate() const {
2310 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2311 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2312 }
2313 IncludeParser::ValidateKeyWords();
2314}
Cary Clark2dc84ad2018-01-26 12:56:22 -05002315
2316void IncludeParser::RemoveFile(const char* docs, const char* includes) {
2317 if (!sk_isdir(includes)) {
2318 IncludeParser::RemoveOneFile(docs, includes);
2319 } else {
2320 SkOSFile::Iter it(includes, ".h");
2321 for (SkString file; it.next(&file); ) {
2322 SkString p = SkOSPath::Join(includes, file.c_str());
2323 const char* hunk = p.c_str();
2324 if (!SkStrEndsWith(hunk, ".h")) {
2325 continue;
2326 }
2327 IncludeParser::RemoveOneFile(docs, hunk);
2328 }
2329 }
2330}
2331
2332void IncludeParser::RemoveOneFile(const char* docs, const char* includesFile) {
2333 const char* lastForward = strrchr(includesFile, '/');
2334 const char* lastBackward = strrchr(includesFile, '\\');
2335 const char* last = lastForward > lastBackward ? lastForward : lastBackward;
2336 if (!last) {
2337 last = includesFile;
2338 } else {
2339 last += 1;
2340 }
2341 SkString baseName(last);
2342 SkASSERT(baseName.endsWith(".h"));
2343 baseName.remove(baseName.size() - 2, 2);
2344 baseName.append("_Reference.bmh");
2345 SkString fullName = SkOSPath::Join(docs, baseName.c_str());
2346 remove(fullName.c_str());
2347}