blob: ee2b95a1341c1c1db77b10a22146f082f6db0e7d [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 Clark8032b982017-07-28 11:04:54 -040013 { "bool", KeyWord::kBool, KeyProperty::kNumber },
14 { "char", KeyWord::kChar, KeyProperty::kNumber },
15 { "class", KeyWord::kClass, KeyProperty::kObject },
16 { "const", KeyWord::kConst, KeyProperty::kModifier },
17 { "constexpr", KeyWord::kConstExpr, KeyProperty::kModifier },
18 { "define", KeyWord::kDefine, KeyProperty::kPreprocessor },
19 { "double", KeyWord::kDouble, KeyProperty::kNumber },
20 { "elif", KeyWord::kElif, KeyProperty::kPreprocessor },
21 { "else", KeyWord::kElse, KeyProperty::kPreprocessor },
22 { "endif", KeyWord::kEndif, KeyProperty::kPreprocessor },
23 { "enum", KeyWord::kEnum, KeyProperty::kObject },
24 { "float", KeyWord::kFloat, KeyProperty::kNumber },
25 { "friend", KeyWord::kFriend, KeyProperty::kModifier },
26 { "if", KeyWord::kIf, KeyProperty::kPreprocessor },
27 { "ifdef", KeyWord::kIfdef, KeyProperty::kPreprocessor },
28 { "ifndef", KeyWord::kIfndef, KeyProperty::kPreprocessor },
29 { "include", KeyWord::kInclude, KeyProperty::kPreprocessor },
30 { "inline", KeyWord::kInline, KeyProperty::kModifier },
31 { "int", KeyWord::kInt, KeyProperty::kNumber },
32 { "operator", KeyWord::kOperator, KeyProperty::kFunction },
33 { "private", KeyWord::kPrivate, KeyProperty::kClassSection },
34 { "protected", KeyWord::kProtected, KeyProperty::kClassSection },
35 { "public", KeyWord::kPublic, KeyProperty::kClassSection },
36 { "signed", KeyWord::kSigned, KeyProperty::kNumber },
37 { "size_t", KeyWord::kSize_t, KeyProperty::kNumber },
38 { "static", KeyWord::kStatic, KeyProperty::kModifier },
39 { "struct", KeyWord::kStruct, KeyProperty::kObject },
40 { "template", KeyWord::kTemplate, KeyProperty::kObject },
41 { "typedef", KeyWord::kTypedef, KeyProperty::kObject },
Cary Clarkd0530ba2017-09-14 11:25:39 -040042 { "uint16_t", KeyWord::kUint16_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040043 { "uint32_t", KeyWord::kUint32_t, KeyProperty::kNumber },
Cary Clarkd0530ba2017-09-14 11:25:39 -040044 { "uint64_t", KeyWord::kUint64_t, KeyProperty::kNumber },
45 { "uint8_t", KeyWord::kUint8_t, KeyProperty::kNumber },
Cary Clark8032b982017-07-28 11:04:54 -040046 { "union", KeyWord::kUnion, KeyProperty::kObject },
47 { "unsigned", KeyWord::kUnsigned, KeyProperty::kNumber },
48 { "void", KeyWord::kVoid, KeyProperty::kNumber },
49};
50
51const size_t kKeyWordCount = SK_ARRAY_COUNT(kKeyWords);
52
53KeyWord IncludeParser::FindKey(const char* start, const char* end) {
54 int ch = 0;
55 for (size_t index = 0; index < kKeyWordCount; ) {
56 if (start[ch] > kKeyWords[index].fName[ch]) {
57 ++index;
58 if (ch > 0 && kKeyWords[index - 1].fName[ch - 1] < kKeyWords[index].fName[ch - 1]) {
59 return KeyWord::kNone;
60 }
61 continue;
62 }
63 if (start[ch] < kKeyWords[index].fName[ch]) {
64 return KeyWord::kNone;
65 }
66 ++ch;
67 if (start + ch >= end) {
Cary Clarkbad5ad72017-08-03 17:14:08 -040068 if (end - start < (int) strlen(kKeyWords[index].fName)) {
69 return KeyWord::kNone;
70 }
Cary Clark8032b982017-07-28 11:04:54 -040071 return kKeyWords[index].fKeyWord;
72 }
73 }
74 return KeyWord::kNone;
75}
76
77void IncludeParser::ValidateKeyWords() {
78 for (size_t index = 1; index < kKeyWordCount; ++index) {
79 SkASSERT((int) kKeyWords[index - 1].fKeyWord + 1
80 == (int) kKeyWords[index].fKeyWord);
81 SkASSERT(0 > strcmp(kKeyWords[index - 1].fName, kKeyWords[index].fName));
82 }
83}
84
85void IncludeParser::addKeyword(KeyWord keyWord) {
86 fParent->fTokens.emplace_back(keyWord, fIncludeWord, fChar, fLineCount, fParent);
87 fIncludeWord = nullptr;
88 if (KeyProperty::kObject == kKeyWords[(int) keyWord].fProperty) {
89 Definition* def = &fParent->fTokens.back();
90 this->addDefinition(def);
91 if (KeyWord::kEnum == fParent->fKeyWord) {
92 fInEnum = true;
93 }
94 }
95}
96
97void IncludeParser::checkForMissingParams(const vector<string>& methodParams,
98 const vector<string>& foundParams) {
99 for (auto& methodParam : methodParams) {
100 bool found = false;
101 for (auto& foundParam : foundParams) {
102 if (methodParam == foundParam) {
103 found = true;
104 break;
105 }
106 }
107 if (!found) {
Cary Clark9174bda2017-09-19 17:39:32 -0400108 this->writeEndTag("Param", methodParam, 2);
Cary Clark8032b982017-07-28 11:04:54 -0400109 }
110 }
111 for (auto& foundParam : foundParams) {
112 bool found = false;
113 for (auto& methodParam : methodParams) {
114 if (methodParam == foundParam) {
115 found = true;
116 break;
117 }
118 }
119 if (!found) {
120 this->reportError("doxygen param does not match method declaration");
121 }
122 }
123}
124
125bool IncludeParser::checkForWord() {
126 if (!fIncludeWord) {
127 return true;
128 }
129 KeyWord keyWord = FindKey(fIncludeWord, fChar);
130 if (KeyWord::kNone != keyWord) {
131 if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
132 this->addKeyword(keyWord);
133 return true;
134 }
135 } else {
136 this->addWord();
137 return true;
138 }
139 Definition* poundDef = fParent;
140 if (!fParent) {
141 return reportError<bool>("expected parent");
142 }
143 if (Definition::Type::kBracket != poundDef->fType) {
144 return reportError<bool>("expected bracket");
145 }
146 if (Bracket::kPound != poundDef->fBracket) {
147 return reportError<bool>("expected preprocessor");
148 }
149 if (KeyWord::kNone != poundDef->fKeyWord) {
150 return reportError<bool>("already found keyword");
151 }
152 poundDef->fKeyWord = keyWord;
153 fIncludeWord = nullptr;
154 switch (keyWord) {
155 // these do not link to other # directives
156 case KeyWord::kDefine:
157 case KeyWord::kInclude:
158 break;
159 // these start a # directive link
160 case KeyWord::kIf:
161 case KeyWord::kIfdef:
162 case KeyWord::kIfndef:
163 break;
164 // these continue a # directive link
165 case KeyWord::kElif:
166 case KeyWord::kElse: {
167 this->popObject(); // pop elif
168 if (Bracket::kPound != fParent->fBracket) {
169 return this->reportError<bool>("expected preprocessor directive");
170 }
171 this->popBracket(); // pop if
172 poundDef->fParent = fParent;
173 this->addDefinition(poundDef); // push elif back
174 } break;
175 // this ends a # directive link
176 case KeyWord::kEndif:
177 // FIXME : should this be calling popBracket() instead?
178 this->popObject(); // pop endif
179 if (Bracket::kPound != fParent->fBracket) {
180 return this->reportError<bool>("expected preprocessor directive");
181 }
182 this->popBracket(); // pop if/else
183 break;
184 default:
185 SkASSERT(0);
186 }
187 return true;
188}
189
190string IncludeParser::className() const {
191 string name(fParent->fName);
192 size_t slash = name.find_last_of("/");
193 if (string::npos == slash) {
194 slash = name.find_last_of("\\");
195 }
196 SkASSERT(string::npos != slash);
197 string result = name.substr(slash);
198 result = result.substr(1, result.size() - 3);
199 return result;
200}
201
202bool IncludeParser::crossCheck(BmhParser& bmhParser) {
203 string className = this->className();
204 string classPrefix = className + "::";
205 RootDefinition* root = &bmhParser.fClassMap[className];
206 root->clearVisited();
207 for (auto& classMapper : fIClassMap) {
208 if (className != classMapper.first
209 && classPrefix != classMapper.first.substr(0, classPrefix.length())) {
210 continue;
211 }
212 auto& classMap = classMapper.second;
213 auto& tokens = classMap.fTokens;
214 for (const auto& token : tokens) {
215 if (token.fPrivate) {
216 continue;
217 }
218 string fullName = classMapper.first + "::" + token.fName;
Cary Clarkce101242017-09-01 15:51:02 -0400219 const Definition* def = root->find(fullName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400220 switch (token.fMarkType) {
221 case MarkType::kMethod: {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400222 if (this->internalName(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400223 continue;
224 }
Cary Clark8032b982017-07-28 11:04:54 -0400225 if (!def) {
226 string paramName = className + "::";
227 paramName += string(token.fContentStart,
228 token.fContentEnd - token.fContentStart);
Cary Clarkce101242017-09-01 15:51:02 -0400229 def = root->find(paramName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400230 if (!def && 0 == token.fName.find("operator")) {
231 string operatorName = className + "::";
232 TextParser oper("", token.fStart, token.fContentEnd, 0);
233 const char* start = oper.strnstr("operator", token.fContentEnd);
234 SkASSERT(start);
235 oper.skipTo(start);
236 oper.skipToEndBracket('(');
237 int parens = 0;
238 do {
239 if ('(' == oper.peek()) {
240 ++parens;
241 } else if (')' == oper.peek()) {
242 --parens;
243 }
244 } while (!oper.eof() && oper.next() && parens > 0);
245 operatorName += string(start, oper.fChar - start);
Cary Clarkce101242017-09-01 15:51:02 -0400246 def = root->find(operatorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400247 }
248 }
249 if (!def) {
250 int skip = !strncmp(token.fContentStart, "explicit ", 9) ? 9 : 0;
251 skip = !strncmp(token.fContentStart, "virtual ", 8) ? 8 : skip;
252 string constructorName = className + "::";
253 constructorName += string(token.fContentStart + skip,
254 token.fContentEnd - token.fContentStart - skip);
Cary Clarkce101242017-09-01 15:51:02 -0400255 def = root->find(constructorName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400256 }
257 if (!def && 0 == token.fName.find("SK_")) {
258 string incName = token.fName + "()";
259 string macroName = className + "::" + incName;
Cary Clarkce101242017-09-01 15:51:02 -0400260 def = root->find(macroName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400261 if (def) {
262 if (def->fName == incName) {
263 def->fVisited = true;
264 if ("SK_TO_STRING_NONVIRT" == token.fName) {
Cary Clarkce101242017-09-01 15:51:02 -0400265 def = root->find(className + "::toString",
266 RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400267 if (def) {
268 def->fVisited = true;
269 } else {
270 SkDebugf("missing toString bmh: %s\n", fullName.c_str());
271 }
272 }
273 break;
274 } else {
275 SkDebugf("method macro differs from bmh: %s\n", fullName.c_str());
276 }
277 }
278 }
279 if (!def) {
280 bool allLower = true;
281 for (size_t index = 0; index < token.fName.length(); ++index) {
282 if (!islower(token.fName[index])) {
283 allLower = false;
284 break;
285 }
286 }
287 if (allLower) {
288 string lowerName = className + "::" + token.fName + "()";
Cary Clarkce101242017-09-01 15:51:02 -0400289 def = root->find(lowerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400290 }
291 }
292 if (!def) {
Cary Clark73fa9722017-08-29 17:36:51 -0400293 if ("SK_ATTR_DEPRECATED" == token.fName) {
294 break;
295 }
296 if (0 == token.fName.find("SkDEBUGCODE")) {
297 break;
298 }
299 }
300 if (!def) {
301 // simple method names inside nested classes have a bug and are missing trailing parens
302 string withParens = fullName + "()"; // FIXME: this shouldn't be necessary
Cary Clarkce101242017-09-01 15:51:02 -0400303 def = root->find(withParens, RootDefinition::AllowParens::kNo);
Cary Clark73fa9722017-08-29 17:36:51 -0400304 }
305 if (!def) {
Cary Clark8032b982017-07-28 11:04:54 -0400306 SkDebugf("method missing from bmh: %s\n", fullName.c_str());
307 break;
308 }
Cary Clark73fa9722017-08-29 17:36:51 -0400309 if (def->crossCheck2(token)) {
Cary Clark8032b982017-07-28 11:04:54 -0400310 def->fVisited = true;
Cary Clark73fa9722017-08-29 17:36:51 -0400311 if (MarkType::kDefinedBy == def->fMarkType) {
312 def->fParent->fVisited = true;
313 }
Cary Clark8032b982017-07-28 11:04:54 -0400314 } else {
315 SkDebugf("method differs from bmh: %s\n", fullName.c_str());
316 }
317 } break;
318 case MarkType::kComment:
319 break;
Cary Clarkf05bdda2017-08-24 12:59:48 -0400320 case MarkType::kEnumClass:
Cary Clark8032b982017-07-28 11:04:54 -0400321 case MarkType::kEnum: {
322 if (!def) {
323 // work backwards from first word to deduce #Enum name
324 TextParser firstMember("", token.fStart, token.fContentEnd, 0);
325 SkAssertResult(firstMember.skipName("enum"));
326 SkAssertResult(firstMember.skipToEndBracket('{'));
327 firstMember.next();
328 firstMember.skipWhiteSpace();
329 SkASSERT('k' == firstMember.peek());
330 const char* savePos = firstMember.fChar;
331 firstMember.skipToNonAlphaNum();
332 const char* wordEnd = firstMember.fChar;
333 firstMember.fChar = savePos;
334 const char* lastUnderscore = nullptr;
335 do {
336 if (!firstMember.skipToEndBracket('_')) {
337 break;
338 }
339 if (firstMember.fChar > wordEnd) {
340 break;
341 }
342 lastUnderscore = firstMember.fChar;
343 } while (firstMember.next());
344 if (lastUnderscore) {
345 ++lastUnderscore;
346 string anonName = className + "::" + string(lastUnderscore,
347 wordEnd - lastUnderscore) + 's';
Cary Clarkce101242017-09-01 15:51:02 -0400348 def = root->find(anonName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400349 }
350 if (!def) {
351 SkDebugf("enum missing from bmh: %s\n", fullName.c_str());
352 break;
353 }
354 }
355 def->fVisited = true;
356 for (auto& child : def->fChildren) {
357 if (MarkType::kCode == child->fMarkType) {
358 def = child;
359 break;
360 }
361 }
362 if (MarkType::kCode != def->fMarkType) {
363 SkDebugf("enum code missing from bmh: %s\n", fullName.c_str());
364 break;
365 }
366 if (def->crossCheck(token)) {
367 def->fVisited = true;
368 } else {
369 SkDebugf("enum differs from bmh: %s\n", def->fName.c_str());
370 }
371 for (auto& child : token.fChildren) {
Cary Clarkf05bdda2017-08-24 12:59:48 -0400372 string constName = MarkType::kEnumClass == token.fMarkType ?
373 fullName : className;
374 constName += "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400375 def = root->find(constName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400376 if (!def) {
377 string innerName = classMapper.first + "::" + child->fName;
Cary Clarkce101242017-09-01 15:51:02 -0400378 def = root->find(innerName, RootDefinition::AllowParens::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400379 }
380 if (!def) {
381 if (string::npos == child->fName.find("Legacy_")) {
382 SkDebugf("const missing from bmh: %s\n", constName.c_str());
383 }
384 } else {
385 def->fVisited = true;
386 }
387 }
388 } break;
389 case MarkType::kMember:
390 if (def) {
391 def->fVisited = true;
392 } else {
393 SkDebugf("member missing from bmh: %s\n", fullName.c_str());
394 }
395 break;
396 default:
397 SkASSERT(0); // unhandled
398 break;
399 }
400 }
401 }
402 if (!root->dumpUnVisited()) {
403 SkDebugf("some struct elements not found; struct finding in includeParser is missing\n");
404 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400405 SkDebugf("cross-checked %s\n", className.c_str());
406 bmhParser.fWroteOut = true;
Cary Clark8032b982017-07-28 11:04:54 -0400407 return true;
408}
409
410IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
411 const string& name) {
412 string className;
413 const Definition* test = fParent;
414 while (Definition::Type::kFileType != test->fType) {
415 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
416 className = test->fName + "::";
417 break;
418 }
419 test = test->fParent;
420 }
421 className += name;
422 unordered_map<string, IClassDefinition>& map = fIClassMap;
423 IClassDefinition& markupDef = map[className];
424 if (markupDef.fStart) {
425 typedef IClassDefinition* IClassDefPtr;
426 return INHERITED::reportError<IClassDefPtr>("class already defined");
427 }
428 markupDef.fFileName = fFileName;
429 markupDef.fStart = includeDef.fStart;
430 markupDef.fContentStart = includeDef.fStart;
431 markupDef.fName = className;
432 markupDef.fContentEnd = includeDef.fContentEnd;
433 markupDef.fTerminator = includeDef.fTerminator;
434 markupDef.fParent = fParent;
435 markupDef.fLineCount = fLineCount;
436 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
437 MarkType::kStruct : MarkType::kClass;
438 markupDef.fKeyWord = includeDef.fKeyWord;
439 markupDef.fType = Definition::Type::kMark;
440 fParent = &markupDef;
441 return &markupDef;
442}
443
444void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
445 auto& tokens = classDef.fTokens;
446 for (auto& token : tokens) {
447 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
448 continue;
449 }
450 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400451 this->writeString(
452 "# ------------------------------------------------------------------------------");
453 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400454 }
455 switch (token.fMarkType) {
456 case MarkType::kEnum:
Cary Clark9174bda2017-09-19 17:39:32 -0400457 this->dumpEnum(token);
Cary Clark8032b982017-07-28 11:04:54 -0400458 break;
459 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400460 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400461 break;
462 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400463 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400464 continue;
465 break;
466 default:
467 SkASSERT(0);
468 }
469 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400470 this->writeTag("Example");
471 this->writeEndTag();
472 this->lf(2);
473 this->writeEndTag("ToDo", "incomplete");
474 this->lf(2);
475 this->writeEndTag();
476 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400477 }
478}
Cary Clark9174bda2017-09-19 17:39:32 -0400479void IncludeParser::dumpComment(const Definition& token) {
480 fLineCount = token.fLineCount;
481 fChar = fLine = token.fContentStart;
482 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400483 bool sawParam = false;
484 bool multiline = false;
485 bool sawReturn = false;
486 bool sawComment = false;
487 bool methodHasReturn = false;
488 vector<string> methodParams;
489 vector<string> foundParams;
490 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400491 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
492 token.fLineCount);
493 if (MarkType::kMethod == token.fMarkType) {
494 methodName.fName = string(token.fContentStart,
495 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400496 methodHasReturn = !methodParser.startsWith("void ")
497 && !methodParser.strnchr('~', methodParser.fEnd);
498 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
499 const char* nextEnd = paren;
500 do {
501 string paramName;
502 methodParser.fChar = nextEnd + 1;
503 methodParser.skipSpace();
504 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
505 continue;
506 }
507 methodParams.push_back(paramName);
508 } while (')' != nextEnd[0]);
509 }
Cary Clark9174bda2017-09-19 17:39:32 -0400510 for (const auto& child : token.fTokens) {
511 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
512 break;
513 }
Cary Clark8032b982017-07-28 11:04:54 -0400514 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400515 if (child.fPrivate) {
516 break;
517 }
Cary Clark8032b982017-07-28 11:04:54 -0400518 if ('@' == child.fContentStart[0]) {
519 TextParser parser(&child);
520 do {
521 parser.next();
522 if (parser.startsWith("param ")) {
523 parser.skipWord("param");
524 const char* parmStart = parser.fChar;
525 parser.skipToSpace();
526 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
527 parser.skipWhiteSpace();
528 do {
529 size_t nextComma = parmName.find(',');
530 string piece;
531 if (string::npos == nextComma) {
532 piece = parmName;
533 parmName = "";
534 } else {
535 piece = parmName.substr(0, nextComma);
536 parmName = parmName.substr(nextComma + 1);
537 }
538 if (sawParam) {
539 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400540 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400541 }
Cary Clark9174bda2017-09-19 17:39:32 -0400542 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400543 } else {
544 if (sawComment) {
545 this->nl();
546 }
547 this->lf(2);
548 }
549 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400550 this->writeTag("Param", piece);
551 this->writeSpace(2);
552 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
553 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400554 sawParam = true;
555 sawComment = false;
556 } while (parmName.length());
557 parser.skipTo(parser.fEnd);
558 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
559 parser.skipWord("return");
560 if ('s' == parser.peek()) {
561 parser.next();
562 }
563 if (sawParam) {
564 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400565 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400566 }
Cary Clark9174bda2017-09-19 17:39:32 -0400567 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400568 }
569 this->checkForMissingParams(methodParams, foundParams);
570 sawParam = false;
571 sawComment = false;
572 multiline = false;
573 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400574 this->writeTag("Return");
575 this->writeSpace(2);
576 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
577 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400578 sawReturn = true;
579 parser.skipTo(parser.fEnd);
580 } else {
581 this->reportError("unexpected doxygen directive");
582 }
583 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400584 } else if (child.length() > 1) {
585 const char* start = child.fContentStart;
586 ptrdiff_t length = child.fContentEnd - start;
587 SkASSERT(length >= 0);
588 while (length && '/' == start[0]) {
589 start += 1;
590 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400591 }
Cary Clark9174bda2017-09-19 17:39:32 -0400592 while (length && '/' == start[length - 1]) {
593 length -= 1;
594 if (length && '*' == start[length - 1]) {
595 length -= 1;
596 }
597 }
598 if (length) {
599 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
600 if (sawParam || sawReturn) {
601 this->indentToColumn(8);
602 }
603 this->writeBlock(length, start);
604 this->writeSpace();
605 sawComment = true;
606 if (sawParam || sawReturn) {
607 multiline = true;
608 }
Cary Clark8032b982017-07-28 11:04:54 -0400609 }
610 }
611 }
612 }
613 if (sawParam || sawReturn) {
614 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400615 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400616 }
Cary Clark9174bda2017-09-19 17:39:32 -0400617 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400618 }
619 if (!sawReturn) {
620 if (!sawParam) {
621 if (sawComment) {
622 this->nl();
623 }
624 this->lf(2);
625 }
626 this->checkForMissingParams(methodParams, foundParams);
627 }
628 if (methodHasReturn != sawReturn) {
629 if (!methodHasReturn) {
630 this->reportError("unexpected doxygen return");
631 } else {
632 if (sawComment) {
633 this->nl();
634 }
635 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400636 this->writeEndTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400637 }
638 }
639}
640
Cary Clark9174bda2017-09-19 17:39:32 -0400641void IncludeParser::dumpEnum(const Definition& token) {
642 this->writeTag("Enum", token.fName);
643 this->lf(2);
644 this->writeString("#Code");
645 this->lfAlways(1);
646 this->indentToColumn(4);
647 this->writeString("enum");
648 this->writeSpace();
649 if ("_anonymous" != token.fName.substr(0, 10)) {
650 this->writeString(token.fName);
651 this->writeSpace();
652 }
653 this->writeString("{");
654 this->lfAlways(1);
655 for (auto& child : token.fChildren) {
656 this->indentToColumn(8);
657 this->writeString(child->fName);
658 if (child->length()) {
659 this->writeSpace();
660 this->writeBlock(child->length(), child->fContentStart);
661 }
662 if (',' != fLastChar) {
663 this->writeString(",");
664 }
665 this->lfAlways(1);
666 }
667 this->indentToColumn(4);
668 this->writeString("};");
669 this->lf(1);
670 this->writeString("##");
671 this->lf(2);
672 this->dumpComment(token);
673 for (auto& child : token.fChildren) {
674 // start here;
675 // get comments before
676 // or after const values
677 this->writeString("#Const");
678 this->writeSpace();
679 this->writeString(child->fName);
680 TextParser val(child);
681 if (!val.eof()) {
682 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
683 val.next();
684 val.skipSpace();
685 const char* valEnd = val.anyOf(",\n");
686 if (!valEnd) {
687 valEnd = val.fEnd;
688 }
689 this->writeSpace();
690 this->writeBlock(valEnd - val.fStart, val.fStart);
691 } else {
692 this->writeSpace();
693 this->writeDefinition(*child);
694 }
695 }
696 this->lf(1);
697 for (auto comment : child->fChildren) {
698 if (MarkType::kComment == comment->fMarkType) {
699 TextParser parser(comment);
700 parser.skipExact("*");
701 parser.skipExact("*");
702 while (!parser.eof() && parser.skipWhiteSpace()) {
703 parser.skipExact("*");
704 parser.skipWhiteSpace();
705 const char* start = parser.fChar;
706 parser.skipToEndBracket('\n');
707 this->lf(1);
708 this->writeBlock(parser.fChar - start, start);
709 }
710 }
711 }
712 this->writeEndTag();
713 }
714 this->lf(2);
715}
716
717void IncludeParser::dumpMethod(const Definition& token) {
718 this->writeString("#Method");
719 this->writeSpace();
720 if ("SK_TO_STRING_NONVIRT" == token.fName) {
721 this->writeString("void toString(SkString* str) const;");
722 this->lf(2);
723 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
724 this->lf(2);
725 this->writeTag("Private");
726 this->lf(1);
727 this->writeString("macro expands to: void toString(SkString* str) const;");
728 this->writeEndTag();
729 this->lf(2);
730 const char desc[] =
731 "Creates string representation. The representation is read by\n"
732 "internal debugging tools. The interface and implementation may be\n"
733 "suppressed by defining SK_IGNORE_TO_STRING.";
734 this->writeBlock(sizeof(desc) - 1, desc);
735 this->lf(2);
736 this->writeTag("Param", "str");
737 this->writeSpace(2);
738 this->writeString("storage for string representation");
739 this->writeSpace();
740 this->writeString("##");
741 this->lf(2);
742 return;
743 }
744 this->writeBlock(token.length(), token.fStart);
745 this->lf(1);
746 this->dumpComment(token);
747}
748
749void IncludeParser::dumpMember(const Definition& token) {
750 this->writeTag("Member");
751 this->writeSpace();
752 this->writeDefinition(token, token.fName, 2);
753 lf(1);
754 for (auto child : token.fChildren) {
755 this->writeDefinition(*child);
756 }
757 this->writeEndTag();
758 lf(2);
759}
760
Cary Clarkd0530ba2017-09-14 11:25:39 -0400761bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400762 for (const auto& member : fIClassMap) {
763 if (string::npos != member.first.find("::")) {
764 continue;
765 }
766 if (!this->dumpTokens(dir, member.first)) {
767 return false;
768 }
769 }
770 return true;
771}
772
773 // dump equivalent markup
774bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400775 string fileName = dir;
776 if (dir.length() && '/' != dir[dir.length() - 1]) {
777 fileName += '/';
778 }
779 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400780 fOut = fopen(fileName.c_str(), "wb");
781 if (!fOut) {
782 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400783 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400784 }
785 string prefixName = skClassName.substr(0, 2);
786 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
787 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400788 this->writeTagNoLF("Topic", topicName);
789 this->writeTag("Alias", topicName + "_Reference");
790 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400791 auto& classMap = fIClassMap[skClassName];
Cary Clark9174bda2017-09-19 17:39:32 -0400792 const char* containerType = kKeyWords[(int) classMap.fKeyWord].fName;
793 this->writeTag(containerType, skClassName);
794 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400795 auto& tokens = classMap.fTokens;
796 for (auto& token : tokens) {
797 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
798 continue;
799 }
Cary Clark9174bda2017-09-19 17:39:32 -0400800 this->writeDefinition(token);
801 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400802 }
803 this->lf(2);
804 string className(skClassName.substr(2));
805 vector<string> sortedClasses;
806 size_t maxLen = 0;
807 for (const auto& oneClass : fIClassMap) {
808 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
809 continue;
810 }
811 string structName = oneClass.first.substr(skClassName.length() + 2);
812 maxLen = SkTMax(maxLen, structName.length());
813 sortedClasses.emplace_back(structName);
814 }
Cary Clark9174bda2017-09-19 17:39:32 -0400815 this->writeTag("Topic", "Overview");
816 this->lf(2);
817 this->writeTag("Subtopic", "Subtopics");
818 this->writeEndTag("ToDo", "manually add subtopics");
819 this->writeTableHeader("topics", 0, "description");
820 this->writeTableTrailer();
821 this->writeEndTag();
822 this->lf(2);
823 if (maxLen) {
824 this->writeTag("Subtopic", "Structs");
825 this->writeTableHeader("description", maxLen, "struct");
826 for (auto& name : sortedClasses) {
827 this->writeTableRow(maxLen, name);
828 }
829 this->writeTableTrailer();
830 this->writeEndTag("Subtopic");
831 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400832 }
Cary Clark8032b982017-07-28 11:04:54 -0400833 maxLen = 0;
Cary Clark9174bda2017-09-19 17:39:32 -0400834 size_t constructorMax = 0;
835 size_t operatorMax = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400836 vector<string> sortedNames;
Cary Clark9174bda2017-09-19 17:39:32 -0400837 vector<string> constructorNames;
838 vector<string> operatorNames;
Cary Clark8032b982017-07-28 11:04:54 -0400839 for (const auto& token : classMap.fTokens) {
840 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
841 continue;
842 }
Cary Clark9174bda2017-09-19 17:39:32 -0400843 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400844 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
845 continue;
846 }
Cary Clark9174bda2017-09-19 17:39:32 -0400847 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
848 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
849 constructorMax = SkTMax(constructorMax, name.length());
850 constructorNames.emplace_back(name);
851 continue;
852 }
853 if (name.substr(0, 8) == "operator") {
854 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
855 operatorMax = SkTMax(operatorMax, name.length());
856 operatorNames.emplace_back(name);
857 continue;
858 }
Cary Clark8032b982017-07-28 11:04:54 -0400859 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
860 continue;
861 }
Cary Clark9174bda2017-09-19 17:39:32 -0400862 if ("SK_TO_STRING_NONVIRT" == name) {
863 name = "toString";
864 }
Cary Clark8032b982017-07-28 11:04:54 -0400865 size_t paren = name.find('(');
866 size_t funcLen = string::npos == paren ? name.length() : paren;
867 maxLen = SkTMax(maxLen, funcLen);
868 sortedNames.emplace_back(name);
869 }
Cary Clark9174bda2017-09-19 17:39:32 -0400870 if (constructorMax) {
871 std::sort(constructorNames.begin(), constructorNames.end());
872 this->writeTag("Subtopic", "Constructors");
873 this->writeTableHeader("description", constructorMax, "function");
874 for (auto& name : constructorNames) {
875 this->writeTableRow(constructorMax, name);
876 }
877 this->writeTableTrailer();
878 this->writeEndTag("Subtopic");
879 this->lf(2);
880 }
881 if (operatorMax) {
882 std::sort(operatorNames.begin(), operatorNames.end());
883 this->writeTag("Subtopic", "Operators");
884 this->writeTableHeader("description", operatorMax, "function");
885 for (auto& name : operatorNames) {
886 this->writeTableRow(operatorMax, name);
887 }
888 this->writeTableTrailer();
889 this->writeEndTag("Subtopic");
890 this->lf(2);
891 }
Cary Clark8032b982017-07-28 11:04:54 -0400892 std::sort(sortedNames.begin(), sortedNames.end());
Cary Clark9174bda2017-09-19 17:39:32 -0400893 this->writeTag("Subtopic", "Member_Functions");
894 this->writeTableHeader("description", maxLen, "function");
Cary Clark8032b982017-07-28 11:04:54 -0400895 for (auto& name : sortedNames) {
896 size_t paren = name.find('(');
897 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark9174bda2017-09-19 17:39:32 -0400898 this->writeTableRow(maxLen, name.substr(0, funcLen));
Cary Clark8032b982017-07-28 11:04:54 -0400899 }
Cary Clark9174bda2017-09-19 17:39:32 -0400900 this->writeTableTrailer();
901 this->writeEndTag("Subtopic");
902 this->lf(2);
903 this->writeEndTag("Topic");
904 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400905
906 for (auto& oneClass : fIClassMap) {
907 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
908 continue;
909 }
910 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -0400911 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -0400912 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -0400913 this->lf(2);
914 const char* containerType = kKeyWords[(int) oneClass.second.fKeyWord].fName;
915 this->writeTag(containerType, innerName);
916 this->lf(2);
917 this->writeTag("Code");
918 this->writeEndTag("ToDo", "fill this in manually");
919 this->writeEndTag();
920 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400921 for (auto& token : oneClass.second.fTokens) {
922 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
923 continue;
924 }
Cary Clark9174bda2017-09-19 17:39:32 -0400925 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -0400926 }
927 this->lf(2);
928 this->dumpClassTokens(oneClass.second);
929 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400930 this->writeEndTag(containerType, innerName);
931 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400932 }
933 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -0400934 this->writeEndTag(containerType, skClassName);
935 this->lf(2);
936 this->writeEndTag("Topic", topicName);
937 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -0400938 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -0400939 SkDebugf("wrote %s\n", fileName.c_str());
940 return true;
Cary Clark8032b982017-07-28 11:04:54 -0400941}
942
943bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
944 // add comment preceding class, if any
945 const Definition* parent = includeDef.fParent;
946 int index = includeDef.fParentIndex;
947 auto wordIter = parent->fTokens.begin();
948 std::advance(wordIter, index);
949 SkASSERT(&*wordIter == &includeDef);
950 while (parent->fTokens.begin() != wordIter) {
951 auto testIter = std::prev(wordIter);
952 if (Definition::Type::kWord != testIter->fType
953 && Definition::Type::kKeyWord != testIter->fType
954 && (Definition::Type::kBracket != testIter->fType
955 || Bracket::kAngle != testIter->fBracket)
956 && (Definition::Type::kPunctuation != testIter->fType
957 || Punctuation::kAsterisk != testIter->fPunctuation)) {
958 break;
959 }
960 wordIter = testIter;
961 }
962 auto commentIter = wordIter;
963 while (parent->fTokens.begin() != commentIter) {
964 auto testIter = std::prev(commentIter);
965 bool isComment = Definition::Type::kBracket == testIter->fType
966 && (Bracket::kSlashSlash == testIter->fBracket
967 || Bracket::kSlashStar == testIter->fBracket);
968 if (!isComment) {
969 break;
970 }
971 commentIter = testIter;
972 }
973 while (commentIter != wordIter) {
974 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
975 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
976 return false;
977 }
978 commentIter = std::next(commentIter);
979 }
980 return true;
981}
982
Cary Clarkbad5ad72017-08-03 17:14:08 -0400983bool IncludeParser::internalName(const Definition& token) const {
984 return 0 == token.fName.find("internal_")
985 || 0 == token.fName.find("Internal_")
986 || 0 == token.fName.find("legacy_")
987 || 0 == token.fName.find("temporary_")
988 || 0 == token.fName.find("private_");
989}
990
Cary Clark8032b982017-07-28 11:04:54 -0400991// caller calls reportError, so just return false here
992bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
993 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -0400994 // parse class header
995 auto iter = includeDef->fTokens.begin();
996 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
997 // todo : documentation is ignoring this for now
998 iter = std::next(iter);
999 }
1000 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1001 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001002 iter = std::next(iter);
1003 if (iter == includeDef->fTokens.end()) {
1004 return true; // forward declaration only
1005 }
Cary Clark8032b982017-07-28 11:04:54 -04001006 do {
1007 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001008 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001009 }
1010 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1011 break;
1012 }
1013 } while (static_cast<void>(iter = std::next(iter)), true);
1014 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001015 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001016 }
1017 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1018 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001019 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001020 }
1021 markupDef->fStart = iter->fStart;
1022 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001023 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001024 }
1025// if (1 != includeDef->fChildren.size()) {
1026// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1027// }
1028 includeDef = includeDef->fChildren.front();
1029 iter = includeDef->fTokens.begin();
1030 // skip until public
1031 int publicIndex = 0;
1032 if (IsStruct::kNo == isStruct) {
1033 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1034 size_t publicLen = strlen(publicName);
1035 while (iter != includeDef->fTokens.end()
1036 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1037 || strncmp(iter->fStart, publicName, publicLen))) {
1038 iter = std::next(iter);
1039 ++publicIndex;
1040 }
1041 }
1042 auto childIter = includeDef->fChildren.begin();
1043 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1044 (*childIter)->fPrivate = true;
1045 childIter = std::next(childIter);
1046 }
1047 int lastPublic = publicIndex;
1048 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1049 size_t protectedLen = strlen(protectedName);
1050 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1051 size_t privateLen = strlen(privateName);
1052 while (iter != includeDef->fTokens.end()
1053 && (protectedLen != (size_t) (iter->fContentEnd - iter->fStart)
1054 || strncmp(iter->fStart, protectedName, protectedLen))
1055 && (privateLen != (size_t) (iter->fContentEnd - iter->fStart)
1056 || strncmp(iter->fStart, privateName, privateLen))) {
1057 iter = std::next(iter);
1058 ++lastPublic;
1059 }
Cary Clark73fa9722017-08-29 17:36:51 -04001060 fLastObject = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001061 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < lastPublic) {
1062 Definition* child = *childIter;
1063 if (!this->parseObject(child, markupDef)) {
1064 return false;
1065 }
Cary Clark73fa9722017-08-29 17:36:51 -04001066 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001067 childIter = std::next(childIter);
1068 }
1069 while (childIter != includeDef->fChildren.end()) {
1070 (*childIter)->fPrivate = true;
1071 childIter = std::next(childIter);
1072 }
1073 SkASSERT(fParent->fParent);
1074 fParent = fParent->fParent;
1075 return true;
1076}
1077
1078bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1079 int lineCount, Definition* markupDef) {
1080 TextParser parser(filename, start, end, lineCount);
1081 // parse doxygen if present
1082 if (parser.startsWith("**")) {
1083 parser.next();
1084 parser.next();
1085 parser.skipWhiteSpace();
1086 if ('\\' == parser.peek()) {
1087 parser.next();
1088 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1089 return reportError<bool>("missing object type");
1090 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001091 if (!parser.skipWord(markupDef->fName.c_str()) &&
1092 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001093 return reportError<bool>("missing object name");
1094 }
1095
1096 }
1097 }
1098 // remove leading '*' if present
1099 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1100 while (!parser.eof() && parser.skipWhiteSpace()) {
1101 while ('*' == parser.peek()) {
1102 parser.next();
1103 if (parser.eof()) {
1104 break;
1105 }
1106 parser.skipWhiteSpace();
1107 }
1108 if (parser.eof()) {
1109 break;
1110 }
1111 const char* lineEnd = parser.trimmedLineEnd();
1112 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
1113 parser.fLineCount, parent);
1114 parser.skipToEndBracket('\n');
1115 }
1116 return true;
1117}
1118
1119bool IncludeParser::parseDefine() {
1120
1121 return true;
1122}
1123
1124bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
1125 string nameStr;
1126 if (child->fTokens.size() > 0) {
1127 auto token = child->fTokens.begin();
1128 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1129 token = token->fTokens.begin();
1130 }
1131 if (Definition::Type::kWord == token->fType) {
1132 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1133 }
1134 }
1135 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1136 child->fLineCount, markupDef);
1137 Definition* markupChild = &markupDef->fTokens.back();
1138 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1139 markupChild->fKeyWord = KeyWord::kEnum;
1140 TextParser enumName(child);
1141 enumName.skipExact("enum ");
Cary Clarkbad5ad72017-08-03 17:14:08 -04001142 if (enumName.skipExact("class ")) {
1143 markupChild->fMarkType = MarkType::kEnumClass;
1144 }
Cary Clark8032b982017-07-28 11:04:54 -04001145 const char* nameStart = enumName.fChar;
1146 enumName.skipToSpace();
1147 markupChild->fName = markupDef->fName + "::" +
1148 string(nameStart, (size_t) (enumName.fChar - nameStart));
1149 if (!this->findComments(*child, markupChild)) {
1150 return false;
1151 }
1152 TextParser parser(child);
1153 parser.skipToEndBracket('{');
Cary Clark9174bda2017-09-19 17:39:32 -04001154 parser.next();
Cary Clark8032b982017-07-28 11:04:54 -04001155 const char* dataEnd;
1156 do {
Cary Clark8032b982017-07-28 11:04:54 -04001157 parser.skipWhiteSpace();
1158 if ('}' == parser.peek()) {
1159 break;
1160 }
1161 Definition* comment = nullptr;
1162 // note that comment, if any, can be before or after (on the same line, though) as member
1163 if ('#' == parser.peek()) {
1164 // fixme: handle preprecessor, but just skip it for now
1165 parser.skipToLineStart();
1166 }
1167 while (parser.startsWith("/*") || parser.startsWith("//")) {
1168 parser.next();
1169 const char* start = parser.fChar;
1170 const char* end;
1171 if ('*' == parser.peek()) {
1172 end = parser.strnstr("*/", parser.fEnd);
1173 parser.fChar = end;
1174 parser.next();
1175 parser.next();
1176 } else {
1177 end = parser.trimmedLineEnd();
1178 parser.skipToLineStart();
1179 }
1180 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1181 markupChild);
1182 comment = &markupChild->fTokens.back();
1183 comment->fTerminator = end;
1184 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1185 return false;
1186 }
1187 parser.skipWhiteSpace();
1188 }
1189 parser.skipWhiteSpace();
1190 const char* memberStart = parser.fChar;
1191 if ('}' == memberStart[0]) {
1192 break;
1193 }
Cary Clark9174bda2017-09-19 17:39:32 -04001194 // if there's comment on same the line as member def, output first as if it was before
1195
Cary Clark8032b982017-07-28 11:04:54 -04001196 parser.skipToNonAlphaNum();
1197 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001198 if (parser.eof() || !parser.skipWhiteSpace()) {
1199 return this->reportError<bool>("enum member must end with comma 1");
1200 }
Cary Clark8032b982017-07-28 11:04:54 -04001201 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001202 if ('=' == parser.peek()) {
1203 parser.skipToEndBracket(',');
1204 }
1205 if (parser.eof() || ',' != parser.peek()) {
1206 return this->reportError<bool>("enum member must end with comma 2");
1207 }
1208 dataEnd = parser.fChar;
1209 const char* start = parser.anyOf("/\n");
1210 SkASSERT(start);
1211 parser.skipTo(start);
1212 if ('/' == parser.next()) {
1213 char slashStar = parser.next();
1214 if ('/' == slashStar || '*' == slashStar) {
1215 TextParser::Save save(&parser);
1216 char doxCheck = parser.next();
1217 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1218 save.restore();
1219 }
1220 }
1221 parser.skipWhiteSpace();
1222 const char* commentStart = parser.fChar;
1223 if ('/' == slashStar) {
1224 parser.skipToEndBracket('\n');
1225 } else {
1226 parser.skipToEndBracket("*/");
1227 }
1228 SkASSERT(!parser.eof());
1229 const char* commentEnd = parser.fChar;
1230 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
1231 parser.fLineCount, markupChild);
1232 comment = &markupChild->fTokens.back();
1233 comment->fTerminator = commentEnd;
1234 }
Cary Clark8032b982017-07-28 11:04:54 -04001235 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1236 markupChild);
1237 Definition* member = &markupChild->fTokens.back();
1238 member->fName = memberName;
1239 if (comment) {
1240 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001241 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001242 }
1243 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001244 } while (true);
Cary Clark8032b982017-07-28 11:04:54 -04001245 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1246 SkASSERT(classDef.fStart);
1247 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1248 markupChild->fName = uniqueName;
1249 classDef.fEnums[uniqueName] = markupChild;
1250 return true;
1251}
1252
1253bool IncludeParser::parseInclude(const string& name) {
1254 fParent = &fIncludeMap[name];
1255 fParent->fName = name;
1256 fParent->fFileName = fFileName;
1257 fParent->fType = Definition::Type::kFileType;
1258 fParent->fContentStart = fChar;
1259 fParent->fContentEnd = fEnd;
1260 // parse include file into tree
1261 while (fChar < fEnd) {
1262 if (!this->parseChar()) {
1263 return false;
1264 }
1265 }
1266 // parse tree and add named objects to maps
1267 fParent = &fIncludeMap[name];
1268 if (!this->parseObjects(fParent, nullptr)) {
1269 return false;
1270 }
1271 return true;
1272}
1273
1274bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1275 const char* typeStart = child->fChildren[0]->fContentStart;
1276 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1277 child->fLineCount, markupDef);
1278 Definition* markupChild = &markupDef->fTokens.back();
1279 TextParser nameParser(child);
1280 nameParser.skipToNonAlphaNum();
1281 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1282 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1283 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1284 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001285 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001286 classDef.fMembers[uniqueName] = markupChild;
1287 if (child->fParentIndex >= 2) {
1288 auto comment = child->fParent->fTokens.begin();
1289 std::advance(comment, child->fParentIndex - 2);
1290 if (Definition::Type::kBracket == comment->fType
1291 && (Bracket::kSlashStar == comment->fBracket
1292 || Bracket::kSlashSlash == comment->fBracket)) {
1293 TextParser parser(&*comment);
1294 do {
1295 parser.skipToAlpha();
1296 if (parser.eof()) {
1297 break;
1298 }
1299 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001300 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001301 if (Bracket::kSlashStar == comment->fBracket) {
1302 const char* commentEnd = parser.strnstr("*/", end);
1303 if (commentEnd) {
1304 end = commentEnd;
1305 }
1306 }
1307 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1308 markupDef);
1309 Definition* commentChild = &markupDef->fTokens.back();
1310 markupChild->fChildren.emplace_back(commentChild);
1311 parser.skipTo(end);
1312 } while (!parser.eof());
1313 }
1314 }
1315 return true;
1316}
1317
1318bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1319 auto tokenIter = child->fParent->fTokens.begin();
1320 std::advance(tokenIter, child->fParentIndex);
1321 tokenIter = std::prev(tokenIter);
1322 string nameStr(tokenIter->fStart, tokenIter->fContentEnd - tokenIter->fStart);
Cary Clark9174bda2017-09-19 17:39:32 -04001323 if (0 == nameStr.find("SK_ATTR_DEPRECATED")) {
1324 SkDebugf("");
1325 }
Cary Clark8032b982017-07-28 11:04:54 -04001326 while (tokenIter != child->fParent->fTokens.begin()) {
1327 auto testIter = std::prev(tokenIter);
1328 switch (testIter->fType) {
1329 case Definition::Type::kWord:
1330 goto keepGoing;
1331 case Definition::Type::kKeyWord: {
1332 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1333 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1334 goto keepGoing;
1335 }
1336 } break;
1337 case Definition::Type::kBracket:
1338 if (Bracket::kAngle == testIter->fBracket) {
1339 goto keepGoing;
1340 }
1341 break;
1342 case Definition::Type::kPunctuation:
1343 if (Punctuation::kSemicolon == testIter->fPunctuation
1344 || Punctuation::kLeftBrace == testIter->fPunctuation
1345 || Punctuation::kColon == testIter->fPunctuation) {
1346 break;
1347 }
1348 keepGoing:
1349 tokenIter = testIter;
1350 continue;
1351 default:
1352 break;
1353 }
1354 break;
1355 }
1356 tokenIter->fName = nameStr;
1357 tokenIter->fMarkType = MarkType::kMethod;
1358 auto testIter = child->fParent->fTokens.begin();
1359 SkASSERT(child->fParentIndex > 0);
1360 std::advance(testIter, child->fParentIndex - 1);
1361 const char* start = tokenIter->fContentStart;
1362 const char* end = tokenIter->fContentEnd;
1363 const char kDebugCodeStr[] = "SkDEBUGCODE";
1364 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1365 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1366 std::advance(testIter, 1);
1367 start = testIter->fContentStart + 1;
1368 end = testIter->fContentEnd - 1;
1369 } else {
1370 end = testIter->fContentEnd;
1371 while (testIter != child->fParent->fTokens.end()) {
1372 testIter = std::next(testIter);
1373 switch (testIter->fType) {
1374 case Definition::Type::kPunctuation:
1375 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1376 || Punctuation::kLeftBrace == testIter->fPunctuation
1377 || Punctuation::kColon == testIter->fPunctuation);
1378 end = testIter->fStart;
1379 break;
1380 case Definition::Type::kKeyWord: {
1381 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1382 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1383 continue;
1384 }
1385 } break;
1386 default:
1387 continue;
1388 }
1389 break;
1390 }
1391 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001392 while (end > start && ' ' >= end[-1]) {
1393 --end;
1394 }
Cary Clark9174bda2017-09-19 17:39:32 -04001395 if (!markupDef) {
1396 auto parentIter = child->fParent->fTokens.begin();
1397 SkASSERT(child->fParentIndex > 0);
1398 std::advance(parentIter, child->fParentIndex - 1);
1399 Definition* methodName = &*parentIter;
1400 TextParser name(methodName);
1401 if (name.skipToEndBracket(':') && name.startsWith("::")) {
1402 return true; // expect this is inline class definition outside of class
1403 }
1404 SkASSERT(0); // code incomplete
1405 }
Cary Clark8032b982017-07-28 11:04:54 -04001406 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1407 markupDef);
1408 Definition* markupChild = &markupDef->fTokens.back();
1409 // do find instead -- I wonder if there is a way to prevent this in c++
1410 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1411 SkASSERT(classDef.fStart);
1412 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1413 markupChild->fName = uniqueName;
1414 if (!this->findComments(*child, markupChild)) {
1415 return false;
1416 }
1417 classDef.fMethods[uniqueName] = markupChild;
1418 return true;
1419}
1420
Cary Clark8032b982017-07-28 11:04:54 -04001421bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1422 for (auto& child : parent->fChildren) {
1423 if (!this->parseObject(child, markupDef)) {
1424 return false;
1425 }
1426 }
1427 return true;
1428}
1429
1430bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1431 // set up for error reporting
1432 fLine = fChar = child->fStart;
1433 fEnd = child->fContentEnd;
1434 // todo: put original line number in child as well
1435 switch (child->fType) {
1436 case Definition::Type::kKeyWord:
1437 switch (child->fKeyWord) {
1438 case KeyWord::kClass:
1439 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001440 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001441 }
1442 break;
1443 case KeyWord::kEnum:
1444 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001445 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001446 }
1447 break;
1448 case KeyWord::kStruct:
1449 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001450 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001451 }
1452 break;
1453 case KeyWord::kTemplate:
1454 if (!this->parseTemplate()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001455 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001456 }
1457 break;
1458 case KeyWord::kTypedef:
1459 if (!this->parseTypedef()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001460 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001461 }
1462 break;
1463 case KeyWord::kUnion:
1464 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001465 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001466 }
1467 break;
1468 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001469 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001470 }
1471 break;
1472 case Definition::Type::kBracket:
1473 switch (child->fBracket) {
1474 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001475 if (fLastObject) {
1476 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1477 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001478 if (!checkDeprecated.eof()) {
1479 checkDeprecated.skipWhiteSpace();
1480 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1481 break;
1482 }
1483 }
1484 }
1485 {
1486 auto tokenIter = child->fParent->fTokens.begin();
1487 std::advance(tokenIter, child->fParentIndex);
1488 tokenIter = std::prev(tokenIter);
1489 TextParser checkDeprecated(&*tokenIter);
Cary Clark73fa9722017-08-29 17:36:51 -04001490 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1491 break;
1492 }
1493 }
Cary Clark8032b982017-07-28 11:04:54 -04001494 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001495 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001496 }
Cary Clark73fa9722017-08-29 17:36:51 -04001497 break;
Cary Clark8032b982017-07-28 11:04:54 -04001498 case Bracket::kSlashSlash:
1499 case Bracket::kSlashStar:
1500 // comments are picked up by parsing objects first
1501 break;
1502 case Bracket::kPound:
1503 // special-case the #xxx xxx_DEFINED entries
1504 switch (child->fKeyWord) {
1505 case KeyWord::kIfndef:
1506 case KeyWord::kIfdef:
1507 if (child->boilerplateIfDef(fParent)) {
1508 if (!this->parseObjects(child, markupDef)) {
1509 return false;
1510 }
1511 break;
1512 }
1513 goto preproError;
1514 case KeyWord::kDefine:
1515 if (child->boilerplateDef(fParent)) {
1516 break;
1517 }
1518 goto preproError;
1519 case KeyWord::kEndif:
1520 if (child->boilerplateEndIf()) {
1521 break;
1522 }
1523 case KeyWord::kInclude:
1524 // ignored for now
1525 break;
1526 case KeyWord::kElse:
1527 case KeyWord::kElif:
1528 // todo: handle these
1529 break;
1530 default:
1531 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001532 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001533 }
1534 break;
1535 case Bracket::kAngle:
1536 // pick up templated function pieces when method is found
1537 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001538 case Bracket::kDebugCode:
1539 // todo: handle this
1540 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001541 case Bracket::kSquare: {
1542 // check to see if parent is operator, the only case we handle so far
1543 auto prev = child->fParent->fTokens.begin();
1544 std::advance(prev, child->fParentIndex - 1);
1545 if (KeyWord::kOperator != prev->fKeyWord) {
1546 return child->reportError<bool>("expected operator overload");
1547 }
1548 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001549 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001550 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001551 }
1552 break;
1553 case Definition::Type::kWord:
1554 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001555 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001556 }
1557 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001558 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001559 }
1560 break;
1561 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001562 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001563 break;
1564 }
1565 return true;
1566}
1567
1568bool IncludeParser::parseTemplate() {
1569
1570 return true;
1571}
1572
1573bool IncludeParser::parseTypedef() {
1574
1575 return true;
1576}
1577
1578bool IncludeParser::parseUnion() {
1579
1580 return true;
1581}
1582
1583bool IncludeParser::parseChar() {
1584 char test = *fChar;
1585 if ('\\' == fPrev) {
1586 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001587// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001588 fLine = fChar + 1;
1589 }
1590 goto done;
1591 }
1592 switch (test) {
1593 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001594// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001595 fLine = fChar + 1;
1596 if (fInChar) {
1597 return reportError<bool>("malformed char");
1598 }
1599 if (fInString) {
1600 return reportError<bool>("malformed string");
1601 }
1602 if (!this->checkForWord()) {
1603 return false;
1604 }
1605 if (Bracket::kPound == this->topBracket()) {
1606 KeyWord keyWord = fParent->fKeyWord;
1607 if (KeyWord::kNone == keyWord) {
1608 return this->reportError<bool>("unhandled preprocessor directive");
1609 }
1610 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) {
1611 this->popBracket();
1612 }
1613 } else if (Bracket::kSlashSlash == this->topBracket()) {
1614 this->popBracket();
1615 }
1616 break;
1617 case '*':
1618 if (!fInCharCommentString && '/' == fPrev) {
1619 this->pushBracket(Bracket::kSlashStar);
1620 }
1621 if (!this->checkForWord()) {
1622 return false;
1623 }
1624 if (!fInCharCommentString) {
1625 this->addPunctuation(Punctuation::kAsterisk);
1626 }
1627 break;
1628 case '/':
1629 if ('*' == fPrev) {
1630 if (!fInCharCommentString) {
1631 return reportError<bool>("malformed closing comment");
1632 }
1633 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarkce101242017-09-01 15:51:02 -04001634 this->next(); // include close in bracket -- FIXME? will this skip stuff?
Cary Clark8032b982017-07-28 11:04:54 -04001635 this->popBracket();
1636 }
1637 break;
1638 }
1639 if (!fInCharCommentString && '/' == fPrev) {
1640 this->pushBracket(Bracket::kSlashSlash);
1641 break;
1642 }
1643 if (!this->checkForWord()) {
1644 return false;
1645 }
1646 break;
1647 case '\'':
1648 if (Bracket::kChar == this->topBracket()) {
1649 this->popBracket();
1650 } else if (!fInComment && !fInString) {
1651 if (fIncludeWord) {
1652 return this->reportError<bool>("word then single-quote");
1653 }
1654 this->pushBracket(Bracket::kChar);
1655 }
1656 break;
1657 case '\"':
1658 if (Bracket::kString == this->topBracket()) {
1659 this->popBracket();
1660 } else if (!fInComment && !fInChar) {
1661 if (fIncludeWord) {
1662 return this->reportError<bool>("word then double-quote");
1663 }
1664 this->pushBracket(Bracket::kString);
1665 }
1666 break;
1667 case ':':
1668 case '(':
1669 case '[':
1670 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001671 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1672 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1673 this->pushBracket(Bracket::kDebugCode);
1674 break;
1675 }
Cary Clark8032b982017-07-28 11:04:54 -04001676 if (fInCharCommentString) {
1677 break;
1678 }
1679 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1680 break;
1681 }
1682 if (!fInBrace) {
1683 if (!this->checkForWord()) {
1684 return false;
1685 }
1686 if (':' == test && !fInFunction) {
1687 break;
1688 }
1689 if ('{' == test) {
1690 this->addPunctuation(Punctuation::kLeftBrace);
1691 } else if (':' == test) {
1692 this->addPunctuation(Punctuation::kColon);
1693 }
1694 }
1695 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1696 && Bracket::kColon == fInBrace->fBracket) {
1697 Definition* braceParent = fParent->fParent;
1698 braceParent->fChildren.pop_back();
1699 braceParent->fTokens.pop_back();
1700 fParent = braceParent;
1701 fInBrace = nullptr;
1702 }
1703 this->pushBracket(
1704 '(' == test ? Bracket::kParen :
1705 '[' == test ? Bracket::kSquare :
1706 '{' == test ? Bracket::kBrace :
1707 Bracket::kColon);
1708 if (!fInBrace
1709 && ('{' == test || (':' == test && ' ' >= fChar[1]))
1710 && fInFunction) {
1711 fInBrace = fParent;
1712 }
1713 } break;
1714 case '<':
1715 if (fInCharCommentString || fInBrace) {
1716 break;
1717 }
1718 if (!this->checkForWord()) {
1719 return false;
1720 }
1721 if (fInEnum) {
1722 break;
1723 }
1724 this->pushBracket(Bracket::kAngle);
1725 break;
1726 case ')':
1727 case ']':
1728 case '}': {
1729 if (fInCharCommentString) {
1730 break;
1731 }
1732 if (!fInBrace) {
1733 if (!this->checkForWord()) {
1734 return false;
1735 }
1736 }
1737 bool popBraceParent = fInBrace == fParent;
1738 if ((')' == test ? Bracket::kParen :
1739 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
1740 this->popBracket();
1741 if (!fInFunction) {
1742 bool deprecatedMacro = false;
1743 if (')' == test) {
1744 auto iter = fParent->fTokens.end();
1745 bool lookForWord = false;
1746 while (fParent->fTokens.begin() != iter) {
1747 --iter;
1748 if (lookForWord) {
1749 if (Definition::Type::kWord != iter->fType) {
1750 break;
1751 }
1752 string word(iter->fContentStart, iter->length());
1753 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
1754 deprecatedMacro = true;
1755 // remove macro paren (would confuse method parsing later)
1756 fParent->fTokens.pop_back();
1757 fParent->fChildren.pop_back();
1758 }
1759 break;
1760 }
1761 if (Definition::Type::kBracket != iter->fType) {
1762 break;
1763 }
1764 if (Bracket::kParen != iter->fBracket) {
1765 break;
1766 }
1767 lookForWord = true;
1768 }
1769 }
1770 fInFunction = ')' == test && !deprecatedMacro;
1771 } else {
1772 fInFunction = '}' != test;
1773 }
Cary Clark73fa9722017-08-29 17:36:51 -04001774 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
1775 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04001776 } else {
1777 return reportError<bool>("malformed close bracket");
1778 }
1779 if (popBraceParent) {
1780 Definition* braceParent = fInBrace->fParent;
1781 braceParent->fChildren.pop_back();
1782 braceParent->fTokens.pop_back();
1783 fInBrace = nullptr;
1784 }
1785 } break;
1786 case '>':
1787 if (fInCharCommentString || fInBrace) {
1788 break;
1789 }
1790 if (!this->checkForWord()) {
1791 return false;
1792 }
1793 if (fInEnum) {
1794 break;
1795 }
1796 if (Bracket::kAngle == this->topBracket()) {
1797 this->popBracket();
1798 } else {
1799 return reportError<bool>("malformed close angle bracket");
1800 }
1801 break;
1802 case '#': {
1803 if (fInCharCommentString || fInBrace) {
1804 break;
1805 }
1806 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
1807 this->pushBracket(Bracket::kPound);
1808 break;
1809 }
1810 case '&':
1811 case ',':
1812 case ' ':
1813 if (fInCharCommentString || fInBrace) {
1814 break;
1815 }
1816 if (!this->checkForWord()) {
1817 return false;
1818 }
1819 break;
1820 case ';':
1821 if (fInCharCommentString || fInBrace) {
1822 break;
1823 }
1824 if (!this->checkForWord()) {
1825 return false;
1826 }
1827 if (Definition::Type::kKeyWord == fParent->fType
1828 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001829 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
1830 KeyWord::kEnum == fParent->fParent->fKeyWord) {
1831 this->popObject();
1832 }
Cary Clark8032b982017-07-28 11:04:54 -04001833 if (KeyWord::kEnum == fParent->fKeyWord) {
1834 fInEnum = false;
1835 }
1836 this->popObject();
1837 } else if (Definition::Type::kBracket == fParent->fType
1838 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
1839 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
1840 list<Definition>::iterator baseIter = fParent->fTokens.end();
1841 list<Definition>::iterator namedIter = fParent->fTokens.end();
1842 for (auto tokenIter = fParent->fTokens.end();
1843 fParent->fTokens.begin() != tokenIter--; ) {
1844 if (tokenIter->fLineCount == fLineCount) {
1845 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
1846 if (namedIter != fParent->fTokens.end()) {
1847 return reportError<bool>("found two named member tokens");
1848 }
1849 namedIter = tokenIter;
1850 }
1851 baseIter = tokenIter;
1852 } else {
1853 break;
1854 }
1855 }
1856 // FIXME: if a member definition spans multiple lines, this won't work
1857 if (namedIter != fParent->fTokens.end()) {
1858 if (baseIter == namedIter) {
1859 return this->reportError<bool>("expected type before named token");
1860 }
1861 Definition* member = &*namedIter;
1862 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04001863 if (!member->fTerminator) {
1864 member->fTerminator = member->fContentEnd;
1865 }
Cary Clark8032b982017-07-28 11:04:54 -04001866 fParent->fChildren.push_back(member);
1867 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
1868 member->fChildren.push_back(&*nameType);
1869 }
1870
1871 }
1872 } else if (fParent->fChildren.size() > 0) {
1873 auto lastIter = fParent->fChildren.end();
1874 Definition* priorEnum;
1875 while (fParent->fChildren.begin() != lastIter) {
1876 std::advance(lastIter, -1);
1877 priorEnum = *lastIter;
1878 if (Definition::Type::kBracket != priorEnum->fType ||
1879 (Bracket::kSlashSlash != priorEnum->fBracket
1880 && Bracket::kSlashStar != priorEnum->fBracket)) {
1881 break;
1882 }
1883 }
1884 if (Definition::Type::kKeyWord == priorEnum->fType
1885 && KeyWord::kEnum == priorEnum->fKeyWord) {
1886 auto tokenWalker = fParent->fTokens.begin();
1887 std::advance(tokenWalker, priorEnum->fParentIndex);
1888 SkASSERT(KeyWord::kEnum == tokenWalker->fKeyWord);
1889 while (tokenWalker != fParent->fTokens.end()) {
1890 std::advance(tokenWalker, 1);
1891 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
1892 break;
1893 }
1894 }
1895 while (tokenWalker != fParent->fTokens.end()) {
1896 std::advance(tokenWalker, 1);
1897 const Definition* test = &*tokenWalker;
1898 if (Definition::Type::kBracket != test->fType ||
1899 (Bracket::kSlashSlash != test->fBracket
1900 && Bracket::kSlashStar != test->fBracket)) {
1901 break;
1902 }
1903 }
1904 Definition* start = &*tokenWalker;
1905 bool foundExpected = true;
1906 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
1907 const Definition* test = &*tokenWalker;
1908 if (expected != test->fKeyWord) {
1909 foundExpected = false;
1910 break;
1911 }
1912 if (tokenWalker == fParent->fTokens.end()) {
1913 break;
1914 }
1915 std::advance(tokenWalker, 1);
1916 }
1917 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
1918 const char* nameStart = tokenWalker->fStart;
1919 std::advance(tokenWalker, 1);
1920 if (tokenWalker != fParent->fTokens.end()) {
1921 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
1922 tp.skipToNonAlphaNum();
1923 start->fName = string(nameStart, tp.fChar - nameStart);
1924 start->fContentEnd = fChar;
1925 priorEnum->fChildren.emplace_back(start);
1926 }
1927 }
1928 }
1929 }
1930 this->addPunctuation(Punctuation::kSemicolon);
1931 fInFunction = false;
1932 break;
1933 case '~':
1934 if (fInEnum) {
1935 break;
1936 }
1937 case '0': case '1': case '2': case '3': case '4':
1938 case '5': case '6': case '7': case '8': case '9':
1939 // TODO: don't want to parse numbers, but do need to track for enum defs
1940 // break;
1941 case 'A': case 'B': case 'C': case 'D': case 'E':
1942 case 'F': case 'G': case 'H': case 'I': case 'J':
1943 case 'K': case 'L': case 'M': case 'N': case 'O':
1944 case 'P': case 'Q': case 'R': case 'S': case 'T':
1945 case 'U': case 'V': case 'W': case 'X': case 'Y':
1946 case 'Z': case '_':
1947 case 'a': case 'b': case 'c': case 'd': case 'e':
1948 case 'f': case 'g': case 'h': case 'i': case 'j':
1949 case 'k': case 'l': case 'm': case 'n': case 'o':
1950 case 'p': case 'q': case 'r': case 's': case 't':
1951 case 'u': case 'v': case 'w': case 'x': case 'y':
1952 case 'z':
1953 if (fInCharCommentString || fInBrace) {
1954 break;
1955 }
1956 if (!fIncludeWord) {
1957 fIncludeWord = fChar;
1958 }
1959 break;
1960 }
1961done:
1962 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001963 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04001964 return true;
1965}
1966
1967void IncludeParser::validate() const {
1968 for (int index = 0; index <= (int) Last_MarkType; ++index) {
1969 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
1970 }
1971 IncludeParser::ValidateKeyWords();
1972}