blob: cd6e9e3914abb2e1770de9d8c4340450b2196cc7 [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;
Cary Clark2f466242017-12-11 16:03:17 -0500429 case MarkType::kTypedef:
430 if (def) {
431 def->fVisited = true;
432 } else {
433 SkDebugf("typedef missing from bmh: %s\n", fullName.c_str());
434 }
435 break;
Cary Clark8032b982017-07-28 11:04:54 -0400436 default:
Ben Wagner63fd7602017-10-09 15:45:33 -0400437 SkASSERT(0); // unhandled
Cary Clark8032b982017-07-28 11:04:54 -0400438 break;
439 }
440 }
441 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400442 for (auto& classMapper : fIClassMap) {
443 string className = classMapper.first;
444 auto finder = bmhParser.fClassMap.find(className);
445 if (bmhParser.fClassMap.end() == finder) {
446 continue;
447 }
448 RootDefinition* root = &finder->second;
Cary Clarka560c472017-11-27 10:44:06 -0500449 if (!root->dumpUnVisited(bmhParser.fSkip)) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400450 SkDebugf("some struct elements not found; struct finding in includeParser is missing\n");
451 }
452 SkDebugf("cross-checked %s\n", className.c_str());
Cary Clark8032b982017-07-28 11:04:54 -0400453 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400454 bmhParser.fWroteOut = true;
Cary Clark8032b982017-07-28 11:04:54 -0400455 return true;
456}
457
458IClassDefinition* IncludeParser::defineClass(const Definition& includeDef,
459 const string& name) {
460 string className;
461 const Definition* test = fParent;
462 while (Definition::Type::kFileType != test->fType) {
463 if (Definition::Type::kMark == test->fType && KeyWord::kClass == test->fKeyWord) {
464 className = test->fName + "::";
465 break;
466 }
467 test = test->fParent;
468 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400469 className += name;
Cary Clark8032b982017-07-28 11:04:54 -0400470 unordered_map<string, IClassDefinition>& map = fIClassMap;
471 IClassDefinition& markupDef = map[className];
472 if (markupDef.fStart) {
473 typedef IClassDefinition* IClassDefPtr;
474 return INHERITED::reportError<IClassDefPtr>("class already defined");
475 }
476 markupDef.fFileName = fFileName;
477 markupDef.fStart = includeDef.fStart;
478 markupDef.fContentStart = includeDef.fStart;
479 markupDef.fName = className;
480 markupDef.fContentEnd = includeDef.fContentEnd;
481 markupDef.fTerminator = includeDef.fTerminator;
482 markupDef.fParent = fParent;
483 markupDef.fLineCount = fLineCount;
484 markupDef.fMarkType = KeyWord::kStruct == includeDef.fKeyWord ?
485 MarkType::kStruct : MarkType::kClass;
486 markupDef.fKeyWord = includeDef.fKeyWord;
487 markupDef.fType = Definition::Type::kMark;
488 fParent = &markupDef;
489 return &markupDef;
490}
491
492void IncludeParser::dumpClassTokens(IClassDefinition& classDef) {
493 auto& tokens = classDef.fTokens;
494 for (auto& token : tokens) {
495 if (Definition::Type::kMark == token.fType && MarkType::kComment == token.fMarkType) {
496 continue;
497 }
498 if (MarkType::kMember != token.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400499 this->writeString(
500 "# ------------------------------------------------------------------------------");
501 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400502 }
503 switch (token.fMarkType) {
504 case MarkType::kEnum:
Cary Clarka560c472017-11-27 10:44:06 -0500505 case MarkType::kEnumClass:
Cary Clark9174bda2017-09-19 17:39:32 -0400506 this->dumpEnum(token);
Cary Clark8032b982017-07-28 11:04:54 -0400507 break;
508 case MarkType::kMethod:
Cary Clark9174bda2017-09-19 17:39:32 -0400509 this->dumpMethod(token);
Cary Clark8032b982017-07-28 11:04:54 -0400510 break;
511 case MarkType::kMember:
Cary Clark9174bda2017-09-19 17:39:32 -0400512 this->dumpMember(token);
Cary Clark8032b982017-07-28 11:04:54 -0400513 continue;
514 break;
515 default:
516 SkASSERT(0);
517 }
518 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400519 this->writeTag("Example");
Cary Clark154beea2017-10-26 07:58:48 -0400520 this->lf(1);
521 this->writeString("// incomplete");
522 this->lf(1);
Cary Clark9174bda2017-09-19 17:39:32 -0400523 this->writeEndTag();
524 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400525 this->writeTag("SeeAlso");
526 this->writeSpace();
527 this->writeString("incomplete");
Cary Clark9174bda2017-09-19 17:39:32 -0400528 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500529 switch (token.fMarkType) {
530 case MarkType::kEnum:
531 case MarkType::kEnumClass:
532 this->writeEndTag("Enum");
533 break;
534 case MarkType::kMethod:
535 this->writeEndTag("Method");
536 break;
537 case MarkType::kMember:
538 this->writeEndTag("Member");
539 continue;
540 break;
541 default:
542 SkASSERT(0);
543 }
Cary Clark9174bda2017-09-19 17:39:32 -0400544 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400545 }
546}
Cary Clark9174bda2017-09-19 17:39:32 -0400547void IncludeParser::dumpComment(const Definition& token) {
548 fLineCount = token.fLineCount;
549 fChar = fLine = token.fContentStart;
550 fEnd = token.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400551 bool sawParam = false;
552 bool multiline = false;
553 bool sawReturn = false;
554 bool sawComment = false;
555 bool methodHasReturn = false;
556 vector<string> methodParams;
557 vector<string> foundParams;
558 Definition methodName;
Cary Clark9174bda2017-09-19 17:39:32 -0400559 TextParser methodParser(token.fFileName, token.fContentStart, token.fContentEnd,
560 token.fLineCount);
561 if (MarkType::kMethod == token.fMarkType) {
562 methodName.fName = string(token.fContentStart,
563 (int) (token.fContentEnd - token.fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400564 methodHasReturn = !methodParser.startsWith("void ")
Cary Clarka560c472017-11-27 10:44:06 -0500565 && !methodParser.startsWith("static void ")
Cary Clark8032b982017-07-28 11:04:54 -0400566 && !methodParser.strnchr('~', methodParser.fEnd);
567 const char* paren = methodParser.strnchr('(', methodParser.fEnd);
568 const char* nextEnd = paren;
569 do {
570 string paramName;
571 methodParser.fChar = nextEnd + 1;
572 methodParser.skipSpace();
573 if (!methodName.nextMethodParam(&methodParser, &nextEnd, &paramName)) {
574 continue;
575 }
576 methodParams.push_back(paramName);
577 } while (')' != nextEnd[0]);
578 }
Cary Clark9174bda2017-09-19 17:39:32 -0400579 for (const auto& child : token.fTokens) {
580 if (Definition::Type::kMark == child.fType && MarkType::kMember == child.fMarkType) {
581 break;
582 }
Cary Clark8032b982017-07-28 11:04:54 -0400583 if (Definition::Type::kMark == child.fType && MarkType::kComment == child.fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -0400584 if (child.fPrivate) {
585 break;
586 }
Cary Clark8032b982017-07-28 11:04:54 -0400587 if ('@' == child.fContentStart[0]) {
588 TextParser parser(&child);
589 do {
590 parser.next();
591 if (parser.startsWith("param ")) {
592 parser.skipWord("param");
593 const char* parmStart = parser.fChar;
594 parser.skipToSpace();
595 string parmName = string(parmStart, (int) (parser.fChar - parmStart));
596 parser.skipWhiteSpace();
597 do {
598 size_t nextComma = parmName.find(',');
599 string piece;
600 if (string::npos == nextComma) {
601 piece = parmName;
602 parmName = "";
603 } else {
604 piece = parmName.substr(0, nextComma);
605 parmName = parmName.substr(nextComma + 1);
606 }
607 if (sawParam) {
608 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400609 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400610 }
Cary Clark9174bda2017-09-19 17:39:32 -0400611 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400612 } else {
613 if (sawComment) {
614 this->nl();
615 }
616 this->lf(2);
617 }
618 foundParams.emplace_back(piece);
Cary Clark9174bda2017-09-19 17:39:32 -0400619 this->writeTag("Param", piece);
620 this->writeSpace(2);
621 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
622 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400623 sawParam = true;
624 sawComment = false;
625 } while (parmName.length());
626 parser.skipTo(parser.fEnd);
627 } else if (parser.startsWith("return ") || parser.startsWith("returns ")) {
628 parser.skipWord("return");
629 if ('s' == parser.peek()) {
630 parser.next();
631 }
632 if (sawParam) {
633 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400634 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400635 }
Cary Clark9174bda2017-09-19 17:39:32 -0400636 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400637 }
638 this->checkForMissingParams(methodParams, foundParams);
639 sawParam = false;
640 sawComment = false;
641 multiline = false;
642 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -0400643 this->writeTag("Return");
644 this->writeSpace(2);
645 this->writeBlock(parser.fEnd - parser.fChar, parser.fChar);
646 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400647 sawReturn = true;
648 parser.skipTo(parser.fEnd);
649 } else {
650 this->reportError("unexpected doxygen directive");
651 }
652 } while (!parser.eof());
Cary Clark9174bda2017-09-19 17:39:32 -0400653 } else if (child.length() > 1) {
654 const char* start = child.fContentStart;
655 ptrdiff_t length = child.fContentEnd - start;
656 SkASSERT(length >= 0);
657 while (length && '/' == start[0]) {
658 start += 1;
659 --length;
Cary Clark8032b982017-07-28 11:04:54 -0400660 }
Cary Clark9174bda2017-09-19 17:39:32 -0400661 while (length && '/' == start[length - 1]) {
662 length -= 1;
663 if (length && '*' == start[length - 1]) {
664 length -= 1;
665 }
666 }
667 if (length) {
668 this->lfAlways(sawComment || sawParam || sawReturn ? 1 : 2);
669 if (sawParam || sawReturn) {
670 this->indentToColumn(8);
671 }
672 this->writeBlock(length, start);
673 this->writeSpace();
674 sawComment = true;
675 if (sawParam || sawReturn) {
676 multiline = true;
677 }
Cary Clark8032b982017-07-28 11:04:54 -0400678 }
679 }
680 }
681 }
682 if (sawParam || sawReturn) {
683 if (multiline) {
Cary Clark9174bda2017-09-19 17:39:32 -0400684 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400685 }
Cary Clark9174bda2017-09-19 17:39:32 -0400686 this->writeEndTag();
Cary Clark8032b982017-07-28 11:04:54 -0400687 }
688 if (!sawReturn) {
689 if (!sawParam) {
690 if (sawComment) {
691 this->nl();
692 }
693 this->lf(2);
694 }
695 this->checkForMissingParams(methodParams, foundParams);
696 }
697 if (methodHasReturn != sawReturn) {
698 if (!methodHasReturn) {
699 this->reportError("unexpected doxygen return");
700 } else {
701 if (sawComment) {
702 this->nl();
703 }
704 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400705 this->writeIncompleteTag("Return");
Cary Clark8032b982017-07-28 11:04:54 -0400706 }
707 }
708}
709
Cary Clark9174bda2017-09-19 17:39:32 -0400710void IncludeParser::dumpEnum(const Definition& token) {
711 this->writeTag("Enum", token.fName);
712 this->lf(2);
713 this->writeString("#Code");
714 this->lfAlways(1);
715 this->indentToColumn(4);
716 this->writeString("enum");
717 this->writeSpace();
718 if ("_anonymous" != token.fName.substr(0, 10)) {
719 this->writeString(token.fName);
720 this->writeSpace();
721 }
722 this->writeString("{");
723 this->lfAlways(1);
724 for (auto& child : token.fChildren) {
725 this->indentToColumn(8);
726 this->writeString(child->fName);
727 if (child->length()) {
728 this->writeSpace();
729 this->writeBlock(child->length(), child->fContentStart);
730 }
731 if (',' != fLastChar) {
732 this->writeString(",");
733 }
734 this->lfAlways(1);
735 }
736 this->indentToColumn(4);
737 this->writeString("};");
738 this->lf(1);
739 this->writeString("##");
740 this->lf(2);
741 this->dumpComment(token);
742 for (auto& child : token.fChildren) {
743 // start here;
744 // get comments before
745 // or after const values
746 this->writeString("#Const");
747 this->writeSpace();
748 this->writeString(child->fName);
749 TextParser val(child);
750 if (!val.eof()) {
751 if ('=' == val.fStart[0] || ',' == val.fStart[0]) {
752 val.next();
753 val.skipSpace();
754 const char* valEnd = val.anyOf(",\n");
755 if (!valEnd) {
756 valEnd = val.fEnd;
757 }
758 this->writeSpace();
759 this->writeBlock(valEnd - val.fStart, val.fStart);
760 } else {
761 this->writeSpace();
762 this->writeDefinition(*child);
763 }
764 }
765 this->lf(1);
766 for (auto comment : child->fChildren) {
767 if (MarkType::kComment == comment->fMarkType) {
768 TextParser parser(comment);
769 parser.skipExact("*");
770 parser.skipExact("*");
771 while (!parser.eof() && parser.skipWhiteSpace()) {
772 parser.skipExact("*");
773 parser.skipWhiteSpace();
774 const char* start = parser.fChar;
775 parser.skipToEndBracket('\n');
776 this->lf(1);
777 this->writeBlock(parser.fChar - start, start);
778 }
779 }
780 }
781 this->writeEndTag();
782 }
783 this->lf(2);
784}
785
786void IncludeParser::dumpMethod(const Definition& token) {
787 this->writeString("#Method");
788 this->writeSpace();
789 if ("SK_TO_STRING_NONVIRT" == token.fName) {
790 this->writeString("void toString(SkString* str) const;");
791 this->lf(2);
792 this->writeEndTag("DefinedBy", "SK_TO_STRING_NONVIRT()");
793 this->lf(2);
794 this->writeTag("Private");
795 this->lf(1);
796 this->writeString("macro expands to: void toString(SkString* str) const;");
797 this->writeEndTag();
798 this->lf(2);
Ben Wagner63fd7602017-10-09 15:45:33 -0400799 const char desc[] =
Cary Clark9174bda2017-09-19 17:39:32 -0400800 "Creates string representation. The representation is read by\n"
801 "internal debugging tools. The interface and implementation may be\n"
802 "suppressed by defining SK_IGNORE_TO_STRING.";
803 this->writeBlock(sizeof(desc) - 1, desc);
804 this->lf(2);
805 this->writeTag("Param", "str");
806 this->writeSpace(2);
807 this->writeString("storage for string representation");
808 this->writeSpace();
809 this->writeString("##");
810 this->lf(2);
811 return;
812 }
813 this->writeBlock(token.length(), token.fStart);
814 this->lf(1);
815 this->dumpComment(token);
816}
817
818void IncludeParser::dumpMember(const Definition& token) {
819 this->writeTag("Member");
820 this->writeSpace();
821 this->writeDefinition(token, token.fName, 2);
822 lf(1);
823 for (auto child : token.fChildren) {
824 this->writeDefinition(*child);
825 }
826 this->writeEndTag();
827 lf(2);
828}
829
Cary Clarkd0530ba2017-09-14 11:25:39 -0400830bool IncludeParser::dumpTokens(const string& dir) {
Cary Clark9174bda2017-09-19 17:39:32 -0400831 for (const auto& member : fIClassMap) {
832 if (string::npos != member.first.find("::")) {
833 continue;
834 }
835 if (!this->dumpTokens(dir, member.first)) {
836 return false;
837 }
838 }
839 return true;
840}
841
Ben Wagner63fd7602017-10-09 15:45:33 -0400842 // dump equivalent markup
Cary Clark9174bda2017-09-19 17:39:32 -0400843bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400844 string fileName = dir;
845 if (dir.length() && '/' != dir[dir.length() - 1]) {
846 fileName += '/';
847 }
848 fileName += skClassName + "_Reference.bmh";
Cary Clark8032b982017-07-28 11:04:54 -0400849 fOut = fopen(fileName.c_str(), "wb");
850 if (!fOut) {
851 SkDebugf("could not open output file %s\n", fileName.c_str());
Cary Clarkd0530ba2017-09-14 11:25:39 -0400852 return false;
Cary Clark8032b982017-07-28 11:04:54 -0400853 }
854 string prefixName = skClassName.substr(0, 2);
855 string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
856 ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
Cary Clark9174bda2017-09-19 17:39:32 -0400857 this->writeTagNoLF("Topic", topicName);
858 this->writeTag("Alias", topicName + "_Reference");
859 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400860 auto& classMap = fIClassMap[skClassName];
Cary Clarka560c472017-11-27 10:44:06 -0500861 SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
862 const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400863 this->writeTag(containerType, skClassName);
864 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400865 auto& tokens = classMap.fTokens;
866 for (auto& token : tokens) {
867 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
868 continue;
869 }
Cary Clark9174bda2017-09-19 17:39:32 -0400870 this->writeDefinition(token);
871 this->lf(1);
Cary Clark8032b982017-07-28 11:04:54 -0400872 }
873 this->lf(2);
874 string className(skClassName.substr(2));
875 vector<string> sortedClasses;
876 size_t maxLen = 0;
877 for (const auto& oneClass : fIClassMap) {
878 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
879 continue;
880 }
881 string structName = oneClass.first.substr(skClassName.length() + 2);
882 maxLen = SkTMax(maxLen, structName.length());
883 sortedClasses.emplace_back(structName);
884 }
Cary Clark9174bda2017-09-19 17:39:32 -0400885 this->writeTag("Topic", "Overview");
886 this->lf(2);
887 this->writeTag("Subtopic", "Subtopics");
888 this->writeEndTag("ToDo", "manually add subtopics");
889 this->writeTableHeader("topics", 0, "description");
890 this->writeTableTrailer();
891 this->writeEndTag();
892 this->lf(2);
893 if (maxLen) {
894 this->writeTag("Subtopic", "Structs");
895 this->writeTableHeader("description", maxLen, "struct");
896 for (auto& name : sortedClasses) {
897 this->writeTableRow(maxLen, name);
898 }
899 this->writeTableTrailer();
900 this->writeEndTag("Subtopic");
901 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400902 }
Cary Clark8032b982017-07-28 11:04:54 -0400903 maxLen = 0;
Cary Clark9174bda2017-09-19 17:39:32 -0400904 size_t constructorMax = 0;
905 size_t operatorMax = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400906 vector<string> sortedNames;
Cary Clark9174bda2017-09-19 17:39:32 -0400907 vector<string> constructorNames;
908 vector<string> operatorNames;
Cary Clark8032b982017-07-28 11:04:54 -0400909 for (const auto& token : classMap.fTokens) {
910 if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
911 continue;
912 }
Cary Clark9174bda2017-09-19 17:39:32 -0400913 string name = token.fName;
Cary Clark8032b982017-07-28 11:04:54 -0400914 if (name.substr(0, 7) == "android" || string::npos != name.find("nternal_")) {
915 continue;
916 }
Cary Clark9174bda2017-09-19 17:39:32 -0400917 if ((name.substr(0, 2) == "Sk" && 2 == name.find(className)) || '~' == name[0]) {
918 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
919 constructorMax = SkTMax(constructorMax, name.length());
920 constructorNames.emplace_back(name);
921 continue;
922 }
923 if (name.substr(0, 8) == "operator") {
924 name = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart));
925 operatorMax = SkTMax(operatorMax, name.length());
926 operatorNames.emplace_back(name);
927 continue;
928 }
Cary Clark8032b982017-07-28 11:04:54 -0400929 if (name[name.length() - 2] == '_' && isdigit(name[name.length() - 1])) {
930 continue;
931 }
Cary Clark9174bda2017-09-19 17:39:32 -0400932 if ("SK_TO_STRING_NONVIRT" == name) {
933 name = "toString";
934 }
Cary Clark8032b982017-07-28 11:04:54 -0400935 size_t paren = name.find('(');
936 size_t funcLen = string::npos == paren ? name.length() : paren;
937 maxLen = SkTMax(maxLen, funcLen);
938 sortedNames.emplace_back(name);
939 }
Cary Clark9174bda2017-09-19 17:39:32 -0400940 if (constructorMax) {
941 std::sort(constructorNames.begin(), constructorNames.end());
942 this->writeTag("Subtopic", "Constructors");
943 this->writeTableHeader("description", constructorMax, "function");
944 for (auto& name : constructorNames) {
945 this->writeTableRow(constructorMax, name);
946 }
947 this->writeTableTrailer();
948 this->writeEndTag("Subtopic");
949 this->lf(2);
950 }
951 if (operatorMax) {
952 std::sort(operatorNames.begin(), operatorNames.end());
953 this->writeTag("Subtopic", "Operators");
954 this->writeTableHeader("description", operatorMax, "function");
955 for (auto& name : operatorNames) {
956 this->writeTableRow(operatorMax, name);
957 }
958 this->writeTableTrailer();
959 this->writeEndTag("Subtopic");
960 this->lf(2);
961 }
Cary Clark8032b982017-07-28 11:04:54 -0400962 std::sort(sortedNames.begin(), sortedNames.end());
Cary Clark9174bda2017-09-19 17:39:32 -0400963 this->writeTag("Subtopic", "Member_Functions");
964 this->writeTableHeader("description", maxLen, "function");
Cary Clark8032b982017-07-28 11:04:54 -0400965 for (auto& name : sortedNames) {
966 size_t paren = name.find('(');
967 size_t funcLen = string::npos == paren ? name.length() : paren;
Cary Clark9174bda2017-09-19 17:39:32 -0400968 this->writeTableRow(maxLen, name.substr(0, funcLen));
Cary Clark8032b982017-07-28 11:04:54 -0400969 }
Cary Clark9174bda2017-09-19 17:39:32 -0400970 this->writeTableTrailer();
971 this->writeEndTag("Subtopic");
972 this->lf(2);
973 this->writeEndTag("Topic");
974 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400975
976 for (auto& oneClass : fIClassMap) {
977 if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
978 continue;
979 }
980 string innerName = oneClass.first.substr(skClassName.length() + 2);
Cary Clark9174bda2017-09-19 17:39:32 -0400981 this->writeString(
Cary Clark8032b982017-07-28 11:04:54 -0400982 "# ------------------------------------------------------------------------------");
Cary Clark9174bda2017-09-19 17:39:32 -0400983 this->lf(2);
Cary Clarka560c472017-11-27 10:44:06 -0500984 KeyWord keyword = oneClass.second.fKeyWord;
985 SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
986 const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
Cary Clark9174bda2017-09-19 17:39:32 -0400987 this->writeTag(containerType, innerName);
988 this->lf(2);
989 this->writeTag("Code");
990 this->writeEndTag("ToDo", "fill this in manually");
991 this->writeEndTag();
992 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -0400993 for (auto& token : oneClass.second.fTokens) {
994 if (Definition::Type::kMark != token.fType || MarkType::kComment != token.fMarkType) {
995 continue;
996 }
Cary Clark9174bda2017-09-19 17:39:32 -0400997 this->writeDefinition(token);
Cary Clark8032b982017-07-28 11:04:54 -0400998 }
999 this->lf(2);
1000 this->dumpClassTokens(oneClass.second);
1001 this->lf(2);
Cary Clark9174bda2017-09-19 17:39:32 -04001002 this->writeEndTag(containerType, innerName);
1003 this->lf(2);
Cary Clark8032b982017-07-28 11:04:54 -04001004 }
1005 this->dumpClassTokens(classMap);
Cary Clark9174bda2017-09-19 17:39:32 -04001006 this->writeEndTag(containerType, skClassName);
1007 this->lf(2);
1008 this->writeEndTag("Topic", topicName);
1009 this->lfAlways(1);
Cary Clark8032b982017-07-28 11:04:54 -04001010 fclose(fOut);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001011 SkDebugf("wrote %s\n", fileName.c_str());
1012 return true;
Cary Clark8032b982017-07-28 11:04:54 -04001013}
1014
1015bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
1016 // add comment preceding class, if any
1017 const Definition* parent = includeDef.fParent;
1018 int index = includeDef.fParentIndex;
1019 auto wordIter = parent->fTokens.begin();
1020 std::advance(wordIter, index);
1021 SkASSERT(&*wordIter == &includeDef);
1022 while (parent->fTokens.begin() != wordIter) {
1023 auto testIter = std::prev(wordIter);
1024 if (Definition::Type::kWord != testIter->fType
1025 && Definition::Type::kKeyWord != testIter->fType
1026 && (Definition::Type::kBracket != testIter->fType
1027 || Bracket::kAngle != testIter->fBracket)
1028 && (Definition::Type::kPunctuation != testIter->fType
1029 || Punctuation::kAsterisk != testIter->fPunctuation)) {
1030 break;
1031 }
1032 wordIter = testIter;
1033 }
1034 auto commentIter = wordIter;
1035 while (parent->fTokens.begin() != commentIter) {
1036 auto testIter = std::prev(commentIter);
1037 bool isComment = Definition::Type::kBracket == testIter->fType
1038 && (Bracket::kSlashSlash == testIter->fBracket
1039 || Bracket::kSlashStar == testIter->fBracket);
1040 if (!isComment) {
1041 break;
1042 }
1043 commentIter = testIter;
1044 }
1045 while (commentIter != wordIter) {
1046 if (!this->parseComment(commentIter->fFileName, commentIter->fContentStart,
1047 commentIter->fContentEnd, commentIter->fLineCount, markupDef)) {
1048 return false;
1049 }
1050 commentIter = std::next(commentIter);
1051 }
1052 return true;
1053}
1054
Cary Clarkbad5ad72017-08-03 17:14:08 -04001055bool IncludeParser::internalName(const Definition& token) const {
1056 return 0 == token.fName.find("internal_")
1057 || 0 == token.fName.find("Internal_")
1058 || 0 == token.fName.find("legacy_")
1059 || 0 == token.fName.find("temporary_")
1060 || 0 == token.fName.find("private_");
1061}
1062
Cary Clark8032b982017-07-28 11:04:54 -04001063// caller calls reportError, so just return false here
1064bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
1065 SkASSERT(includeDef->fTokens.size() > 0);
Cary Clark8032b982017-07-28 11:04:54 -04001066 // parse class header
1067 auto iter = includeDef->fTokens.begin();
1068 if (!strncmp(iter->fStart, "SK_API", iter->fContentEnd - iter->fStart)) {
1069 // todo : documentation is ignoring this for now
1070 iter = std::next(iter);
1071 }
1072 string nameStr(iter->fStart, iter->fContentEnd - iter->fStart);
1073 includeDef->fName = nameStr;
Cary Clark9174bda2017-09-19 17:39:32 -04001074 iter = std::next(iter);
1075 if (iter == includeDef->fTokens.end()) {
1076 return true; // forward declaration only
1077 }
Cary Clark8032b982017-07-28 11:04:54 -04001078 do {
1079 if (iter == includeDef->fTokens.end()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001080 return includeDef->reportError<bool>("unexpected end");
Cary Clark8032b982017-07-28 11:04:54 -04001081 }
1082 if ('{' == iter->fStart[0] && Definition::Type::kPunctuation == iter->fType) {
1083 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001084 }
Cary Clark8032b982017-07-28 11:04:54 -04001085 } while (static_cast<void>(iter = std::next(iter)), true);
1086 if (Punctuation::kLeftBrace != iter->fPunctuation) {
Cary Clark9174bda2017-09-19 17:39:32 -04001087 return iter->reportError<bool>("expected left brace");
Cary Clark8032b982017-07-28 11:04:54 -04001088 }
1089 IClassDefinition* markupDef = this->defineClass(*includeDef, nameStr);
1090 if (!markupDef) {
Cary Clark9174bda2017-09-19 17:39:32 -04001091 return iter->reportError<bool>("expected markup definition");
Cary Clark8032b982017-07-28 11:04:54 -04001092 }
1093 markupDef->fStart = iter->fStart;
1094 if (!this->findComments(*includeDef, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001095 return iter->reportError<bool>("find comments failed");
Cary Clark8032b982017-07-28 11:04:54 -04001096 }
1097// if (1 != includeDef->fChildren.size()) {
1098// return false; // fix me: SkCanvasClipVisitor isn't correctly parsed
1099// }
1100 includeDef = includeDef->fChildren.front();
1101 iter = includeDef->fTokens.begin();
1102 // skip until public
1103 int publicIndex = 0;
1104 if (IsStruct::kNo == isStruct) {
1105 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1106 size_t publicLen = strlen(publicName);
1107 while (iter != includeDef->fTokens.end()
1108 && (publicLen != (size_t) (iter->fContentEnd - iter->fStart)
1109 || strncmp(iter->fStart, publicName, publicLen))) {
1110 iter = std::next(iter);
1111 ++publicIndex;
1112 }
1113 }
1114 auto childIter = includeDef->fChildren.begin();
1115 while (childIter != includeDef->fChildren.end() && (*childIter)->fParentIndex < publicIndex) {
1116 (*childIter)->fPrivate = true;
1117 childIter = std::next(childIter);
1118 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001119 int keyIndex = publicIndex;
1120 KeyWord currentKey = KeyWord::kPublic;
1121 const char* publicName = kKeyWords[(int) KeyWord::kPublic].fName;
1122 size_t publicLen = strlen(publicName);
Cary Clark8032b982017-07-28 11:04:54 -04001123 const char* protectedName = kKeyWords[(int) KeyWord::kProtected].fName;
1124 size_t protectedLen = strlen(protectedName);
1125 const char* privateName = kKeyWords[(int) KeyWord::kPrivate].fName;
1126 size_t privateLen = strlen(privateName);
Cary Clark884dd7d2017-10-11 10:37:52 -04001127 while (childIter != includeDef->fChildren.end()) {
Cary Clark8032b982017-07-28 11:04:54 -04001128 Definition* child = *childIter;
Cary Clark884dd7d2017-10-11 10:37:52 -04001129 while (child->fParentIndex > keyIndex && iter != includeDef->fTokens.end()) {
1130 const char* testStart = iter->fStart;
1131 size_t testLen = (size_t) (iter->fContentEnd - testStart);
1132 iter = std::next(iter);
1133 ++keyIndex;
1134 if (publicLen == testLen && !strncmp(testStart, publicName, testLen)) {
1135 currentKey = KeyWord::kPublic;
1136 break;
1137 }
1138 if (protectedLen == testLen && !strncmp(testStart, protectedName, testLen)) {
1139 currentKey = KeyWord::kProtected;
1140 break;
1141 }
1142 if (privateLen == testLen && !strncmp(testStart, privateName, testLen)) {
1143 currentKey = KeyWord::kPrivate;
1144 break;
1145 }
1146 }
1147 fLastObject = nullptr;
1148 if (KeyWord::kPublic == currentKey) {
1149 if (!this->parseObject(child, markupDef)) {
1150 return false;
1151 }
1152 } else {
1153 child->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001154 }
Cary Clark73fa9722017-08-29 17:36:51 -04001155 fLastObject = child;
Cary Clark8032b982017-07-28 11:04:54 -04001156 childIter = std::next(childIter);
1157 }
Cary Clark8032b982017-07-28 11:04:54 -04001158 SkASSERT(fParent->fParent);
1159 fParent = fParent->fParent;
1160 return true;
1161}
1162
1163bool IncludeParser::parseComment(const string& filename, const char* start, const char* end,
1164 int lineCount, Definition* markupDef) {
1165 TextParser parser(filename, start, end, lineCount);
1166 // parse doxygen if present
1167 if (parser.startsWith("**")) {
1168 parser.next();
1169 parser.next();
1170 parser.skipWhiteSpace();
1171 if ('\\' == parser.peek()) {
1172 parser.next();
1173 if (!parser.skipWord(kKeyWords[(int) markupDef->fKeyWord].fName)) {
1174 return reportError<bool>("missing object type");
1175 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001176 if (!parser.skipWord(markupDef->fName.c_str()) &&
1177 KeyWord::kEnum != markupDef->fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001178 return reportError<bool>("missing object name");
1179 }
1180
1181 }
1182 }
1183 // remove leading '*' if present
1184 Definition* parent = markupDef->fTokens.size() ? &markupDef->fTokens.back() : markupDef;
1185 while (!parser.eof() && parser.skipWhiteSpace()) {
1186 while ('*' == parser.peek()) {
1187 parser.next();
1188 if (parser.eof()) {
1189 break;
1190 }
1191 parser.skipWhiteSpace();
1192 }
1193 if (parser.eof()) {
1194 break;
1195 }
1196 const char* lineEnd = parser.trimmedLineEnd();
Ben Wagner63fd7602017-10-09 15:45:33 -04001197 markupDef->fTokens.emplace_back(MarkType::kComment, parser.fChar, lineEnd,
Cary Clark8032b982017-07-28 11:04:54 -04001198 parser.fLineCount, parent);
1199 parser.skipToEndBracket('\n');
1200 }
1201 return true;
1202}
1203
1204bool IncludeParser::parseDefine() {
1205
1206 return true;
1207}
1208
1209bool IncludeParser::parseEnum(Definition* child, Definition* markupDef) {
1210 string nameStr;
1211 if (child->fTokens.size() > 0) {
1212 auto token = child->fTokens.begin();
1213 if (Definition::Type::kKeyWord == token->fType && KeyWord::kClass == token->fKeyWord) {
1214 token = token->fTokens.begin();
1215 }
1216 if (Definition::Type::kWord == token->fType) {
1217 nameStr += string(token->fStart, token->fContentEnd - token->fStart);
1218 }
1219 }
1220 markupDef->fTokens.emplace_back(MarkType::kEnum, child->fContentStart, child->fContentEnd,
1221 child->fLineCount, markupDef);
1222 Definition* markupChild = &markupDef->fTokens.back();
1223 SkASSERT(KeyWord::kNone == markupChild->fKeyWord);
1224 markupChild->fKeyWord = KeyWord::kEnum;
1225 TextParser enumName(child);
1226 enumName.skipExact("enum ");
Cary Clarkbad5ad72017-08-03 17:14:08 -04001227 if (enumName.skipExact("class ")) {
1228 markupChild->fMarkType = MarkType::kEnumClass;
1229 }
Cary Clark8032b982017-07-28 11:04:54 -04001230 const char* nameStart = enumName.fChar;
1231 enumName.skipToSpace();
Ben Wagner63fd7602017-10-09 15:45:33 -04001232 markupChild->fName = markupDef->fName + "::" +
Cary Clark8032b982017-07-28 11:04:54 -04001233 string(nameStart, (size_t) (enumName.fChar - nameStart));
1234 if (!this->findComments(*child, markupChild)) {
1235 return false;
1236 }
1237 TextParser parser(child);
1238 parser.skipToEndBracket('{');
Cary Clark9174bda2017-09-19 17:39:32 -04001239 parser.next();
Cary Clark8032b982017-07-28 11:04:54 -04001240 const char* dataEnd;
1241 do {
Cary Clark8032b982017-07-28 11:04:54 -04001242 parser.skipWhiteSpace();
1243 if ('}' == parser.peek()) {
1244 break;
1245 }
1246 Definition* comment = nullptr;
1247 // note that comment, if any, can be before or after (on the same line, though) as member
1248 if ('#' == parser.peek()) {
1249 // fixme: handle preprecessor, but just skip it for now
1250 parser.skipToLineStart();
1251 }
1252 while (parser.startsWith("/*") || parser.startsWith("//")) {
1253 parser.next();
1254 const char* start = parser.fChar;
1255 const char* end;
1256 if ('*' == parser.peek()) {
1257 end = parser.strnstr("*/", parser.fEnd);
1258 parser.fChar = end;
1259 parser.next();
1260 parser.next();
1261 } else {
1262 end = parser.trimmedLineEnd();
1263 parser.skipToLineStart();
1264 }
1265 markupChild->fTokens.emplace_back(MarkType::kComment, start, end, parser.fLineCount,
1266 markupChild);
1267 comment = &markupChild->fTokens.back();
1268 comment->fTerminator = end;
1269 if (!this->parseComment(parser.fFileName, start, end, parser.fLineCount, comment)) {
1270 return false;
1271 }
1272 parser.skipWhiteSpace();
1273 }
1274 parser.skipWhiteSpace();
1275 const char* memberStart = parser.fChar;
1276 if ('}' == memberStart[0]) {
1277 break;
1278 }
Cary Clark9174bda2017-09-19 17:39:32 -04001279 // if there's comment on same the line as member def, output first as if it was before
1280
Cary Clark8032b982017-07-28 11:04:54 -04001281 parser.skipToNonAlphaNum();
1282 string memberName(memberStart, parser.fChar);
Cary Clark9174bda2017-09-19 17:39:32 -04001283 if (parser.eof() || !parser.skipWhiteSpace()) {
1284 return this->reportError<bool>("enum member must end with comma 1");
1285 }
Cary Clark8032b982017-07-28 11:04:54 -04001286 const char* dataStart = parser.fChar;
Cary Clark9174bda2017-09-19 17:39:32 -04001287 if ('=' == parser.peek()) {
1288 parser.skipToEndBracket(',');
1289 }
1290 if (parser.eof() || ',' != parser.peek()) {
1291 return this->reportError<bool>("enum member must end with comma 2");
1292 }
1293 dataEnd = parser.fChar;
1294 const char* start = parser.anyOf("/\n");
1295 SkASSERT(start);
1296 parser.skipTo(start);
1297 if ('/' == parser.next()) {
1298 char slashStar = parser.next();
1299 if ('/' == slashStar || '*' == slashStar) {
1300 TextParser::Save save(&parser);
1301 char doxCheck = parser.next();
1302 if ((slashStar != doxCheck && '!' != doxCheck) || '<' != parser.next()) {
1303 save.restore();
1304 }
1305 }
1306 parser.skipWhiteSpace();
1307 const char* commentStart = parser.fChar;
1308 if ('/' == slashStar) {
1309 parser.skipToEndBracket('\n');
1310 } else {
1311 parser.skipToEndBracket("*/");
1312 }
1313 SkASSERT(!parser.eof());
1314 const char* commentEnd = parser.fChar;
1315 markupChild->fTokens.emplace_back(MarkType::kComment, commentStart, commentEnd,
1316 parser.fLineCount, markupChild);
1317 comment = &markupChild->fTokens.back();
1318 comment->fTerminator = commentEnd;
1319 }
Cary Clark8032b982017-07-28 11:04:54 -04001320 markupChild->fTokens.emplace_back(MarkType::kMember, dataStart, dataEnd, parser.fLineCount,
1321 markupChild);
1322 Definition* member = &markupChild->fTokens.back();
1323 member->fName = memberName;
1324 if (comment) {
1325 member->fChildren.push_back(comment);
Cary Clark9174bda2017-09-19 17:39:32 -04001326 comment->fPrivate = true;
Cary Clark8032b982017-07-28 11:04:54 -04001327 }
1328 markupChild->fChildren.push_back(member);
Cary Clark9174bda2017-09-19 17:39:32 -04001329 } while (true);
Cary Clark884dd7d2017-10-11 10:37:52 -04001330 for (auto count : child->fChildren) {
1331 if (Definition::Type::kBracket == count->fType) {
1332 continue;
1333 }
1334 SkASSERT(Definition::Type::kKeyWord == count->fType);
1335 if (KeyWord::kClass == count->fKeyWord) {
1336 continue;
1337 }
1338 SkASSERT(KeyWord::kStatic == count->fKeyWord);
1339 markupChild->fTokens.emplace_back(MarkType::kMember, count->fContentStart,
1340 count->fContentEnd, count->fLineCount, markupChild);
1341 Definition* member = &markupChild->fTokens.back();
1342 member->fName = count->fName;
1343 // FIXME: ? add comment as well ?
1344 markupChild->fChildren.push_back(member);
1345 break;
1346 }
Cary Clark8032b982017-07-28 11:04:54 -04001347 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1348 SkASSERT(classDef.fStart);
1349 string uniqueName = this->uniqueName(classDef.fEnums, nameStr);
1350 markupChild->fName = uniqueName;
1351 classDef.fEnums[uniqueName] = markupChild;
1352 return true;
1353}
1354
1355bool IncludeParser::parseInclude(const string& name) {
1356 fParent = &fIncludeMap[name];
1357 fParent->fName = name;
1358 fParent->fFileName = fFileName;
1359 fParent->fType = Definition::Type::kFileType;
1360 fParent->fContentStart = fChar;
1361 fParent->fContentEnd = fEnd;
1362 // parse include file into tree
1363 while (fChar < fEnd) {
1364 if (!this->parseChar()) {
1365 return false;
1366 }
1367 }
1368 // parse tree and add named objects to maps
1369 fParent = &fIncludeMap[name];
1370 if (!this->parseObjects(fParent, nullptr)) {
1371 return false;
1372 }
1373 return true;
1374}
1375
1376bool IncludeParser::parseMember(Definition* child, Definition* markupDef) {
1377 const char* typeStart = child->fChildren[0]->fContentStart;
1378 markupDef->fTokens.emplace_back(MarkType::kMember, typeStart, child->fContentStart,
1379 child->fLineCount, markupDef);
1380 Definition* markupChild = &markupDef->fTokens.back();
1381 TextParser nameParser(child);
1382 nameParser.skipToNonAlphaNum();
1383 string nameStr = string(child->fContentStart, nameParser.fChar - child->fContentStart);
1384 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1385 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1386 markupChild->fName = uniqueName;
Cary Clark9174bda2017-09-19 17:39:32 -04001387 markupChild->fTerminator = markupChild->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001388 classDef.fMembers[uniqueName] = markupChild;
1389 if (child->fParentIndex >= 2) {
1390 auto comment = child->fParent->fTokens.begin();
1391 std::advance(comment, child->fParentIndex - 2);
1392 if (Definition::Type::kBracket == comment->fType
1393 && (Bracket::kSlashStar == comment->fBracket
1394 || Bracket::kSlashSlash == comment->fBracket)) {
1395 TextParser parser(&*comment);
1396 do {
1397 parser.skipToAlpha();
1398 if (parser.eof()) {
1399 break;
1400 }
1401 const char* start = parser.fChar;
Cary Clarkce101242017-09-01 15:51:02 -04001402 const char* end = parser.trimmedBracketEnd('\n');
Cary Clark8032b982017-07-28 11:04:54 -04001403 if (Bracket::kSlashStar == comment->fBracket) {
1404 const char* commentEnd = parser.strnstr("*/", end);
1405 if (commentEnd) {
1406 end = commentEnd;
1407 }
1408 }
1409 markupDef->fTokens.emplace_back(MarkType::kComment, start, end, child->fLineCount,
1410 markupDef);
1411 Definition* commentChild = &markupDef->fTokens.back();
1412 markupChild->fChildren.emplace_back(commentChild);
1413 parser.skipTo(end);
1414 } while (!parser.eof());
1415 }
1416 }
1417 return true;
1418}
1419
1420bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) {
1421 auto tokenIter = child->fParent->fTokens.begin();
1422 std::advance(tokenIter, child->fParentIndex);
1423 tokenIter = std::prev(tokenIter);
Cary Clark154beea2017-10-26 07:58:48 -04001424 const char* nameEnd = tokenIter->fContentEnd;
Cary Clarka560c472017-11-27 10:44:06 -05001425 bool addConst = false;
1426 auto operatorCheck = tokenIter;
1427 if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) {
1428 operatorCheck = std::prev(tokenIter);
1429 }
1430 if (KeyWord::kOperator == operatorCheck->fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001431 auto closeParen = std::next(tokenIter);
1432 SkASSERT(Definition::Type::kBracket == closeParen->fType &&
1433 '(' == closeParen->fContentStart[0]);
1434 nameEnd = closeParen->fContentEnd + 1;
1435 closeParen = std::next(closeParen);
Cary Clark154beea2017-10-26 07:58:48 -04001436 if (Definition::Type::kKeyWord == closeParen->fType &&
1437 KeyWord::kConst == closeParen->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001438 addConst = true;
Cary Clark154beea2017-10-26 07:58:48 -04001439 }
Cary Clarka560c472017-11-27 10:44:06 -05001440 tokenIter = operatorCheck;
Cary Clark154beea2017-10-26 07:58:48 -04001441 }
1442 string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart);
Cary Clarka560c472017-11-27 10:44:06 -05001443 if (addConst) {
1444 nameStr += "_const";
Cary Clark154beea2017-10-26 07:58:48 -04001445 }
Cary Clark8032b982017-07-28 11:04:54 -04001446 while (tokenIter != child->fParent->fTokens.begin()) {
1447 auto testIter = std::prev(tokenIter);
1448 switch (testIter->fType) {
1449 case Definition::Type::kWord:
Cary Clark154beea2017-10-26 07:58:48 -04001450 if (testIter == child->fParent->fTokens.begin() &&
1451 (KeyWord::kIfdef == child->fParent->fKeyWord ||
1452 KeyWord::kIfndef == child->fParent->fKeyWord ||
1453 KeyWord::kIf == child->fParent->fKeyWord)) {
1454 std::next(tokenIter);
1455 break;
1456 }
Cary Clark8032b982017-07-28 11:04:54 -04001457 goto keepGoing;
1458 case Definition::Type::kKeyWord: {
1459 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1460 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1461 goto keepGoing;
1462 }
1463 } break;
1464 case Definition::Type::kBracket:
1465 if (Bracket::kAngle == testIter->fBracket) {
1466 goto keepGoing;
1467 }
1468 break;
1469 case Definition::Type::kPunctuation:
1470 if (Punctuation::kSemicolon == testIter->fPunctuation
1471 || Punctuation::kLeftBrace == testIter->fPunctuation
1472 || Punctuation::kColon == testIter->fPunctuation) {
1473 break;
1474 }
1475 keepGoing:
1476 tokenIter = testIter;
1477 continue;
1478 default:
1479 break;
1480 }
1481 break;
1482 }
1483 tokenIter->fName = nameStr;
1484 tokenIter->fMarkType = MarkType::kMethod;
Cary Clark884dd7d2017-10-11 10:37:52 -04001485 tokenIter->fPrivate = string::npos != nameStr.find("::");
Cary Clark8032b982017-07-28 11:04:54 -04001486 auto testIter = child->fParent->fTokens.begin();
1487 SkASSERT(child->fParentIndex > 0);
1488 std::advance(testIter, child->fParentIndex - 1);
Cary Clark884dd7d2017-10-11 10:37:52 -04001489 if (tokenIter->fParent && KeyWord::kIfdef == tokenIter->fParent->fKeyWord &&
1490 0 == tokenIter->fParentIndex) {
1491 tokenIter = std::next(tokenIter);
1492 }
Cary Clark8032b982017-07-28 11:04:54 -04001493 const char* start = tokenIter->fContentStart;
1494 const char* end = tokenIter->fContentEnd;
1495 const char kDebugCodeStr[] = "SkDEBUGCODE";
1496 const size_t kDebugCodeLen = sizeof(kDebugCodeStr) - 1;
1497 if (end - start == kDebugCodeLen && !strncmp(start, kDebugCodeStr, kDebugCodeLen)) {
1498 std::advance(testIter, 1);
1499 start = testIter->fContentStart + 1;
1500 end = testIter->fContentEnd - 1;
1501 } else {
1502 end = testIter->fContentEnd;
1503 while (testIter != child->fParent->fTokens.end()) {
1504 testIter = std::next(testIter);
1505 switch (testIter->fType) {
1506 case Definition::Type::kPunctuation:
1507 SkASSERT(Punctuation::kSemicolon == testIter->fPunctuation
1508 || Punctuation::kLeftBrace == testIter->fPunctuation
1509 || Punctuation::kColon == testIter->fPunctuation);
1510 end = testIter->fStart;
1511 break;
1512 case Definition::Type::kKeyWord: {
1513 KeyProperty keyProperty = kKeyWords[(int) testIter->fKeyWord].fProperty;
1514 if (KeyProperty::kNumber == keyProperty || KeyProperty::kModifier == keyProperty) {
1515 continue;
1516 }
1517 } break;
1518 default:
1519 continue;
1520 }
1521 break;
1522 }
1523 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001524 while (end > start && ' ' >= end[-1]) {
1525 --end;
1526 }
Cary Clark9174bda2017-09-19 17:39:32 -04001527 if (!markupDef) {
1528 auto parentIter = child->fParent->fTokens.begin();
1529 SkASSERT(child->fParentIndex > 0);
1530 std::advance(parentIter, child->fParentIndex - 1);
1531 Definition* methodName = &*parentIter;
Cary Clarka560c472017-11-27 10:44:06 -05001532 TextParser nameParser(methodName);
1533 if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) {
Cary Clark9174bda2017-09-19 17:39:32 -04001534 return true; // expect this is inline class definition outside of class
1535 }
Cary Clarka560c472017-11-27 10:44:06 -05001536 string name(nameParser.fLine, nameParser.lineLength());
1537 auto finder = fIFunctionMap.find(name);
1538 if (fIFunctionMap.end() != finder) {
1539 // create unique name
1540 SkASSERT(0); // incomplete
1541 }
1542 auto globalFunction = &fIFunctionMap[name];
1543 globalFunction->fContentStart = start;
1544 globalFunction->fName = name;
Cary Clarka560c472017-11-27 10:44:06 -05001545 globalFunction->fFiddle = name;
1546 globalFunction->fContentEnd = end;
1547 globalFunction->fMarkType = MarkType::kMethod;
1548 globalFunction->fLineCount = tokenIter->fLineCount;
1549 return true;
Cary Clark9174bda2017-09-19 17:39:32 -04001550 }
Cary Clark8032b982017-07-28 11:04:54 -04001551 markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
1552 markupDef);
1553 Definition* markupChild = &markupDef->fTokens.back();
1554 // do find instead -- I wonder if there is a way to prevent this in c++
1555 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1556 SkASSERT(classDef.fStart);
1557 string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
1558 markupChild->fName = uniqueName;
1559 if (!this->findComments(*child, markupChild)) {
1560 return false;
1561 }
1562 classDef.fMethods[uniqueName] = markupChild;
1563 return true;
1564}
1565
Cary Clark8032b982017-07-28 11:04:54 -04001566bool IncludeParser::parseObjects(Definition* parent, Definition* markupDef) {
1567 for (auto& child : parent->fChildren) {
1568 if (!this->parseObject(child, markupDef)) {
1569 return false;
1570 }
1571 }
1572 return true;
1573}
1574
1575bool IncludeParser::parseObject(Definition* child, Definition* markupDef) {
1576 // set up for error reporting
1577 fLine = fChar = child->fStart;
1578 fEnd = child->fContentEnd;
1579 // todo: put original line number in child as well
1580 switch (child->fType) {
1581 case Definition::Type::kKeyWord:
1582 switch (child->fKeyWord) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001583 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001584 if (!this->parseClass(child, IsStruct::kNo)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001585 return false;
Cary Clark8032b982017-07-28 11:04:54 -04001586 }
1587 break;
1588 case KeyWord::kEnum:
1589 if (!this->parseEnum(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001590 return child->reportError<bool>("failed to parse enum");
Cary Clark8032b982017-07-28 11:04:54 -04001591 }
1592 break;
1593 case KeyWord::kStruct:
1594 if (!this->parseClass(child, IsStruct::kYes)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001595 return child->reportError<bool>("failed to parse struct");
Cary Clark8032b982017-07-28 11:04:54 -04001596 }
1597 break;
1598 case KeyWord::kTemplate:
1599 if (!this->parseTemplate()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001600 return child->reportError<bool>("failed to parse template");
Cary Clark8032b982017-07-28 11:04:54 -04001601 }
1602 break;
1603 case KeyWord::kTypedef:
Cary Clark2f466242017-12-11 16:03:17 -05001604 if (!this->parseTypedef(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001605 return child->reportError<bool>("failed to parse typedef");
Cary Clark8032b982017-07-28 11:04:54 -04001606 }
1607 break;
1608 case KeyWord::kUnion:
1609 if (!this->parseUnion()) {
Cary Clark9174bda2017-09-19 17:39:32 -04001610 return child->reportError<bool>("failed to parse union");
Cary Clark8032b982017-07-28 11:04:54 -04001611 }
1612 break;
1613 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001614 return child->reportError<bool>("unhandled keyword");
Cary Clark8032b982017-07-28 11:04:54 -04001615 }
1616 break;
1617 case Definition::Type::kBracket:
1618 switch (child->fBracket) {
1619 case Bracket::kParen:
Cary Clark73fa9722017-08-29 17:36:51 -04001620 if (fLastObject) {
1621 TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
1622 child->fStart, fLastObject->fLineCount);
Cary Clark9174bda2017-09-19 17:39:32 -04001623 if (!checkDeprecated.eof()) {
1624 checkDeprecated.skipWhiteSpace();
1625 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1626 break;
1627 }
1628 }
1629 }
1630 {
1631 auto tokenIter = child->fParent->fTokens.begin();
1632 std::advance(tokenIter, child->fParentIndex);
1633 tokenIter = std::prev(tokenIter);
1634 TextParser checkDeprecated(&*tokenIter);
Cary Clark73fa9722017-08-29 17:36:51 -04001635 if (checkDeprecated.startsWith("SK_ATTR_DEPRECATED")) {
1636 break;
1637 }
1638 }
Cary Clark8032b982017-07-28 11:04:54 -04001639 if (!this->parseMethod(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001640 return child->reportError<bool>("failed to parse method");
Cary Clark8032b982017-07-28 11:04:54 -04001641 }
Cary Clark73fa9722017-08-29 17:36:51 -04001642 break;
Cary Clark8032b982017-07-28 11:04:54 -04001643 case Bracket::kSlashSlash:
1644 case Bracket::kSlashStar:
1645 // comments are picked up by parsing objects first
1646 break;
1647 case Bracket::kPound:
1648 // special-case the #xxx xxx_DEFINED entries
1649 switch (child->fKeyWord) {
Cary Clarka560c472017-11-27 10:44:06 -05001650 case KeyWord::kIf:
Cary Clark8032b982017-07-28 11:04:54 -04001651 case KeyWord::kIfndef:
1652 case KeyWord::kIfdef:
1653 if (child->boilerplateIfDef(fParent)) {
1654 if (!this->parseObjects(child, markupDef)) {
1655 return false;
1656 }
1657 break;
1658 }
1659 goto preproError;
1660 case KeyWord::kDefine:
1661 if (child->boilerplateDef(fParent)) {
1662 break;
1663 }
1664 goto preproError;
1665 case KeyWord::kEndif:
1666 if (child->boilerplateEndIf()) {
1667 break;
1668 }
1669 case KeyWord::kInclude:
1670 // ignored for now
1671 break;
1672 case KeyWord::kElse:
1673 case KeyWord::kElif:
1674 // todo: handle these
1675 break;
1676 default:
1677 preproError:
Cary Clark9174bda2017-09-19 17:39:32 -04001678 return child->reportError<bool>("unhandled preprocessor");
Cary Clark8032b982017-07-28 11:04:54 -04001679 }
1680 break;
1681 case Bracket::kAngle:
1682 // pick up templated function pieces when method is found
1683 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001684 case Bracket::kDebugCode:
Cary Clark154beea2017-10-26 07:58:48 -04001685 if (!this->parseObjects(child, markupDef)) {
1686 return false;
1687 }
Cary Clark73fa9722017-08-29 17:36:51 -04001688 break;
Cary Clark9174bda2017-09-19 17:39:32 -04001689 case Bracket::kSquare: {
1690 // check to see if parent is operator, the only case we handle so far
1691 auto prev = child->fParent->fTokens.begin();
1692 std::advance(prev, child->fParentIndex - 1);
1693 if (KeyWord::kOperator != prev->fKeyWord) {
1694 return child->reportError<bool>("expected operator overload");
1695 }
1696 } break;
Cary Clark8032b982017-07-28 11:04:54 -04001697 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001698 return child->reportError<bool>("unhandled bracket");
Cary Clark8032b982017-07-28 11:04:54 -04001699 }
1700 break;
1701 case Definition::Type::kWord:
1702 if (MarkType::kMember != child->fMarkType) {
Cary Clark9174bda2017-09-19 17:39:32 -04001703 return child->reportError<bool>("unhandled word type");
Cary Clark8032b982017-07-28 11:04:54 -04001704 }
1705 if (!this->parseMember(child, markupDef)) {
Cary Clark9174bda2017-09-19 17:39:32 -04001706 return child->reportError<bool>("unparsable member");
Cary Clark8032b982017-07-28 11:04:54 -04001707 }
1708 break;
1709 default:
Cary Clark9174bda2017-09-19 17:39:32 -04001710 return child->reportError<bool>("unhandled type");
Cary Clark8032b982017-07-28 11:04:54 -04001711 break;
1712 }
1713 return true;
1714}
1715
1716bool IncludeParser::parseTemplate() {
1717
1718 return true;
1719}
1720
Cary Clark2f466242017-12-11 16:03:17 -05001721bool IncludeParser::parseTypedef(Definition* child, Definition* markupDef) {
1722 TextParser typedefParser(child);
1723 string nameStr = typedefParser.typedefName();
1724 if (!markupDef) {
1725 Definition& typedefDef = fITypedefMap[nameStr];
1726 SkASSERT(!typedefDef.fStart);
1727 typedefDef.fStart = child->fContentStart;
1728 typedefDef.fContentStart = child->fContentStart;
1729 typedefDef.fName = nameStr;
1730 typedefDef.fFiddle = nameStr;
1731 typedefDef.fContentEnd = child->fContentEnd;
1732 typedefDef.fTerminator = child->fContentEnd;
1733 typedefDef.fMarkType = MarkType::kTypedef;
1734 typedefDef.fLineCount = child->fLineCount;
1735 return true;
1736 }
1737 markupDef->fTokens.emplace_back(MarkType::kTypedef, child->fContentStart, child->fContentEnd,
1738 child->fLineCount, markupDef);
1739 Definition* markupChild = &markupDef->fTokens.back();
1740 markupChild->fName = nameStr;
1741 markupChild->fTerminator = markupChild->fContentEnd;
1742 IClassDefinition& classDef = fIClassMap[markupDef->fName];
1743 classDef.fTypedefs[nameStr] = markupChild;
Cary Clark8032b982017-07-28 11:04:54 -04001744 return true;
1745}
1746
1747bool IncludeParser::parseUnion() {
1748
1749 return true;
1750}
1751
1752bool IncludeParser::parseChar() {
1753 char test = *fChar;
1754 if ('\\' == fPrev) {
1755 if ('\n' == test) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001756// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001757 fLine = fChar + 1;
1758 }
1759 goto done;
1760 }
1761 switch (test) {
1762 case '\n':
Cary Clarkd0530ba2017-09-14 11:25:39 -04001763// ++fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001764 fLine = fChar + 1;
1765 if (fInChar) {
1766 return reportError<bool>("malformed char");
1767 }
1768 if (fInString) {
1769 return reportError<bool>("malformed string");
1770 }
1771 if (!this->checkForWord()) {
1772 return false;
1773 }
1774 if (Bracket::kPound == this->topBracket()) {
1775 KeyWord keyWord = fParent->fKeyWord;
1776 if (KeyWord::kNone == keyWord) {
1777 return this->reportError<bool>("unhandled preprocessor directive");
1778 }
1779 if (KeyWord::kInclude == keyWord || KeyWord::kDefine == keyWord) {
1780 this->popBracket();
1781 }
1782 } else if (Bracket::kSlashSlash == this->topBracket()) {
1783 this->popBracket();
1784 }
1785 break;
1786 case '*':
1787 if (!fInCharCommentString && '/' == fPrev) {
1788 this->pushBracket(Bracket::kSlashStar);
1789 }
1790 if (!this->checkForWord()) {
1791 return false;
1792 }
1793 if (!fInCharCommentString) {
1794 this->addPunctuation(Punctuation::kAsterisk);
1795 }
1796 break;
1797 case '/':
1798 if ('*' == fPrev) {
1799 if (!fInCharCommentString) {
1800 return reportError<bool>("malformed closing comment");
1801 }
1802 if (Bracket::kSlashStar == this->topBracket()) {
Cary Clarka560c472017-11-27 10:44:06 -05001803 TextParser::Save save(this);
1804 this->next(); // include close in bracket
Cary Clark8032b982017-07-28 11:04:54 -04001805 this->popBracket();
Cary Clarka560c472017-11-27 10:44:06 -05001806 save.restore(); // put things back so nothing is skipped
Cary Clark8032b982017-07-28 11:04:54 -04001807 }
1808 break;
Ben Wagner63fd7602017-10-09 15:45:33 -04001809 }
Cary Clark8032b982017-07-28 11:04:54 -04001810 if (!fInCharCommentString && '/' == fPrev) {
1811 this->pushBracket(Bracket::kSlashSlash);
1812 break;
1813 }
1814 if (!this->checkForWord()) {
1815 return false;
1816 }
1817 break;
1818 case '\'':
1819 if (Bracket::kChar == this->topBracket()) {
1820 this->popBracket();
1821 } else if (!fInComment && !fInString) {
1822 if (fIncludeWord) {
1823 return this->reportError<bool>("word then single-quote");
1824 }
1825 this->pushBracket(Bracket::kChar);
1826 }
1827 break;
1828 case '\"':
1829 if (Bracket::kString == this->topBracket()) {
1830 this->popBracket();
1831 } else if (!fInComment && !fInChar) {
1832 if (fIncludeWord) {
1833 return this->reportError<bool>("word then double-quote");
1834 }
1835 this->pushBracket(Bracket::kString);
1836 }
1837 break;
1838 case ':':
1839 case '(':
1840 case '[':
1841 case '{': {
Cary Clark73fa9722017-08-29 17:36:51 -04001842 if (fIncludeWord && '(' == test && fChar - fIncludeWord >= 10 &&
1843 !strncmp("SkDEBUGCODE", fIncludeWord, 10)) {
1844 this->pushBracket(Bracket::kDebugCode);
1845 break;
1846 }
Cary Clark8032b982017-07-28 11:04:54 -04001847 if (fInCharCommentString) {
1848 break;
1849 }
1850 if (':' == test && (fInBrace || ':' == fChar[-1] || ':' == fChar[1])) {
1851 break;
1852 }
1853 if (!fInBrace) {
1854 if (!this->checkForWord()) {
1855 return false;
1856 }
1857 if (':' == test && !fInFunction) {
1858 break;
1859 }
1860 if ('{' == test) {
1861 this->addPunctuation(Punctuation::kLeftBrace);
1862 } else if (':' == test) {
1863 this->addPunctuation(Punctuation::kColon);
1864 }
1865 }
1866 if (fInBrace && '{' == test && Definition::Type::kBracket == fInBrace->fType
1867 && Bracket::kColon == fInBrace->fBracket) {
1868 Definition* braceParent = fParent->fParent;
1869 braceParent->fChildren.pop_back();
1870 braceParent->fTokens.pop_back();
1871 fParent = braceParent;
1872 fInBrace = nullptr;
1873 }
1874 this->pushBracket(
1875 '(' == test ? Bracket::kParen :
1876 '[' == test ? Bracket::kSquare :
1877 '{' == test ? Bracket::kBrace :
1878 Bracket::kColon);
1879 if (!fInBrace
1880 && ('{' == test || (':' == test && ' ' >= fChar[1]))
1881 && fInFunction) {
1882 fInBrace = fParent;
1883 }
1884 } break;
1885 case '<':
1886 if (fInCharCommentString || fInBrace) {
1887 break;
1888 }
1889 if (!this->checkForWord()) {
1890 return false;
1891 }
1892 if (fInEnum) {
1893 break;
1894 }
1895 this->pushBracket(Bracket::kAngle);
1896 break;
1897 case ')':
1898 case ']':
1899 case '}': {
1900 if (fInCharCommentString) {
1901 break;
1902 }
1903 if (!fInBrace) {
1904 if (!this->checkForWord()) {
1905 return false;
1906 }
1907 }
1908 bool popBraceParent = fInBrace == fParent;
1909 if ((')' == test ? Bracket::kParen :
1910 ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
1911 this->popBracket();
1912 if (!fInFunction) {
1913 bool deprecatedMacro = false;
1914 if (')' == test) {
1915 auto iter = fParent->fTokens.end();
1916 bool lookForWord = false;
1917 while (fParent->fTokens.begin() != iter) {
1918 --iter;
1919 if (lookForWord) {
1920 if (Definition::Type::kWord != iter->fType) {
1921 break;
1922 }
1923 string word(iter->fContentStart, iter->length());
1924 if ("SK_ATTR_EXTERNALLY_DEPRECATED" == word) {
1925 deprecatedMacro = true;
1926 // remove macro paren (would confuse method parsing later)
Ben Wagner63fd7602017-10-09 15:45:33 -04001927 fParent->fTokens.pop_back();
Cary Clark8032b982017-07-28 11:04:54 -04001928 fParent->fChildren.pop_back();
1929 }
1930 break;
1931 }
1932 if (Definition::Type::kBracket != iter->fType) {
1933 break;
1934 }
1935 if (Bracket::kParen != iter->fBracket) {
1936 break;
1937 }
1938 lookForWord = true;
1939 }
1940 }
1941 fInFunction = ')' == test && !deprecatedMacro;
1942 } else {
1943 fInFunction = '}' != test;
1944 }
Cary Clark73fa9722017-08-29 17:36:51 -04001945 } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
1946 this->popBracket();
Cary Clark8032b982017-07-28 11:04:54 -04001947 } else {
1948 return reportError<bool>("malformed close bracket");
1949 }
1950 if (popBraceParent) {
1951 Definition* braceParent = fInBrace->fParent;
1952 braceParent->fChildren.pop_back();
1953 braceParent->fTokens.pop_back();
1954 fInBrace = nullptr;
1955 }
1956 } break;
1957 case '>':
1958 if (fInCharCommentString || fInBrace) {
1959 break;
1960 }
1961 if (!this->checkForWord()) {
1962 return false;
1963 }
1964 if (fInEnum) {
1965 break;
1966 }
Cary Clarka560c472017-11-27 10:44:06 -05001967 if (Bracket::kPound == this->topBracket()) {
1968 break;
1969 }
Cary Clark8032b982017-07-28 11:04:54 -04001970 if (Bracket::kAngle == this->topBracket()) {
1971 this->popBracket();
1972 } else {
1973 return reportError<bool>("malformed close angle bracket");
1974 }
1975 break;
1976 case '#': {
1977 if (fInCharCommentString || fInBrace) {
1978 break;
1979 }
1980 SkASSERT(!fIncludeWord); // don't expect this, curious if it is triggered
1981 this->pushBracket(Bracket::kPound);
1982 break;
1983 }
1984 case '&':
1985 case ',':
1986 case ' ':
Cary Clarka560c472017-11-27 10:44:06 -05001987 case '+':
1988 case '=':
1989 case '-':
1990 case '!':
Cary Clark8032b982017-07-28 11:04:54 -04001991 if (fInCharCommentString || fInBrace) {
1992 break;
1993 }
1994 if (!this->checkForWord()) {
1995 return false;
1996 }
1997 break;
1998 case ';':
1999 if (fInCharCommentString || fInBrace) {
2000 break;
2001 }
2002 if (!this->checkForWord()) {
2003 return false;
2004 }
2005 if (Definition::Type::kKeyWord == fParent->fType
2006 && KeyProperty::kObject == (kKeyWords[(int) fParent->fKeyWord].fProperty)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04002007 if (KeyWord::kClass == fParent->fKeyWord && fParent->fParent &&
2008 KeyWord::kEnum == fParent->fParent->fKeyWord) {
2009 this->popObject();
2010 }
Cary Clark8032b982017-07-28 11:04:54 -04002011 if (KeyWord::kEnum == fParent->fKeyWord) {
2012 fInEnum = false;
2013 }
2014 this->popObject();
2015 } else if (Definition::Type::kBracket == fParent->fType
2016 && fParent->fParent && Definition::Type::kKeyWord == fParent->fParent->fType
2017 && KeyWord::kStruct == fParent->fParent->fKeyWord) {
2018 list<Definition>::iterator baseIter = fParent->fTokens.end();
2019 list<Definition>::iterator namedIter = fParent->fTokens.end();
2020 for (auto tokenIter = fParent->fTokens.end();
2021 fParent->fTokens.begin() != tokenIter--; ) {
2022 if (tokenIter->fLineCount == fLineCount) {
2023 if ('f' == tokenIter->fStart[0] && isupper(tokenIter->fStart[1])) {
2024 if (namedIter != fParent->fTokens.end()) {
2025 return reportError<bool>("found two named member tokens");
2026 }
2027 namedIter = tokenIter;
2028 }
2029 baseIter = tokenIter;
2030 } else {
2031 break;
2032 }
2033 }
2034 // FIXME: if a member definition spans multiple lines, this won't work
2035 if (namedIter != fParent->fTokens.end()) {
2036 if (baseIter == namedIter) {
2037 return this->reportError<bool>("expected type before named token");
2038 }
2039 Definition* member = &*namedIter;
2040 member->fMarkType = MarkType::kMember;
Cary Clark9174bda2017-09-19 17:39:32 -04002041 if (!member->fTerminator) {
2042 member->fTerminator = member->fContentEnd;
2043 }
Cary Clark8032b982017-07-28 11:04:54 -04002044 fParent->fChildren.push_back(member);
2045 for (auto nameType = baseIter; nameType != namedIter; ++nameType) {
2046 member->fChildren.push_back(&*nameType);
2047 }
2048
2049 }
2050 } else if (fParent->fChildren.size() > 0) {
2051 auto lastIter = fParent->fChildren.end();
2052 Definition* priorEnum;
2053 while (fParent->fChildren.begin() != lastIter) {
2054 std::advance(lastIter, -1);
2055 priorEnum = *lastIter;
2056 if (Definition::Type::kBracket != priorEnum->fType ||
2057 (Bracket::kSlashSlash != priorEnum->fBracket
2058 && Bracket::kSlashStar != priorEnum->fBracket)) {
2059 break;
2060 }
2061 }
2062 if (Definition::Type::kKeyWord == priorEnum->fType
2063 && KeyWord::kEnum == priorEnum->fKeyWord) {
2064 auto tokenWalker = fParent->fTokens.begin();
2065 std::advance(tokenWalker, priorEnum->fParentIndex);
2066 SkASSERT(KeyWord::kEnum == tokenWalker->fKeyWord);
2067 while (tokenWalker != fParent->fTokens.end()) {
2068 std::advance(tokenWalker, 1);
2069 if (Punctuation::kSemicolon == tokenWalker->fPunctuation) {
2070 break;
2071 }
2072 }
2073 while (tokenWalker != fParent->fTokens.end()) {
2074 std::advance(tokenWalker, 1);
2075 const Definition* test = &*tokenWalker;
2076 if (Definition::Type::kBracket != test->fType ||
2077 (Bracket::kSlashSlash != test->fBracket
2078 && Bracket::kSlashStar != test->fBracket)) {
2079 break;
2080 }
2081 }
2082 Definition* start = &*tokenWalker;
2083 bool foundExpected = true;
2084 for (KeyWord expected : {KeyWord::kStatic, KeyWord::kConstExpr, KeyWord::kInt}){
2085 const Definition* test = &*tokenWalker;
2086 if (expected != test->fKeyWord) {
2087 foundExpected = false;
2088 break;
2089 }
2090 if (tokenWalker == fParent->fTokens.end()) {
2091 break;
2092 }
2093 std::advance(tokenWalker, 1);
2094 }
2095 if (foundExpected && tokenWalker != fParent->fTokens.end()) {
2096 const char* nameStart = tokenWalker->fStart;
2097 std::advance(tokenWalker, 1);
2098 if (tokenWalker != fParent->fTokens.end()) {
2099 TextParser tp(fFileName, nameStart, tokenWalker->fStart, fLineCount);
2100 tp.skipToNonAlphaNum();
2101 start->fName = string(nameStart, tp.fChar - nameStart);
2102 start->fContentEnd = fChar;
2103 priorEnum->fChildren.emplace_back(start);
2104 }
2105 }
2106 }
2107 }
2108 this->addPunctuation(Punctuation::kSemicolon);
2109 fInFunction = false;
2110 break;
2111 case '~':
2112 if (fInEnum) {
2113 break;
2114 }
2115 case '0': case '1': case '2': case '3': case '4':
2116 case '5': case '6': case '7': case '8': case '9':
2117 // TODO: don't want to parse numbers, but do need to track for enum defs
2118 // break;
2119 case 'A': case 'B': case 'C': case 'D': case 'E':
2120 case 'F': case 'G': case 'H': case 'I': case 'J':
2121 case 'K': case 'L': case 'M': case 'N': case 'O':
2122 case 'P': case 'Q': case 'R': case 'S': case 'T':
2123 case 'U': case 'V': case 'W': case 'X': case 'Y':
2124 case 'Z': case '_':
2125 case 'a': case 'b': case 'c': case 'd': case 'e':
2126 case 'f': case 'g': case 'h': case 'i': case 'j':
2127 case 'k': case 'l': case 'm': case 'n': case 'o':
2128 case 'p': case 'q': case 'r': case 's': case 't':
2129 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002130 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002131 if (fInCharCommentString || fInBrace) {
2132 break;
2133 }
2134 if (!fIncludeWord) {
2135 fIncludeWord = fChar;
2136 }
2137 break;
2138 }
2139done:
2140 fPrev = test;
Cary Clarkd0530ba2017-09-14 11:25:39 -04002141 this->next();
Cary Clark8032b982017-07-28 11:04:54 -04002142 return true;
2143}
2144
2145void IncludeParser::validate() const {
2146 for (int index = 0; index <= (int) Last_MarkType; ++index) {
2147 SkASSERT(fMaps[index].fMarkType == (MarkType) index);
2148 }
2149 IncludeParser::ValidateKeyWords();
2150}