blob: 955e6ef724e3e06a3c57560e20cf8bc4963354f6 [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"
9
Cary Clark8032b982017-07-28 11:04:54 -040010const IncludeKey kKeyWords[] = {
11 { "", KeyWord::kNone, KeyProperty::kNone },
Cary Clark73fa9722017-08-29 17:36:51 -040012 { "SK_API", KeyWord::kSK_API, KeyProperty::kModifier },
Cary Clark154beea2017-10-26 07:58:48 -040013 { "SK_BEGIN_REQUIRE_DENSE", KeyWord::kSK_BEGIN_REQUIRE_DENSE, KeyProperty::kModifier },
Cary Clark8032b982017-07-28 11:04:54 -040014 { "bool", KeyWord::kBool, KeyProperty::kNumber },
15 { "char", KeyWord::kChar, KeyProperty::kNumber },
16 { "class", KeyWord::kClass, KeyProperty::kObject },
17 { "const", KeyWord::kConst, KeyProperty::kModifier },
18 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
19 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
20 { "double", KeyWord::kDouble, KeyProperty::kNumber },
21 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
22 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
23 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
24 { "enum", KeyWord::kEnum, KeyProperty::kObject },
25 { "float", KeyWord::kFloat, KeyProperty::kNumber },
26 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
27 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
28 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
29 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
30 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
31 { "inline", KeyWord::kInline, KeyProperty::kModifier },
32 { "int", KeyWord::kInt, KeyProperty::kNumber },
33 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
34 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
35 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
36 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
37 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
38 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
39 { "static", KeyWord::kStatic, KeyProperty::kModifier },
40 { "struct", KeyWord::kStruct, KeyProperty::kObject },
41 { "template", KeyWord::kTemplate, KeyProperty::kObject },
42 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040043 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040044 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040045 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
46 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040047 { "union", KeyWord::kUnion, KeyProperty::kObject },
48 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
49 { "void", KeyWord::kVoid, KeyProperty::kNumber },
50};
51
52const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
53
54KeyWord IncludeParser::FindKey(const char* start, const char* end) {
55 int ch = 0;
56 for (size_t index = 0; index < kKeyWordCount; ) {
57 if (start[ch] > kKeyWords[index].fName[ch]) {
58 ++index;
59 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
60 return KeyWord::kNone;
61 }
62 continue;
63 }
64 if (start[ch] < kKeyWords[index].fName[ch]) {
65 return KeyWord::kNone;
66 }
67 ++ch;
68 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040069 if (end - start < (int) strlen(kKeyWords[index].fName)) {
70 return KeyWord::kNone;
71 }
Cary Clark8032b982017-07-28 11:04:54 -040072 return kKeyWords[index].fKeyWord;
73 }
74 }
75 return KeyWord::kNone;
76}
77
78void IncludeParser::ValidateKeyWords() {
79 for (size_t index = 1; index < kKeyWordCount; ++index) {
80 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
81 == (int) kKeyWords[index].fKeyWord);
82 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
83 }
84}
85
86void IncludeParser::addKeyword(KeyWord keyWord) {
87 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent);
88 fIncludeWord = nullptr;
89 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
90 Definition* def = &fParent->fTokens.back();
91 this->addDefinition(def);
92 if (KeyWord::kEnum == fParent->fKeyWord) {
93 fInEnum = true;
94 }
95 }
96}
97
Ben Wagner63fd7602017-10-09 15:45:33 -040098void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
Cary Clark8032b982017-07-28 11:04:54 -040099 const vector<string>& foundParams) {
100 for (auto& methodParam : methodParams) {
101 bool found = false;
102 for (auto& foundParam : foundParams) {
103 if (methodParam == foundParam) {
104 found = true;
105 break;
106 }
107 }
108 if (!found) {
Cary Clark154beea2017-10-26 07:58:48 -0400109 this->writeIncompleteTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400110 }
111 }
112 for (auto& foundParam : foundParams) {
113 bool found = false;
114 for (auto& methodParam : methodParams) {
115 if (methodParam == foundParam) {
116 found = true;
117 break;
118 }
119 }
120 if (!found) {
121 this->reportError("doxygen param does not match method declaration");
122 }
123 }
124}
125
126bool IncludeParser::checkForWord() {
127 if (!fIncludeWord) {
128 return true;
129 }
130 KeyWord keyWord = FindKey(fIncludeWord, fChar);
131 if (KeyWord::kNone != keyWord) {
132 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
133 this->addKeyword(keyWord);
134 return true;
135 }
136 } else {
137 this->addWord();
138 return true;
139 }
140 Definition* poundDef = fParent;
141 if (!fParent) {
142 return reportError<bool>("expected parent");
143 }
144 if (Definition::Type::kBracket != poundDef->fType) {
145 return reportError<bool>("expected bracket");
146 }
147 if (Bracket::kPound != poundDef->fBracket) {
148 return reportError<bool>("expected preprocessor");
149 }
150 if (KeyWord::kNone != poundDef->fKeyWord) {
151 return reportError<bool>("already found keyword");
152 }
153 poundDef->fKeyWord = keyWord;
154 fIncludeWord = nullptr;
155 switch (keyWord) {
156 // these do not link to other # directives
157 case KeyWord::kDefine:
158 case KeyWord::kInclude:
159 break;
160 // these start a # directive link
161 case KeyWord::kIf:
162 case KeyWord::kIfdef:
163 case KeyWord::kIfndef:
164 break;
165 // these continue a # directive link
166 case KeyWord::kElif:
167 case KeyWord::kElse: {
168 this->popObject(); // pop elif
169 if (Bracket::kPound != fParent->fBracket) {
170 return this->reportError<bool>("expected preprocessor directive");
171 }
172 this->popBracket(); // pop if
173 poundDef->fParent = fParent;
174 this->addDefinition(poundDef); // push elif back
175 } break;
176 // this ends a # directive link
177 case KeyWord::kEndif:
178 // FIXME : should this be calling popBracket() instead?
179 this->popObject(); // pop endif
180 if (Bracket::kPound != fParent->fBracket) {
181 return this->reportError<bool>("expected preprocessor directive");
182 }
183 this->popBracket(); // pop if/else
184 break;
185 default:
186 SkASSERT(0);
187 }
188 return true;
189}
190
191string IncludeParser::className() const {
192 string name(fParent->fName);
193 size_t slash = name.find_last_of("/");
194 if (string::npos == slash) {
195 slash = name.find_last_of("\\");
196 }
197 SkASSERT(string::npos != slash);
198 string result = name.substr(slash);
199 result = result.substr(1, result.size() - 3);
200 return result;
201}
202
Cary Clark884dd7d2017-10-11 10:37:52 -0400203#include <sstream>
204#include <iostream>
205
Cary Clark8032b982017-07-28 11:04:54 -0400206bool IncludeParser::crossCheck(BmhParser& bmhParser) {
Cary Clark8032b982017-07-28 11:04:54 -0400207 for (auto& classMapper : fIClassMap) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400208 string className = classMapper.first;
209 auto finder = bmhParser.fClassMap.find(className);
210 if (bmhParser.fClassMap.end() == finder) {
211 SkASSERT(string::npos != className.find("::"));
Cary Clark8032b982017-07-28 11:04:54 -0400212 continue;
213 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400214 RootDefinition* root = &finder->second;
215 root->clearVisited();
216 }
217 for (auto& classMapper : fIClassMap) {
218 string className = classMapper.first;
219 std::istringstream iss(className);
220 string classStr;
221 string classBase;
222 RootDefinition* root = nullptr;
223 while (std::getline(iss, classStr, ':')) {
224 if (root) {
225 if (!classStr.length()) {
226 continue;
227 }
228 classBase += "::" + classStr;
229 auto finder = root->fBranches.find(classBase);
230 if (root->fBranches.end() != finder) {
231 root = finder->second;
232 } else {
233 SkASSERT(0);
234 }
235 } else {
236 classBase = classStr;
237 auto finder = bmhParser.fClassMap.find(classBase);
238 if (bmhParser.fClassMap.end() != finder) {
239 root = &finder->second;
240 } else {
241 SkASSERT(0);
242 }
243 }
244 }
Cary Clark8032b982017-07-28 11:04:54 -0400245 auto& classMap = classMapper.second;
246 auto& tokens = classMap.fTokens;
247 for (const auto& token : tokens) {
248 if (token.fPrivate) {
249 continue;
250 }
251 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400252 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400253 switch (token.fMarkType) {
254 case MarkType::kMethod: {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400255 if (this->internalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400256 continue;
257 }
Cary Clark8032b982017-07-28 11:04:54 -0400258 if (!def) {
259 string paramName = className + "::";
260 paramName += string(token.fContentStart,
261 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400262 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400263 if (!def && 0 == token.fName.find("operator")) {
264 string operatorName = className + "::";
265 TextParser oper("", token.fStart, token.fContentEnd, 0);
266 const char* start = oper.strnstr("operator", token.fContentEnd);
267 SkASSERT(start);
268 oper.skipTo(start);
269 oper.skipToEndBracket('(');
270 int parens = 0;
271 do {
272 if ('(' == oper.peek()) {
273 ++parens;
274 } else if (')' == oper.peek()) {
275 --parens;
276 }
277 } while (!oper.eof() && oper.next() && parens > 0);
278 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400279 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400280 }
281 }
282 if (!def) {
283 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
284 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
285 string constructorName = className + "::";
286 constructorName += string(token.fContentStart + skip,
287 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400288 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400289 }
290 if (!def && 0 == token.fName.find("SK_")) {
291 string incName = token.fName + "()";
292 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400293 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400294 if (def) {
295 if (def->fName == incName) {
296 def->fVisited = true;
297 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400298 def = root->find(className + "::toString",
299 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400300 if (def) {
301 def->fVisited = true;
302 } else {
303 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
304 }
305 }
306 break;
307 } else {
308 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
309 }
310 }
311 }
312 if (!def) {
313 bool allLower = true;
314 for (size_t index = 0; index < token.fName.length(); ++index) {
315 if (!islower(token.fName[index])) {
316 allLower = false;
317 break;
318 }
319 }
320 if (allLower) {
321 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400322 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400323 }
324 }
325 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400326 if ("SK_ATTR_DEPRECATED" == token.fName) {
327 break;
328 }
329 if (0 == token.fName.find("SkDEBUGCODE")) {
330 break;
331 }
332 }
333 if (!def) {
334 // simple method names inside nested classes have a bug and are missing trailing parens
335 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400336 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400337 }
338 if (!def) {
Cary Clark8032b982017-07-28 11:04:54 -0400339 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
340 break;
341 }
Cary Clark73fa9722017-08-29 17:36:51 -0400342 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400343 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400344 if (MarkType::kDefinedBy == def->fMarkType) {
345 def->fParent->fVisited = true;
346 }
Cary Clark8032b982017-07-28 11:04:54 -0400347 } else {
348 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
349 }
350 } break;
351 case MarkType::kComment:
352 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400353 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400354 case MarkType::kEnum: {
355 if (!def) {
356 // work backwards from first word to deduce #Enum name
357 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
358 SkAssertResult(firstMember.skipName("enum"));
359 SkAssertResult(firstMember.skipToEndBracket('{'));
360 firstMember.next();
361 firstMember.skipWhiteSpace();
362 SkASSERT('k' == firstMember.peek());
363 const char* savePos = firstMember.fChar;
364 firstMember.skipToNonAlphaNum();
365 const char* wordEnd = firstMember.fChar;
Ben Wagner63fd7602017-10-09 15:45:33 -0400366 firstMember.fChar = savePos;
Cary Clark8032b982017-07-28 11:04:54 -0400367 const char* lastUnderscore = nullptr;
368 do {
369 if (!firstMember.skipToEndBracket('_')) {
370 break;
371 }
372 if (firstMember.fChar > wordEnd) {
373 break;
374 }
375 lastUnderscore = firstMember.fChar;
376 } while (firstMember.next());
377 if (lastUnderscore) {
378 ++lastUnderscore;
Ben Wagner63fd7602017-10-09 15:45:33 -0400379 string anonName = className + "::" + string(lastUnderscore,
Cary Clark8032b982017-07-28 11:04:54 -0400380 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400381 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400382 }
383 if (!def) {
384 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
385 break;
386 }
387 }
388 def->fVisited = true;
389 for (auto& child : def->fChildren) {
390 if (MarkType::kCode == child->fMarkType) {
391 def = child;
392 break;
393 }
394 }
395 if (MarkType::kCode != def->fMarkType) {
396 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
397 break;
398 }
399 if (def->crossCheck(token)) {
400 def->fVisited = true;
401 } else {
402 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
403 }
404 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400405 string constName = MarkType::kEnumClass == token.fMarkType ?
406 fullName : className;
407 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400408 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400409 if (!def) {
410 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400411 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400412 }
413 if (!def) {
414 if (string::npos == child->fName.find("Legacy_")) {
415 SkDebugf("const missing from bmh: %s\n", constName.c_str());
416 }
417 } else {
418 def->fVisited = true;
419 }
420 }
421 } break;
422 case MarkType::kMember:
423 if (def) {
424 def->fVisited = true;
425 } else {
426 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
427 }
428 break;
429 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400430 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400431 break;
432 }
433 }
434 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400435 for (auto& classMapper : fIClassMap) {
436 string className = classMapper.first;
437 auto finder = bmhParser.fClassMap.find(className);
438 if (bmhParser.fClassMap.end() == finder) {
439 continue;
440 }
441 RootDefinition* root = &finder->second;
Cary Clarka560c472017-11-27 10:44:06 -0500442 if (!root->dumpUnVisited(bmhParser.fSkip)) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400443 SkDebugf("some struct elements not found; struct finding in includeParser is missing\n");
444 }
445 SkDebugf("cross-checked %s\n", className.c_str());
Cary Clark8032b982017-07-28 11:04:54 -0400446 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400447 bmhParser.fWroteOut = true;
Cary Clark8032b982017-07-28 11:04:54 -0400448 return true;
449}
450
451IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
452 const string& name) {
453 string className;
454 const Definition* test = fParent;
455 while (Definition::Type::kFileType != test->fType) {
456 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
457 className = test->fName + "::";
458 break;
459 }
460 test = test->fParent;
461 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400462 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400463 unordered_map<string, IClassDefinition>& map = fIClassMap;
464 IClassDefinition& markupDef = map[className];
465 if (markupDef.fStart) {
466 typedef IClassDefinition* IClassDefPtr;
467 return INHERITED::reportError<IClassDefPtr>("class already defined");
468 }
469 markupDef.fFileName = fFileName;
470 markupDef.fStart = includeDef.fStart;
471 markupDef.fContentStart = includeDef.fStart;
472 markupDef.fName = className;
473 markupDef.fContentEnd = includeDef.fContentEnd;
474 markupDef.fTerminator = includeDef.fTerminator;
475 markupDef.fParent = fParent;
476 markupDef.fLineCount = fLineCount;
477 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
478 MarkType::kStruct : MarkType::kClass;
479 markupDef.fKeyWord = includeDef.fKeyWord;
480 markupDef.fType = Definition::Type::kMark;
481 fParent = &markupDef;
482 return &markupDef;
483}
484
485void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
486 auto& tokens = classDef.fTokens;
487 for (auto& token : tokens) {
488 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
489 continue;
490 }
491 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400492 this->writeString(
493 "# ------------------------------------------------------------------------------");
494 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400495 }
496 switch (token.fMarkType) {
497 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500498 case MarkType::kEnumClass:
Cary Clark9174bda2017-09-19 17:39:32 -0400499 this->dumpEnum(token);
Cary Clark8032b982017-07-28 11:04:54 -0400500 break;
501 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400502 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400503 break;
504 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400505 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400506 continue;
507 break;
508 default:
509 SkASSERT(0);
510 }
511 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400512 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400513 this->lf(1);
514 this->writeString("// incomplete");
515 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400516 this->writeEndTag();
517 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400518 this->writeTag("SeeAlso");
519 this->writeSpace();
520 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400521 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500522 switch (token.fMarkType) {
523 case MarkType::kEnum:
524 case MarkType::kEnumClass:
525 this->writeEndTag("Enum");
526 break;
527 case MarkType::kMethod:
528 this->writeEndTag("Method");
529 break;
530 case MarkType::kMember:
531 this->writeEndTag("Member");
532 continue;
533 break;
534 default:
535 SkASSERT(0);
536 }
Cary Clark9174bda2017-09-19 17:39:32 -0400537 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400538 }
539}
Cary Clark9174bda2017-09-19 17:39:32 -0400540void IncludeParser::dumpComment(const Definition& token) {
541 fLineCount = token.fLineCount;
542 fChar = fLine = token.fContentStart;
543 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400544 bool sawParam = false;
545 bool multiline = false;
546 bool sawReturn = false;
547 bool sawComment = false;
548 bool methodHasReturn = false;
549 vector<string> methodParams;
550 vector<string> foundParams;
551 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400552 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
553 token.fLineCount);
554 if (MarkType::kMethod == token.fMarkType) {
555 methodName.fName = string(token.fContentStart,
556 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400557 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500558 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400559 && !methodParser.strnchr('~', methodParser.fEnd);
560 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
561 const char* nextEnd = paren;
562 do {
563 string paramName;
564 methodParser.fChar = nextEnd + 1;
565 methodParser.skipSpace();
566 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
567 continue;
568 }
569 methodParams.push_back(paramName);
570 } while (')' != nextEnd[0]);
571 }
Cary Clark9174bda2017-09-19 17:39:32 -0400572 for (const auto& child : token.fTokens) {
573 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
574 break;
575 }
Cary Clark8032b982017-07-28 11:04:54 -0400576 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400577 if (child.fPrivate) {
578 break;
579 }
Cary Clark8032b982017-07-28 11:04:54 -0400580 if ('@' == child.fContentStart[0]) {
581 TextParser parser(&child);
582 do {
583 parser.next();
584 if (parser.startsWith("param ")) {
585 parser.skipWord("param");
586 const char* parmStart = parser.fChar;
587 parser.skipToSpace();
588 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
589 parser.skipWhiteSpace();
590 do {
591 size_t nextComma = parmName.find(',');
592 string piece;
593 if (string::npos == nextComma) {
594 piece = parmName;
595 parmName = "";
596 } else {
597 piece = parmName.substr(0, nextComma);
598 parmName = parmName.substr(nextComma + 1);
599 }
600 if (sawParam) {
601 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400602 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400603 }
Cary Clark9174bda2017-09-19 17:39:32 -0400604 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400605 } else {
606 if (sawComment) {
607 this->nl();
608 }
609 this->lf(2);
610 }
611 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400612 this->writeTag("Param", piece);
613 this->writeSpace(2);
614 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
615 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400616 sawParam = true;
617 sawComment = false;
618 } while (parmName.length());
619 parser.skipTo(parser.fEnd);
620 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
621 parser.skipWord("return");
622 if ('s' == parser.peek()) {
623 parser.next();
624 }
625 if (sawParam) {
626 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400627 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400628 }
Cary Clark9174bda2017-09-19 17:39:32 -0400629 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400630 }
631 this->checkForMissingParams(methodParams, foundParams);
632 sawParam = false;
633 sawComment = false;
634 multiline = false;
635 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400636 this->writeTag("Return");
637 this->writeSpace(2);
638 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
639 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400640 sawReturn = true;
641 parser.skipTo(parser.fEnd);
642 } else {
643 this->reportError("unexpected doxygen directive");
644 }
645 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400646 } else if (child.length() > 1) {
647 const char* start = child.fContentStart;
648 ptrdiff_t length = child.fContentEnd - start;
649 SkASSERT(length >= 0);
650 while (length && '/' == start[0]) {
651 start += 1;
652 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400653 }
Cary Clark9174bda2017-09-19 17:39:32 -0400654 while (length && '/' == start[length - 1]) {
655 length -= 1;
656 if (length && '*' == start[length - 1]) {
657 length -= 1;
658 }
659 }
660 if (length) {
661 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
662 if (sawParam || sawReturn) {
663 this->indentToColumn(8);
664 }
665 this->writeBlock(length, start);
666 this->writeSpace();
667 sawComment = true;
668 if (sawParam || sawReturn) {
669 multiline = true;
670 }
Cary Clark8032b982017-07-28 11:04:54 -0400671 }
672 }
673 }
674 }
675 if (sawParam || sawReturn) {
676 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400677 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400678 }
Cary Clark9174bda2017-09-19 17:39:32 -0400679 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400680 }
681 if (!sawReturn) {
682 if (!sawParam) {
683 if (sawComment) {
684 this->nl();
685 }
686 this->lf(2);
687 }
688 this->checkForMissingParams(methodParams, foundParams);
689 }
690 if (methodHasReturn != sawReturn) {
691 if (!methodHasReturn) {
692 this->reportError("unexpected doxygen return");
693 } else {
694 if (sawComment) {
695 this->nl();
696 }
697 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400698 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400699 }
700 }
701}
702
Cary Clark9174bda2017-09-19 17:39:32 -0400703void IncludeParser::dumpEnum(const Definition& token) {
704 this->writeTag("Enum", token.fName);
705 this->lf(2);
706 this->writeString("#Code");
707 this->lfAlways(1);
708 this->indentToColumn(4);
709 this->writeString("enum");
710 this->writeSpace();
711 if ("_anonymous" != token.fName.substr(0, 10)) {
712 this->writeString(token.fName);
713 this->writeSpace();
714 }
715 this->writeString("{");
716 this->lfAlways(1);
717 for (auto& child : token.fChildren) {
718 this->indentToColumn(8);
719 this->writeString(child->fName);
720 if (child->length()) {
721 this->writeSpace();
722 this->writeBlock(child->length(), child->fContentStart);
723 }
724 if (',' != fLastChar) {
725 this->writeString(",");
726 }
727 this->lfAlways(1);
728 }
729 this->indentToColumn(4);
730 this->writeString("};");
731 this->lf(1);
732 this->writeString("##");
733 this->lf(2);
734 this->dumpComment(token);
735 for (auto& child : token.fChildren) {
736 // start here;
737 // get comments before
738 // or after const values
739 this->writeString("#Const");
740 this->writeSpace();
741 this->writeString(child->fName);
742 TextParser val(child);
743 if (!val.eof()) {
744 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
745 val.next();
746 val.skipSpace();
747 const char* valEnd = val.anyOf(",\n");
748 if (!valEnd) {
749 valEnd = val.fEnd;
750 }
751 this->writeSpace();
752 this->writeBlock(valEnd - val.fStart, val.fStart);
753 } else {
754 this->writeSpace();
755 this->writeDefinition(*child);
756 }
757 }
758 this->lf(1);
759 for (auto comment : child->fChildren) {
760 if (MarkType::kComment == comment->fMarkType) {
761 TextParser parser(comment);
762 parser.skipExact("*");
763 parser.skipExact("*");
764 while (!parser.eof() && parser.skipWhiteSpace()) {
765 parser.skipExact("*");
766 parser.skipWhiteSpace();
767 const char* start = parser.fChar;
768 parser.skipToEndBracket('\n');
769 this->lf(1);
770 this->writeBlock(parser.fChar - start, start);
771 }
772 }
773 }
774 this->writeEndTag();
775 }
776 this->lf(2);
777}
778
779void IncludeParser::dumpMethod(const Definition& token) {
780 this->writeString("#Method");
781 this->writeSpace();
782 if ("SK_TO_STRING_NONVIRT" == token.fName) {
783 this->writeString("void toString(SkString* str) const;");
784 this->lf(2);
785 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
786 this->lf(2);
787 this->writeTag("Private");
788 this->lf(1);
789 this->writeString("macro expands to: void toString(SkString* str) const;");
790 this->writeEndTag();
791 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400792 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400793 "Creates string representation. The representation is read by\n"
794 "internal debugging tools. The interface and implementation may be\n"
795 "suppressed by defining SK_IGNORE_TO_STRING.";
796 this->writeBlock(sizeof(desc) - 1, desc);
797 this->lf(2);
798 this->writeTag("Param", "str");
799 this->writeSpace(2);
800 this->writeString("storage for string representation");
801 this->writeSpace();
802 this->writeString("##");
803 this->lf(2);
804 return;
805 }
806 this->writeBlock(token.length(), token.fStart);
807 this->lf(1);
808 this->dumpComment(token);
809}
810
811void IncludeParser::dumpMember(const Definition& token) {
812 this->writeTag("Member");
813 this->writeSpace();
814 this->writeDefinition(token, token.fName, 2);
815 lf(1);
816 for (auto child : token.fChildren) {
817 this->writeDefinition(*child);
818 }
819 this->writeEndTag();
820 lf(2);
821}
822
Cary Clarkd0530ba2017-09-14 11:25:39 -0400823bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400824 for (const auto& member : fIClassMap) {
825 if (string::npos != member.first.find("::")) {
826 continue;
827 }
828 if (!this->dumpTokens(dir, member.first)) {
829 return false;
830 }
831 }
832 return true;
833}
834
Ben Wagner63fd7602017-10-09 15:45:33 -0400835 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400836bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400837 string fileName = dir;
838 if (dir.length() && '/' != dir[dir.length() - 1]) {
839 fileName += '/';
840 }
841 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400842 fOut = fopen(fileName.c_str(), "wb");
843 if (!fOut) {
844 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400845 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400846 }
847 string prefixName = skClassName.substr(0, 2);
848 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
849 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400850 this->writeTagNoLF("Topic", topicName);
851 this->writeTag("Alias", topicName + "_Reference");
852 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400853 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500854 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
855 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400856 this->writeTag(containerType, skClassName);
857 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400858 auto& tokens = classMap.fTokens;
859 for (auto& token : tokens) {
860 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
861 continue;
862 }
Cary Clark9174bda2017-09-19 17:39:32 -0400863 this->writeDefinition(token);
864 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400865 }
866 this->lf(2);
867 string className(skClassName.substr(2));
868 vector<string> sortedClasses;
869 size_t maxLen = 0;
870 for (const auto& oneClass : fIClassMap) {
871 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
872 continue;
873 }
874 string structName = oneClass.first.substr(skClassName.length() + 2);
875 maxLen = SkTMax(maxLen, structName.length());
876 sortedClasses.emplace_back(structName);
877 }
Cary Clark9174bda2017-09-19 17:39:32 -0400878 this->writeTag("Topic", "Overview");
879 this->lf(2);
880 this->writeTag("Subtopic", "Subtopics");
881 this->writeEndTag("ToDo", "manually add subtopics");
882 this->writeTableHeader("topics", 0, "description");
883 this->writeTableTrailer();
884 this->writeEndTag();
885 this->lf(2);
886 if (maxLen) {
887 this->writeTag("Subtopic", "Structs");
888 this->writeTableHeader("description", maxLen, "struct");
889 for (auto& name : sortedClasses) {
890 this->writeTableRow(maxLen, name);
891 }
892 this->writeTableTrailer();
893 this->writeEndTag("Subtopic");
894 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400895 }
Cary Clark8032b982017-07-28 11:04:54 -0400896 maxLen = 0;
Cary Clark9174bda2017-09-19 17:39:32 -0400897 size_t constructorMax = 0;
898 size_t operatorMax = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400899 vector<string> sortedNames;
Cary Clark9174bda2017-09-19 17:39:32 -0400900 vector<string> constructorNames;
901 vector<string> operatorNames;
Cary Clark8032b982017-07-28 11:04:54 -0400902 for (const auto& token : classMap.fTokens) {
903 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
904 continue;
905 }
Cary Clark9174bda2017-09-19 17:39:32 -0400906 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400907 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
908 continue;
909 }
Cary Clark9174bda2017-09-19 17:39:32 -0400910 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
911 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
912 constructorMax = SkTMax(constructorMax, name.length());
913 constructorNames.emplace_back(name);
914 continue;
915 }
916 if (name.substr(0, 8) == "operator") {
917 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
918 operatorMax = SkTMax(operatorMax, name.length());
919 operatorNames.emplace_back(name);
920 continue;
921 }
Cary Clark8032b982017-07-28 11:04:54 -0400922 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
923 continue;
924 }
Cary Clark9174bda2017-09-19 17:39:32 -0400925 if ("SK_TO_STRING_NONVIRT" == name) {
926 name = "toString";
927 }
Cary Clark8032b982017-07-28 11:04:54 -0400928 size_t paren = name.find('(');
929 size_t funcLen = string::npos == paren ? name.length() : paren;
930 maxLen = SkTMax(maxLen, funcLen);
931 sortedNames.emplace_back(name);
932 }
Cary Clark9174bda2017-09-19 17:39:32 -0400933 if (constructorMax) {
934 std::sort(constructorNames.begin(), constructorNames.end());
935 this->writeTag("Subtopic", "Constructors");
936 this->writeTableHeader("description", constructorMax, "function");
937 for (auto& name : constructorNames) {
938 this->writeTableRow(constructorMax, name);
939 }
940 this->writeTableTrailer();
941 this->writeEndTag("Subtopic");
942 this->lf(2);
943 }
944 if (operatorMax) {
945 std::sort(operatorNames.begin(), operatorNames.end());
946 this->writeTag("Subtopic", "Operators");
947 this->writeTableHeader("description", operatorMax, "function");
948 for (auto& name : operatorNames) {
949 this->writeTableRow(operatorMax, name);
950 }
951 this->writeTableTrailer();
952 this->writeEndTag("Subtopic");
953 this->lf(2);
954 }
Cary Clark8032b982017-07-28 11:04:54 -0400955 std::sort(sortedNames.begin(), sortedNames.end());
Cary Clark9174bda2017-09-19 17:39:32 -0400956 this->writeTag("Subtopic", "Member_Functions");
957 this->writeTableHeader("description", maxLen, "function");
Cary Clark8032b982017-07-28 11:04:54 -0400958 for (auto& name : sortedNames) {
959 size_t paren = name.find('(');
960 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark9174bda2017-09-19 17:39:32 -0400961 this->writeTableRow(maxLen, name.substr(0, funcLen));
Cary Clark8032b982017-07-28 11:04:54 -0400962 }
Cary Clark9174bda2017-09-19 17:39:32 -0400963 this->writeTableTrailer();
964 this->writeEndTag("Subtopic");
965 this->lf(2);
966 this->writeEndTag("Topic");
967 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400968
969 for (auto& oneClass : fIClassMap) {
970 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
971 continue;
972 }
973 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -0400974 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -0400975 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -0400976 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500977 KeyWord keyword = oneClass.second.fKeyWord;
978 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
979 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400980 this->writeTag(containerType, innerName);
981 this->lf(2);
982 this->writeTag("Code");
983 this->writeEndTag("ToDo", "fill this in manually");
984 this->writeEndTag();
985 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400986 for (auto& token : oneClass.second.fTokens) {
987 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
988 continue;
989 }
Cary Clark9174bda2017-09-19 17:39:32 -0400990 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -0400991 }
992 this->lf(2);
993 this->dumpClassTokens(oneClass.second);
994 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400995 this->writeEndTag(containerType, innerName);
996 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400997 }
998 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -0400999 this->writeEndTag(containerType, skClassName);
1000 this->lf(2);
1001 this->writeEndTag("Topic", topicName);
1002 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001003 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001004 SkDebugf("wrote %s\n", fileName.c_str());
1005 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001006}
1007
1008bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1009 // add comment preceding class, if any
1010 const Definition* parent = includeDef.fParent;
1011 int index = includeDef.fParentIndex;
1012 auto wordIter = parent->fTokens.begin();
1013 std::advance(wordIter, index);
1014 SkASSERT(&*wordIter == &includeDef);
1015 while (parent->fTokens.begin() != wordIter) {
1016 auto testIter = std::prev(wordIter);
1017 if (Definition::Type::kWord != testIter->fType
1018 && Definition::Type::kKeyWord != testIter->fType
1019 && (Definition::Type::kBracket != testIter->fType
1020 || Bracket::kAngle != testIter->fBracket)
1021 && (Definition::Type::kPunctuation != testIter->fType
1022 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1023 break;
1024 }
1025 wordIter = testIter;
1026 }
1027 auto commentIter = wordIter;
1028 while (parent->fTokens.begin() != commentIter) {
1029 auto testIter = std::prev(commentIter);
1030 bool isComment = Definition::Type::kBracket == testIter->fType
1031 && (Bracket::kSlashSlash == testIter->fBracket
1032 || Bracket::kSlashStar == testIter->fBracket);
1033 if (!isComment) {
1034 break;
1035 }
1036 commentIter = testIter;
1037 }
1038 while (commentIter != wordIter) {
1039 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1040 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1041 return false;
1042 }
1043 commentIter = std::next(commentIter);
1044 }
1045 return true;
1046}
1047
Cary Clarkbad5ad72017-08-03 17:14:08 -04001048bool IncludeParser::internalName(const Definition& token) const {
1049 return 0 == token.fName.find("internal_")
1050 || 0 == token.fName.find("Internal_")
1051 || 0 == token.fName.find("legacy_")
1052 || 0 == token.fName.find("temporary_")
1053 || 0 == token.fName.find("private_");
1054}
1055
Cary Clark8032b982017-07-28 11:04:54 -04001056// caller calls reportError, so just return false here
1057bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1058 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001059 // parse class header
1060 auto iter = includeDef->fTokens.begin();
1061 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1062 // todo : documentation is ignoring this for now
1063 iter = std::next(iter);
1064 }
1065 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1066 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001067 iter = std::next(iter);
1068 if (iter == includeDef->fTokens.end()) {
1069 return true; // forward declaration only
1070 }
Cary Clark8032b982017-07-28 11:04:54 -04001071 do {
1072 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001073 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001074 }
1075 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1076 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001077 }
Cary Clark8032b982017-07-28 11:04:54 -04001078 } while (static_cast<void>(iter = std::next(iter)), true);
1079 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001080 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001081 }
1082 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1083 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001084 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001085 }
1086 markupDef->fStart = iter->fStart;
1087 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001088 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001089 }
1090// if (1 != includeDef->fChildren.size()) {
1091// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1092// }
1093 includeDef = includeDef->fChildren.front();
1094 iter = includeDef->fTokens.begin();
1095 // skip until public
1096 int publicIndex = 0;
1097 if (IsStruct::kNo == isStruct) {
1098 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1099 size_t publicLen = strlen(publicName);
1100 while (iter != includeDef->fTokens.end()
1101 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1102 || strncmp(iter->fStart, publicName, publicLen))) {
1103 iter = std::next(iter);
1104 ++publicIndex;
1105 }
1106 }
1107 auto childIter = includeDef->fChildren.begin();
1108 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1109 (*childIter)->fPrivate = true;
1110 childIter = std::next(childIter);
1111 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001112 int keyIndex = publicIndex;
1113 KeyWord currentKey = KeyWord::kPublic;
1114 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1115 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001116 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1117 size_t protectedLen = strlen(protectedName);
1118 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1119 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001120 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001121 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001122 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1123 const char* testStart = iter->fStart;
1124 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1125 iter = std::next(iter);
1126 ++keyIndex;
1127 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1128 currentKey = KeyWord::kPublic;
1129 break;
1130 }
1131 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1132 currentKey = KeyWord::kProtected;
1133 break;
1134 }
1135 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1136 currentKey = KeyWord::kPrivate;
1137 break;
1138 }
1139 }
1140 fLastObject = nullptr;
1141 if (KeyWord::kPublic == currentKey) {
1142 if (!this->parseObject(child, markupDef)) {
1143 return false;
1144 }
1145 } else {
1146 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001147 }
Cary Clark73fa9722017-08-29 17:36:51 -04001148 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001149 childIter = std::next(childIter);
1150 }
Cary Clark8032b982017-07-28 11:04:54 -04001151 SkASSERT(fParent->fParent);
1152 fParent = fParent->fParent;
1153 return true;
1154}
1155
1156bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1157 int lineCount, Definition* markupDef) {
1158 TextParser parser(filename, start, end, lineCount);
1159 // parse doxygen if present
1160 if (parser.startsWith("**")) {
1161 parser.next();
1162 parser.next();
1163 parser.skipWhiteSpace();
1164 if ('\\' == parser.peek()) {
1165 parser.next();
1166 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1167 return reportError<bool>("missing object type");
1168 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001169 if (!parser.skipWord(markupDef->fName.c_str()) &&
1170 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001171 return reportError<bool>("missing object name");
1172 }
1173
1174 }
1175 }
1176 // remove leading '*' if present
1177 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1178 while (!parser.eof() && parser.skipWhiteSpace()) {
1179 while ('*' == parser.peek()) {
1180 parser.next();
1181 if (parser.eof()) {
1182 break;
1183 }
1184 parser.skipWhiteSpace();
1185 }
1186 if (parser.eof()) {
1187 break;
1188 }
1189 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001190 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark8032b982017-07-28 11:04:54 -04001191 parser.fLineCount, parent);
1192 parser.skipToEndBracket('\n');
1193 }
1194 return true;
1195}
1196
1197bool IncludeParser::parseDefine() {
1198
1199 return true;
1200}
1201
1202bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
1203 string nameStr;
1204 if (child->fTokens.size() > 0) {
1205 auto token = child->fTokens.begin();
1206 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1207 token = token->fTokens.begin();
1208 }
1209 if (Definition::Type::kWord == token->fType) {
1210 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1211 }
1212 }
1213 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1214 child->fLineCount, markupDef);
1215 Definition* markupChild = &markupDef->fTokens.back();
1216 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1217 markupChild->fKeyWord = KeyWord::kEnum;
1218 TextParser enumName(child);
1219 enumName.skipExact("enum ");
Cary Clarkbad5ad72017-08-03 17:14:08 -04001220 if (enumName.skipExact("class ")) {
1221 markupChild->fMarkType = MarkType::kEnumClass;
1222 }
Cary Clark8032b982017-07-28 11:04:54 -04001223 const char* nameStart = enumName.fChar;
1224 enumName.skipToSpace();
Ben Wagner63fd7602017-10-09 15:45:33 -04001225 markupChild->fName = markupDef->fName + "::" +
Cary Clark8032b982017-07-28 11:04:54 -04001226 string(nameStart, (size_t) (enumName.fChar - nameStart));
1227 if (!this->findComments(*child, markupChild)) {
1228 return false;
1229 }
1230 TextParser parser(child);
1231 parser.skipToEndBracket('{');
Cary Clark9174bda2017-09-19 17:39:32 -04001232 parser.next();
Cary Clark8032b982017-07-28 11:04:54 -04001233 const char* dataEnd;
1234 do {
Cary Clark8032b982017-07-28 11:04:54 -04001235 parser.skipWhiteSpace();
1236 if ('}' == parser.peek()) {
1237 break;
1238 }
1239 Definition* comment = nullptr;
1240 // note that comment, if any, can be before or after (on the same line, though) as member
1241 if ('#' == parser.peek()) {
1242 // fixme: handle preprecessor, but just skip it for now
1243 parser.skipToLineStart();
1244 }
1245 while (parser.startsWith("/*") || parser.startsWith("//")) {
1246 parser.next();
1247 const char* start = parser.fChar;
1248 const char* end;
1249 if ('*' == parser.peek()) {
1250 end = parser.strnstr("*/", parser.fEnd);
1251 parser.fChar = end;
1252 parser.next();
1253 parser.next();
1254 } else {
1255 end = parser.trimmedLineEnd();
1256 parser.skipToLineStart();
1257 }
1258 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1259 markupChild);
1260 comment = &markupChild->fTokens.back();
1261 comment->fTerminator = end;
1262 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1263 return false;
1264 }
1265 parser.skipWhiteSpace();
1266 }
1267 parser.skipWhiteSpace();
1268 const char* memberStart = parser.fChar;
1269 if ('}' == memberStart[0]) {
1270 break;
1271 }
Cary Clark9174bda2017-09-19 17:39:32 -04001272 // if there's comment on same the line as member def, output first as if it was before
1273
Cary Clark8032b982017-07-28 11:04:54 -04001274 parser.skipToNonAlphaNum();
1275 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001276 if (parser.eof() || !parser.skipWhiteSpace()) {
1277 return this->reportError<bool>("enum member must end with comma 1");
1278 }
Cary Clark8032b982017-07-28 11:04:54 -04001279 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001280 if ('=' == parser.peek()) {
1281 parser.skipToEndBracket(',');
1282 }
1283 if (parser.eof() || ',' != parser.peek()) {
1284 return this->reportError<bool>("enum member must end with comma 2");
1285 }
1286 dataEnd = parser.fChar;
1287 const char* start = parser.anyOf("/\n");
1288 SkASSERT(start);
1289 parser.skipTo(start);
1290 if ('/' == parser.next()) {
1291 char slashStar = parser.next();
1292 if ('/' == slashStar || '*' == slashStar) {
1293 TextParser::Save save(&parser);
1294 char doxCheck = parser.next();
1295 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1296 save.restore();
1297 }
1298 }
1299 parser.skipWhiteSpace();
1300 const char* commentStart = parser.fChar;
1301 if ('/' == slashStar) {
1302 parser.skipToEndBracket('\n');
1303 } else {
1304 parser.skipToEndBracket("*/");
1305 }
1306 SkASSERT(!parser.eof());
1307 const char* commentEnd = parser.fChar;
1308 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
1309 parser.fLineCount, markupChild);
1310 comment = &markupChild->fTokens.back();
1311 comment->fTerminator = commentEnd;
1312 }
Cary Clark8032b982017-07-28 11:04:54 -04001313 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1314 markupChild);
1315 Definition* member = &markupChild->fTokens.back();
1316 member->fName = memberName;
1317 if (comment) {
1318 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001319 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001320 }
1321 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001322 } while (true);
Cary Clark884dd7d2017-10-11 10:37:52 -04001323 for (auto count : child->fChildren) {
1324 if (Definition::Type::kBracket == count->fType) {
1325 continue;
1326 }
1327 SkASSERT(Definition::Type::kKeyWord == count->fType);
1328 if (KeyWord::kClass == count->fKeyWord) {
1329 continue;
1330 }
1331 SkASSERT(KeyWord::kStatic == count->fKeyWord);
1332 markupChild->fTokens.emplace_back(MarkType::kMember, count->fContentStart,
1333 count->fContentEnd, count->fLineCount, markupChild);
1334 Definition* member = &markupChild->fTokens.back();
1335 member->fName = count->fName;
1336 // FIXME: ? add comment as well ?
1337 markupChild->fChildren.push_back(member);
1338 break;
1339 }
Cary Clark8032b982017-07-28 11:04:54 -04001340 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1341 SkASSERT(classDef.fStart);
1342 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1343 markupChild->fName = uniqueName;
1344 classDef.fEnums[uniqueName] = markupChild;
1345 return true;
1346}
1347
1348bool IncludeParser::parseInclude(const string& name) {
1349 fParent = &fIncludeMap[name];
1350 fParent->fName = name;
1351 fParent->fFileName = fFileName;
1352 fParent->fType = Definition::Type::kFileType;
1353 fParent->fContentStart = fChar;
1354 fParent->fContentEnd = fEnd;
1355 // parse include file into tree
1356 while (fChar < fEnd) {
1357 if (!this->parseChar()) {
1358 return false;
1359 }
1360 }
1361 // parse tree and add named objects to maps
1362 fParent = &fIncludeMap[name];
1363 if (!this->parseObjects(fParent, nullptr)) {
1364 return false;
1365 }
1366 return true;
1367}
1368
1369bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1370 const char* typeStart = child->fChildren[0]->fContentStart;
1371 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1372 child->fLineCount, markupDef);
1373 Definition* markupChild = &markupDef->fTokens.back();
1374 TextParser nameParser(child);
1375 nameParser.skipToNonAlphaNum();
1376 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1377 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1378 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1379 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001380 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001381 classDef.fMembers[uniqueName] = markupChild;
1382 if (child->fParentIndex >= 2) {
1383 auto comment = child->fParent->fTokens.begin();
1384 std::advance(comment, child->fParentIndex - 2);
1385 if (Definition::Type::kBracket == comment->fType
1386 && (Bracket::kSlashStar == comment->fBracket
1387 || Bracket::kSlashSlash == comment->fBracket)) {
1388 TextParser parser(&*comment);
1389 do {
1390 parser.skipToAlpha();
1391 if (parser.eof()) {
1392 break;
1393 }
1394 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001395 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001396 if (Bracket::kSlashStar == comment->fBracket) {
1397 const char* commentEnd = parser.strnstr("*/", end);
1398 if (commentEnd) {
1399 end = commentEnd;
1400 }
1401 }
1402 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1403 markupDef);
1404 Definition* commentChild = &markupDef->fTokens.back();
1405 markupChild->fChildren.emplace_back(commentChild);
1406 parser.skipTo(end);
1407 } while (!parser.eof());
1408 }
1409 }
1410 return true;
1411}
1412
1413bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1414 auto tokenIter = child->fParent->fTokens.begin();
1415 std::advance(tokenIter, child->fParentIndex);
1416 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001417 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001418 bool addConst = false;
1419 auto operatorCheck = tokenIter;
1420 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1421 operatorCheck = std::prev(tokenIter);
1422 }
1423 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001424 auto closeParen = std::next(tokenIter);
1425 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1426 '(' == closeParen->fContentStart[0]);
1427 nameEnd = closeParen->fContentEnd + 1;
1428 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001429 if (Definition::Type::kKeyWord == closeParen->fType &&
1430 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001431 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001432 }
Cary Clarka560c472017-11-27 10:44:06 -05001433 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001434 }
1435 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001436 if (addConst) {
1437 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001438 }
Cary Clark8032b982017-07-28 11:04:54 -04001439 while (tokenIter != child->fParent->fTokens.begin()) {
1440 auto testIter = std::prev(tokenIter);
1441 switch (testIter->fType) {
1442 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001443 if (testIter == child->fParent->fTokens.begin() &&
1444 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1445 KeyWord::kIfndef == child->fParent->fKeyWord ||
1446 KeyWord::kIf == child->fParent->fKeyWord)) {
1447 std::next(tokenIter);
1448 break;
1449 }
Cary Clark8032b982017-07-28 11:04:54 -04001450 goto keepGoing;
1451 case Definition::Type::kKeyWord: {
1452 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1453 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1454 goto keepGoing;
1455 }
1456 } break;
1457 case Definition::Type::kBracket:
1458 if (Bracket::kAngle == testIter->fBracket) {
1459 goto keepGoing;
1460 }
1461 break;
1462 case Definition::Type::kPunctuation:
1463 if (Punctuation::kSemicolon == testIter->fPunctuation
1464 || Punctuation::kLeftBrace == testIter->fPunctuation
1465 || Punctuation::kColon == testIter->fPunctuation) {
1466 break;
1467 }
1468 keepGoing:
1469 tokenIter = testIter;
1470 continue;
1471 default:
1472 break;
1473 }
1474 break;
1475 }
1476 tokenIter->fName = nameStr;
Cary Clarka560c472017-11-27 10:44:06 -05001477 if ("operator" == nameStr) {
1478 SkDebugf("");
1479 }
Cary Clark8032b982017-07-28 11:04:54 -04001480 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001481 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001482 auto testIter = child->fParent->fTokens.begin();
1483 SkASSERT(child->fParentIndex > 0);
1484 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001485 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1486 0 == tokenIter->fParentIndex) {
1487 tokenIter = std::next(tokenIter);
1488 }
Cary Clark8032b982017-07-28 11:04:54 -04001489 const char* start = tokenIter->fContentStart;
1490 const char* end = tokenIter->fContentEnd;
1491 const char kDebugCodeStr[] = "SkDEBUGCODE";
1492 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1493 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1494 std::advance(testIter, 1);
1495 start = testIter->fContentStart + 1;
1496 end = testIter->fContentEnd - 1;
1497 } else {
1498 end = testIter->fContentEnd;
1499 while (testIter != child->fParent->fTokens.end()) {
1500 testIter = std::next(testIter);
1501 switch (testIter->fType) {
1502 case Definition::Type::kPunctuation:
1503 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1504 || Punctuation::kLeftBrace == testIter->fPunctuation
1505 || Punctuation::kColon == testIter->fPunctuation);
1506 end = testIter->fStart;
1507 break;
1508 case Definition::Type::kKeyWord: {
1509 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1510 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1511 continue;
1512 }
1513 } break;
1514 default:
1515 continue;
1516 }
1517 break;
1518 }
1519 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001520 while (end > start && ' ' >= end[-1]) {
1521 --end;
1522 }
Cary Clark9174bda2017-09-19 17:39:32 -04001523 if (!markupDef) {
1524 auto parentIter = child->fParent->fTokens.begin();
1525 SkASSERT(child->fParentIndex > 0);
1526 std::advance(parentIter, child->fParentIndex - 1);
1527 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001528 TextParser nameParser(methodName);
1529 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001530 return true; // expect this is inline class definition outside of class
1531 }
Cary Clarka560c472017-11-27 10:44:06 -05001532 string name(nameParser.fLine, nameParser.lineLength());
1533 auto finder = fIFunctionMap.find(name);
1534 if (fIFunctionMap.end() != finder) {
1535 // create unique name
1536 SkASSERT(0); // incomplete
1537 }
1538 auto globalFunction = &fIFunctionMap[name];
1539 globalFunction->fContentStart = start;
1540 globalFunction->fName = name;
1541 if ("operator+" == name) {
1542 SkDebugf("");
1543 }
1544 globalFunction->fFiddle = name;
1545 globalFunction->fContentEnd = end;
1546 globalFunction->fMarkType = MarkType::kMethod;
1547 globalFunction->fLineCount = tokenIter->fLineCount;
1548 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001549 }
Cary Clark8032b982017-07-28 11:04:54 -04001550 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1551 markupDef);
1552 Definition* markupChild = &markupDef->fTokens.back();
1553 // do find instead -- I wonder if there is a way to prevent this in c++
1554 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1555 SkASSERT(classDef.fStart);
1556 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1557 markupChild->fName = uniqueName;
Cary Clarka560c472017-11-27 10:44:06 -05001558 if ("operator+" == uniqueName) {
1559 SkDebugf("");
1560 }
Cary Clark8032b982017-07-28 11:04:54 -04001561 if (!this->findComments(*child, markupChild)) {
1562 return false;
1563 }
1564 classDef.fMethods[uniqueName] = markupChild;
1565 return true;
1566}
1567
Cary Clark8032b982017-07-28 11:04:54 -04001568bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1569 for (auto& child : parent->fChildren) {
1570 if (!this->parseObject(child, markupDef)) {
1571 return false;
1572 }
1573 }
1574 return true;
1575}
1576
1577bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1578 // set up for error reporting
1579 fLine = fChar = child->fStart;
1580 fEnd = child->fContentEnd;
1581 // todo: put original line number in child as well
1582 switch (child->fType) {
1583 case Definition::Type::kKeyWord:
1584 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001585 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001586 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001587 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001588 }
1589 break;
1590 case KeyWord::kEnum:
1591 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001592 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001593 }
1594 break;
1595 case KeyWord::kStruct:
1596 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001597 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001598 }
1599 break;
1600 case KeyWord::kTemplate:
1601 if (!this->parseTemplate()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001602 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001603 }
1604 break;
1605 case KeyWord::kTypedef:
1606 if (!this->parseTypedef()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001607 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001608 }
1609 break;
1610 case KeyWord::kUnion:
1611 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001612 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001613 }
1614 break;
1615 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001616 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001617 }
1618 break;
1619 case Definition::Type::kBracket:
1620 switch (child->fBracket) {
1621 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001622 if (fLastObject) {
1623 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1624 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001625 if (!checkDeprecated.eof()) {
1626 checkDeprecated.skipWhiteSpace();
1627 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1628 break;
1629 }
1630 }
1631 }
1632 {
1633 auto tokenIter = child->fParent->fTokens.begin();
1634 std::advance(tokenIter, child->fParentIndex);
1635 tokenIter = std::prev(tokenIter);
1636 TextParser checkDeprecated(&*tokenIter);
Cary Clark73fa9722017-08-29 17:36:51 -04001637 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1638 break;
1639 }
1640 }
Cary Clark8032b982017-07-28 11:04:54 -04001641 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001642 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001643 }
Cary Clark73fa9722017-08-29 17:36:51 -04001644 break;
Cary Clark8032b982017-07-28 11:04:54 -04001645 case Bracket::kSlashSlash:
1646 case Bracket::kSlashStar:
1647 // comments are picked up by parsing objects first
1648 break;
1649 case Bracket::kPound:
1650 // special-case the #xxx xxx_DEFINED entries
1651 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001652 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001653 case KeyWord::kIfndef:
1654 case KeyWord::kIfdef:
1655 if (child->boilerplateIfDef(fParent)) {
1656 if (!this->parseObjects(child, markupDef)) {
1657 return false;
1658 }
1659 break;
1660 }
1661 goto preproError;
1662 case KeyWord::kDefine:
1663 if (child->boilerplateDef(fParent)) {
1664 break;
1665 }
1666 goto preproError;
1667 case KeyWord::kEndif:
1668 if (child->boilerplateEndIf()) {
1669 break;
1670 }
1671 case KeyWord::kInclude:
1672 // ignored for now
1673 break;
1674 case KeyWord::kElse:
1675 case KeyWord::kElif:
1676 // todo: handle these
1677 break;
1678 default:
1679 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001680 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001681 }
1682 break;
1683 case Bracket::kAngle:
1684 // pick up templated function pieces when method is found
1685 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001686 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001687 if (!this->parseObjects(child, markupDef)) {
1688 return false;
1689 }
Cary Clark73fa9722017-08-29 17:36:51 -04001690 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001691 case Bracket::kSquare: {
1692 // check to see if parent is operator, the only case we handle so far
1693 auto prev = child->fParent->fTokens.begin();
1694 std::advance(prev, child->fParentIndex - 1);
1695 if (KeyWord::kOperator != prev->fKeyWord) {
1696 return child->reportError<bool>("expected operator overload");
1697 }
1698 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001699 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001700 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001701 }
1702 break;
1703 case Definition::Type::kWord:
1704 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001705 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001706 }
1707 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001708 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001709 }
1710 break;
1711 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001712 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001713 break;
1714 }
1715 return true;
1716}
1717
1718bool IncludeParser::parseTemplate() {
1719
1720 return true;
1721}
1722
1723bool IncludeParser::parseTypedef() {
1724
1725 return true;
1726}
1727
1728bool IncludeParser::parseUnion() {
1729
1730 return true;
1731}
1732
1733bool IncludeParser::parseChar() {
1734 char test = *fChar;
1735 if ('\\' == fPrev) {
1736 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001737// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001738 fLine = fChar + 1;
1739 }
1740 goto done;
1741 }
1742 switch (test) {
1743 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001744// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001745 fLine = fChar + 1;
1746 if (fInChar) {
1747 return reportError<bool>("malformed char");
1748 }
1749 if (fInString) {
1750 return reportError<bool>("malformed string");
1751 }
1752 if (!this->checkForWord()) {
1753 return false;
1754 }
1755 if (Bracket::kPound == this->topBracket()) {
1756 KeyWord keyWord = fParent->fKeyWord;
1757 if (KeyWord::kNone == keyWord) {
1758 return this->reportError<bool>("unhandled preprocessor directive");
1759 }
1760 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) {
1761 this->popBracket();
1762 }
1763 } else if (Bracket::kSlashSlash == this->topBracket()) {
1764 this->popBracket();
1765 }
1766 break;
1767 case '*':
1768 if (!fInCharCommentString && '/' == fPrev) {
1769 this->pushBracket(Bracket::kSlashStar);
1770 }
1771 if (!this->checkForWord()) {
1772 return false;
1773 }
1774 if (!fInCharCommentString) {
1775 this->addPunctuation(Punctuation::kAsterisk);
1776 }
1777 break;
1778 case '/':
1779 if ('*' == fPrev) {
1780 if (!fInCharCommentString) {
1781 return reportError<bool>("malformed closing comment");
1782 }
1783 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001784 TextParser::Save save(this);
1785 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001786 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001787 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001788 }
1789 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001790 }
Cary Clark8032b982017-07-28 11:04:54 -04001791 if (!fInCharCommentString && '/' == fPrev) {
1792 this->pushBracket(Bracket::kSlashSlash);
1793 break;
1794 }
1795 if (!this->checkForWord()) {
1796 return false;
1797 }
1798 break;
1799 case '\'':
1800 if (Bracket::kChar == this->topBracket()) {
1801 this->popBracket();
1802 } else if (!fInComment && !fInString) {
1803 if (fIncludeWord) {
1804 return this->reportError<bool>("word then single-quote");
1805 }
1806 this->pushBracket(Bracket::kChar);
1807 }
1808 break;
1809 case '\"':
1810 if (Bracket::kString == this->topBracket()) {
1811 this->popBracket();
1812 } else if (!fInComment && !fInChar) {
1813 if (fIncludeWord) {
1814 return this->reportError<bool>("word then double-quote");
1815 }
1816 this->pushBracket(Bracket::kString);
1817 }
1818 break;
1819 case ':':
1820 case '(':
1821 case '[':
1822 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001823 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1824 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1825 this->pushBracket(Bracket::kDebugCode);
1826 break;
1827 }
Cary Clark8032b982017-07-28 11:04:54 -04001828 if (fInCharCommentString) {
1829 break;
1830 }
1831 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1832 break;
1833 }
1834 if (!fInBrace) {
1835 if (!this->checkForWord()) {
1836 return false;
1837 }
1838 if (':' == test && !fInFunction) {
1839 break;
1840 }
1841 if ('{' == test) {
1842 this->addPunctuation(Punctuation::kLeftBrace);
1843 } else if (':' == test) {
1844 this->addPunctuation(Punctuation::kColon);
1845 }
1846 }
1847 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1848 && Bracket::kColon == fInBrace->fBracket) {
1849 Definition* braceParent = fParent->fParent;
1850 braceParent->fChildren.pop_back();
1851 braceParent->fTokens.pop_back();
1852 fParent = braceParent;
1853 fInBrace = nullptr;
1854 }
1855 this->pushBracket(
1856 '(' == test ? Bracket::kParen :
1857 '[' == test ? Bracket::kSquare :
1858 '{' == test ? Bracket::kBrace :
1859 Bracket::kColon);
1860 if (!fInBrace
1861 && ('{' == test || (':' == test && ' ' >= fChar[1]))
1862 && fInFunction) {
1863 fInBrace = fParent;
1864 }
1865 } break;
1866 case '<':
1867 if (fInCharCommentString || fInBrace) {
1868 break;
1869 }
1870 if (!this->checkForWord()) {
1871 return false;
1872 }
1873 if (fInEnum) {
1874 break;
1875 }
1876 this->pushBracket(Bracket::kAngle);
1877 break;
1878 case ')':
1879 case ']':
1880 case '}': {
1881 if (fInCharCommentString) {
1882 break;
1883 }
1884 if (!fInBrace) {
1885 if (!this->checkForWord()) {
1886 return false;
1887 }
1888 }
1889 bool popBraceParent = fInBrace == fParent;
1890 if ((')' == test ? Bracket::kParen :
1891 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
1892 this->popBracket();
1893 if (!fInFunction) {
1894 bool deprecatedMacro = false;
1895 if (')' == test) {
1896 auto iter = fParent->fTokens.end();
1897 bool lookForWord = false;
1898 while (fParent->fTokens.begin() != iter) {
1899 --iter;
1900 if (lookForWord) {
1901 if (Definition::Type::kWord != iter->fType) {
1902 break;
1903 }
1904 string word(iter->fContentStart, iter->length());
1905 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
1906 deprecatedMacro = true;
1907 // remove macro paren (would confuse method parsing later)
Ben Wagner63fd7602017-10-09 15:45:33 -04001908 fParent->fTokens.pop_back();
Cary Clark8032b982017-07-28 11:04:54 -04001909 fParent->fChildren.pop_back();
1910 }
1911 break;
1912 }
1913 if (Definition::Type::kBracket != iter->fType) {
1914 break;
1915 }
1916 if (Bracket::kParen != iter->fBracket) {
1917 break;
1918 }
1919 lookForWord = true;
1920 }
1921 }
1922 fInFunction = ')' == test && !deprecatedMacro;
1923 } else {
1924 fInFunction = '}' != test;
1925 }
Cary Clark73fa9722017-08-29 17:36:51 -04001926 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
1927 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04001928 } else {
1929 return reportError<bool>("malformed close bracket");
1930 }
1931 if (popBraceParent) {
1932 Definition* braceParent = fInBrace->fParent;
1933 braceParent->fChildren.pop_back();
1934 braceParent->fTokens.pop_back();
1935 fInBrace = nullptr;
1936 }
1937 } break;
1938 case '>':
1939 if (fInCharCommentString || fInBrace) {
1940 break;
1941 }
1942 if (!this->checkForWord()) {
1943 return false;
1944 }
1945 if (fInEnum) {
1946 break;
1947 }
Cary Clarka560c472017-11-27 10:44:06 -05001948 if (Bracket::kPound == this->topBracket()) {
1949 break;
1950 }
Cary Clark8032b982017-07-28 11:04:54 -04001951 if (Bracket::kAngle == this->topBracket()) {
1952 this->popBracket();
1953 } else {
1954 return reportError<bool>("malformed close angle bracket");
1955 }
1956 break;
1957 case '#': {
1958 if (fInCharCommentString || fInBrace) {
1959 break;
1960 }
1961 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
1962 this->pushBracket(Bracket::kPound);
1963 break;
1964 }
1965 case '&':
1966 case ',':
1967 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05001968 case '+':
1969 case '=':
1970 case '-':
1971 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04001972 if (fInCharCommentString || fInBrace) {
1973 break;
1974 }
1975 if (!this->checkForWord()) {
1976 return false;
1977 }
1978 break;
1979 case ';':
1980 if (fInCharCommentString || fInBrace) {
1981 break;
1982 }
1983 if (!this->checkForWord()) {
1984 return false;
1985 }
1986 if (Definition::Type::kKeyWord == fParent->fType
1987 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001988 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
1989 KeyWord::kEnum == fParent->fParent->fKeyWord) {
1990 this->popObject();
1991 }
Cary Clark8032b982017-07-28 11:04:54 -04001992 if (KeyWord::kEnum == fParent->fKeyWord) {
1993 fInEnum = false;
1994 }
1995 this->popObject();
1996 } else if (Definition::Type::kBracket == fParent->fType
1997 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
1998 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
1999 list<Definition>::iterator baseIter = fParent->fTokens.end();
2000 list<Definition>::iterator namedIter = fParent->fTokens.end();
2001 for (auto tokenIter = fParent->fTokens.end();
2002 fParent->fTokens.begin() != tokenIter--; ) {
2003 if (tokenIter->fLineCount == fLineCount) {
2004 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2005 if (namedIter != fParent->fTokens.end()) {
2006 return reportError<bool>("found two named member tokens");
2007 }
2008 namedIter = tokenIter;
2009 }
2010 baseIter = tokenIter;
2011 } else {
2012 break;
2013 }
2014 }
2015 // FIXME: if a member definition spans multiple lines, this won't work
2016 if (namedIter != fParent->fTokens.end()) {
2017 if (baseIter == namedIter) {
2018 return this->reportError<bool>("expected type before named token");
2019 }
2020 Definition* member = &*namedIter;
2021 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002022 if (!member->fTerminator) {
2023 member->fTerminator = member->fContentEnd;
2024 }
Cary Clark8032b982017-07-28 11:04:54 -04002025 fParent->fChildren.push_back(member);
2026 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2027 member->fChildren.push_back(&*nameType);
2028 }
2029
2030 }
2031 } else if (fParent->fChildren.size() > 0) {
2032 auto lastIter = fParent->fChildren.end();
2033 Definition* priorEnum;
2034 while (fParent->fChildren.begin() != lastIter) {
2035 std::advance(lastIter, -1);
2036 priorEnum = *lastIter;
2037 if (Definition::Type::kBracket != priorEnum->fType ||
2038 (Bracket::kSlashSlash != priorEnum->fBracket
2039 && Bracket::kSlashStar != priorEnum->fBracket)) {
2040 break;
2041 }
2042 }
2043 if (Definition::Type::kKeyWord == priorEnum->fType
2044 && KeyWord::kEnum == priorEnum->fKeyWord) {
2045 auto tokenWalker = fParent->fTokens.begin();
2046 std::advance(tokenWalker, priorEnum->fParentIndex);
2047 SkASSERT(KeyWord::kEnum == tokenWalker->fKeyWord);
2048 while (tokenWalker != fParent->fTokens.end()) {
2049 std::advance(tokenWalker, 1);
2050 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2051 break;
2052 }
2053 }
2054 while (tokenWalker != fParent->fTokens.end()) {
2055 std::advance(tokenWalker, 1);
2056 const Definition* test = &*tokenWalker;
2057 if (Definition::Type::kBracket != test->fType ||
2058 (Bracket::kSlashSlash != test->fBracket
2059 && Bracket::kSlashStar != test->fBracket)) {
2060 break;
2061 }
2062 }
2063 Definition* start = &*tokenWalker;
2064 bool foundExpected = true;
2065 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2066 const Definition* test = &*tokenWalker;
2067 if (expected != test->fKeyWord) {
2068 foundExpected = false;
2069 break;
2070 }
2071 if (tokenWalker == fParent->fTokens.end()) {
2072 break;
2073 }
2074 std::advance(tokenWalker, 1);
2075 }
2076 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2077 const char* nameStart = tokenWalker->fStart;
2078 std::advance(tokenWalker, 1);
2079 if (tokenWalker != fParent->fTokens.end()) {
2080 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2081 tp.skipToNonAlphaNum();
2082 start->fName = string(nameStart, tp.fChar - nameStart);
2083 start->fContentEnd = fChar;
2084 priorEnum->fChildren.emplace_back(start);
2085 }
2086 }
2087 }
2088 }
2089 this->addPunctuation(Punctuation::kSemicolon);
2090 fInFunction = false;
2091 break;
2092 case '~':
2093 if (fInEnum) {
2094 break;
2095 }
2096 case '0': case '1': case '2': case '3': case '4':
2097 case '5': case '6': case '7': case '8': case '9':
2098 // TODO: don't want to parse numbers, but do need to track for enum defs
2099 // break;
2100 case 'A': case 'B': case 'C': case 'D': case 'E':
2101 case 'F': case 'G': case 'H': case 'I': case 'J':
2102 case 'K': case 'L': case 'M': case 'N': case 'O':
2103 case 'P': case 'Q': case 'R': case 'S': case 'T':
2104 case 'U': case 'V': case 'W': case 'X': case 'Y':
2105 case 'Z': case '_':
2106 case 'a': case 'b': case 'c': case 'd': case 'e':
2107 case 'f': case 'g': case 'h': case 'i': case 'j':
2108 case 'k': case 'l': case 'm': case 'n': case 'o':
2109 case 'p': case 'q': case 'r': case 's': case 't':
2110 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002111 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002112 if (fInCharCommentString || fInBrace) {
2113 break;
2114 }
2115 if (!fIncludeWord) {
2116 fIncludeWord = fChar;
2117 }
2118 break;
2119 }
2120done:
2121 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002122 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002123 return true;
2124}
2125
2126void IncludeParser::validate() const {
2127 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2128 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2129 }
2130 IncludeParser::ValidateKeyWords();
2131}