blob: 1d95f134532d5429cc0f6792bcfdf0d5372b76bf [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#ifndef bookmaker_DEFINED
9#define bookmaker_DEFINED
10
Cary Clarkce101242017-09-01 15:51:02 -040011#include "SkCommandLineFlags.h"
Cary Clark8032b982017-07-28 11:04:54 -040012#include "SkData.h"
13
14#include <algorithm>
15#include <cmath>
16#include <cctype>
17#include <forward_list>
18#include <list>
19#include <string>
20#include <unordered_map>
21#include <vector>
22
23// std::to_string isn't implemented on android
24#include <sstream>
25
26template <typename T>
27std::string to_string(T value)
28{
29 std::ostringstream os ;
30 os << value ;
31 return os.str() ;
32}
33
34using std::forward_list;
35using std::list;
36using std::unordered_map;
37using std::string;
38using std::vector;
39
40enum class KeyWord {
41 kNone,
Cary Clark73fa9722017-08-29 17:36:51 -040042 kSK_API,
Cary Clark8032b982017-07-28 11:04:54 -040043 kBool,
44 kChar,
45 kClass,
46 kConst,
47 kConstExpr,
48 kDefine,
49 kDouble,
50 kElif,
51 kElse,
52 kEndif,
53 kEnum,
54 kFloat,
55 kFriend,
56 kIf,
57 kIfdef,
58 kIfndef,
59 kInclude,
60 kInline,
61 kInt,
62 kOperator,
63 kPrivate,
64 kProtected,
65 kPublic,
66 kSigned,
67 kSize_t,
68 kStatic,
69 kStruct,
70 kTemplate,
71 kTypedef,
Cary Clarkd0530ba2017-09-14 11:25:39 -040072 kUint16_t,
Cary Clark8032b982017-07-28 11:04:54 -040073 kUint32_t,
Cary Clarkd0530ba2017-09-14 11:25:39 -040074 kUint64_t,
75 kUint8_t,
Cary Clark8032b982017-07-28 11:04:54 -040076 kUnion,
77 kUnsigned,
78 kVoid,
79};
80
81enum class MarkType {
82 kNone,
83 kAnchor,
84 kAlias,
85 kBug,
86 kClass,
87 kCode,
88 kColumn,
89 kComment,
90 kConst,
91 kDefine,
92 kDefinedBy,
93 kDeprecated,
94 kDescription,
95 kDoxygen,
96 kEnum,
97 kEnumClass,
98 kError,
99 kExample,
100 kExperimental,
101 kExternal,
102 kFile,
103 kFormula,
104 kFunction,
105 kHeight,
106 kImage,
107 kLegend,
108 kLink,
109 kList,
110 kMarkChar,
111 kMember,
112 kMethod,
113 kNoExample,
114 kParam,
115 kPlatform,
116 kPrivate,
117 kReturn,
118 kRoot,
119 kRow,
120 kSeeAlso,
121 kStdOut,
122 kStruct,
123 kSubstitute,
124 kSubtopic,
125 kTable,
126 kTemplate,
127 kText,
128 kTime,
129 kToDo,
130 kTopic,
131 kTrack,
132 kTypedef,
133 kUnion,
134 kVolatile,
135 kWidth,
136};
137
138enum {
139 Last_MarkType = (int) MarkType::kWidth,
140};
141
142enum class Bracket {
143 kNone,
144 kParen,
145 kSquare,
146 kBrace,
147 kAngle,
148 kString,
149 kChar,
150 kSlashStar,
151 kSlashSlash,
152 kPound,
153 kColon,
Cary Clark73fa9722017-08-29 17:36:51 -0400154 kDebugCode, // parens get special treatment so SkDEBUGCODE( isn't treated as method
Cary Clark8032b982017-07-28 11:04:54 -0400155};
156
157enum class Punctuation { // catch-all for misc symbols tracked in C
158 kNone,
159 kAsterisk, // for pointer-to
160 kSemicolon, // e.g., to delinate xxx() const ; const int* yyy()
161 kLeftBrace,
162 kColon, // for foo() : bar(1), baz(2) {}
163};
164
Cary Clarkd0530ba2017-09-14 11:25:39 -0400165enum class KeyProperty {
166 kNone,
167 kClassSection,
168 kFunction,
169 kModifier,
170 kNumber,
171 kObject,
172 kPreprocessor,
173};
174
175struct IncludeKey {
176 const char* fName;
177 KeyWord fKeyWord;
178 KeyProperty fProperty;
179};
180
181extern const IncludeKey kKeyWords[];
182
Cary Clark8032b982017-07-28 11:04:54 -0400183static inline bool has_nonwhitespace(const string& s) {
184 bool nonwhite = false;
185 for (const char& c : s) {
186 if (' ' < c) {
187 nonwhite = true;
188 break;
189 }
190 }
191 return nonwhite;
192}
193
194static inline void trim_end(string &s) {
195 s.erase(std::find_if(s.rbegin(), s.rend(),
196 std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
197}
198
199static inline void trim_end_spaces(string &s) {
200 while (s.length() > 0 && ' ' == s.back()) {
201 s.pop_back();
202 }
203}
204
205static inline void trim_start(string &s) {
206 s.erase(s.begin(), std::find_if(s.begin(), s.end(),
207 std::not1(std::ptr_fun<int, int>(std::isspace))));
208}
209
210static inline void trim_start_end(string& s) {
211 trim_start(s);
212 trim_end(s);
213}
214
215class NonAssignable {
216public:
217 NonAssignable(NonAssignable const&) = delete;
218 NonAssignable& operator=(NonAssignable const&) = delete;
219 NonAssignable() {}
220};
221
222class Definition;
223
224class TextParser : public NonAssignable {
225 TextParser() {} // only for ParserCommon to call
226 friend class ParserCommon;
227public:
Cary Clark8032b982017-07-28 11:04:54 -0400228 class Save {
229 public:
230 Save(TextParser* parser) {
231 fParser = parser;
232 fLine = parser->fLine;
233 fChar = parser->fChar;
234 fLineCount = parser->fLineCount;
235 }
236
237 void restore() const {
238 fParser->fLine = fLine;
239 fParser->fChar = fChar;
240 fParser->fLineCount = fLineCount;
241 }
242
243 private:
244 TextParser* fParser;
245 const char* fLine;
246 const char* fChar;
247 int fLineCount;
248 };
249
250 TextParser(const string& fileName, const char* start, const char* end, int lineCount)
251 : fFileName(fileName)
252 , fStart(start)
253 , fLine(start)
254 , fChar(start)
255 , fEnd(end)
256 , fLineCount(lineCount)
257 {
258 }
259
260 TextParser(const Definition* );
261
262 const char* anyOf(const char* str) const {
263 const char* ptr = fChar;
264 while (ptr < fEnd) {
265 if (strchr(str, ptr[0])) {
266 return ptr;
267 }
268 ++ptr;
269 }
270 return nullptr;
271 }
272
273 const char* anyOf(const char* wordStart, const char* wordList[], size_t wordListCount) const {
274 const char** wordPtr = wordList;
275 const char** wordEnd = wordPtr + wordListCount;
276 const size_t matchLen = fChar - wordStart;
277 while (wordPtr < wordEnd) {
278 const char* word = *wordPtr++;
279 if (strlen(word) == matchLen && !strncmp(wordStart, word, matchLen)) {
280 return word;
281 }
282 }
283 return nullptr;
284 }
285
286 char backup(const char* pattern) const {
287 size_t len = strlen(pattern);
288 const char* start = fChar - len;
289 if (start <= fStart) {
290 return '\0';
291 }
292 if (strncmp(start, pattern, len)) {
293 return '\0';
294 }
295 return start[-1];
296 }
297
298 bool contains(const char* match, const char* lineEnd, const char** loc) const {
299 *loc = this->strnstr(match, lineEnd);
300 return *loc;
301 }
302
303 bool eof() const { return fChar >= fEnd; }
304
305 const char* lineEnd() const {
306 const char* ptr = fChar;
307 do {
308 if (ptr >= fEnd) {
309 return ptr;
310 }
311 char test = *ptr++;
312 if (test == '\n' || test == '\0') {
313 break;
314 }
315 } while (true);
316 return ptr;
317 }
318
319 ptrdiff_t lineLength() const {
320 return this->lineEnd() - fLine;
321 }
322
323 bool match(TextParser* );
324
325 char next() {
326 SkASSERT(fChar < fEnd);
327 char result = *fChar++;
328 if ('\n' == result) {
329 ++fLineCount;
330 fLine = fChar;
331 }
332 return result;
333 }
334
335 char peek() const { SkASSERT(fChar < fEnd); return *fChar; }
336
337 void restorePlace(const TextParser& save) {
338 fChar = save.fChar;
339 fLine = save.fLine;
340 fLineCount = save.fLineCount;
341 }
342
343 void savePlace(TextParser* save) {
344 save->fChar = fChar;
345 save->fLine = fLine;
346 save->fLineCount = fLineCount;
347 }
348
349 void reportError(const char* errorStr) const;
350 void reportWarning(const char* errorStr) const;
351
352 template <typename T> T reportError(const char* errorStr) const {
353 this->reportError(errorStr);
354 return T();
355 }
356
357 bool sentenceEnd(const char* check) const {
358 while (check > fStart) {
359 --check;
360 if (' ' < check[0] && '.' != check[0]) {
361 return false;
362 }
363 if ('.' == check[0]) {
364 return ' ' >= check[1];
365 }
366 if ('\n' == check[0] && '\n' == check[1]) {
367 return true;
368 }
369 }
370 return true;
371 }
372
373 bool skipToEndBracket(char endBracket, const char* end = nullptr) {
374 if (nullptr == end) {
375 end = fEnd;
376 }
377 while (fChar[0] != endBracket) {
378 if (fChar >= end) {
379 return false;
380 }
381 (void) this->next();
382 }
383 return true;
384 }
385
386 bool skipToEndBracket(const char* endBracket) {
387 size_t len = strlen(endBracket);
388 while (strncmp(fChar, endBracket, len)) {
389 if (fChar >= fEnd) {
390 return false;
391 }
392 (void) this->next();
393 }
394 return true;
395 }
396
397 bool skipLine() {
398 return skipToEndBracket('\n');
399 }
400
401 void skipTo(const char* skip) {
402 while (fChar < skip) {
403 this->next();
404 }
405 }
406
407 void skipToAlpha() {
408 while (fChar < fEnd && !isalpha(fChar[0])) {
409 fChar++;
410 }
411 }
412
413 void skipToAlphaNum() {
414 while (fChar < fEnd && !isalnum(fChar[0])) {
415 fChar++;
416 }
417 }
418
419 bool skipExact(const char* pattern) {
420 if (!this->startsWith(pattern)) {
421 return false;
422 }
423 this->skipName(pattern);
424 return true;
425 }
426
427 // differs from skipToNonAlphaNum in that a.b isn't considered a full name,
428 // since a.b can't be found as a named definition
429 void skipFullName() {
430 while (fChar < fEnd && (isalnum(fChar[0])
431 || '_' == fChar[0] || '-' == fChar[0]
432 || (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]))) {
433 if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
434 fChar++;
435 }
436 fChar++;
437 }
438 }
439
440 bool skipToLineStart() {
441 if (!this->skipLine()) {
442 return false;
443 }
444 if (!this->eof()) {
445 return this->skipWhiteSpace();
446 }
447 return true;
448 }
449
450 void skipToNonAlphaNum() {
451 while (fChar < fEnd && (isalnum(fChar[0])
452 || '_' == fChar[0] || '-' == fChar[0]
Cary Clarkce101242017-09-01 15:51:02 -0400453 || (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1])
Cary Clark8032b982017-07-28 11:04:54 -0400454 || ('.' == fChar[0] && fChar + 1 < fEnd && isalpha(fChar[1])))) {
455 if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
456 fChar++;
457 }
458 fChar++;
459 }
460 }
461
462 void skipToSpace() {
463 while (fChar < fEnd && ' ' != fChar[0]) {
464 fChar++;
465 }
466 }
467
468 bool skipName(const char* word) {
469 size_t len = strlen(word);
Cary Clarkce101242017-09-01 15:51:02 -0400470 if (len <= (size_t) (fEnd - fChar) && !strncmp(word, fChar, len)) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400471 for (size_t i = 0; i < len; ++i) {
472 this->next();
473 }
Cary Clark8032b982017-07-28 11:04:54 -0400474 }
475 return this->eof() || ' ' >= fChar[0];
476 }
477
478 bool skipSpace() {
479 while (' ' == this->peek()) {
480 (void) this->next();
481 if (fChar >= fEnd) {
482 return false;
483 }
484 }
485 return true;
486 }
487
488 bool skipWord(const char* word) {
489 if (!this->skipWhiteSpace()) {
490 return false;
491 }
492 const char* save = fChar;
493 if (!this->skipName(word)) {
494 fChar = save;
495 return false;
496 }
497 if (!this->skipWhiteSpace()) {
498 return false;
499 }
500 return true;
501 }
502
503 bool skipWhiteSpace() {
504 while (' ' >= this->peek()) {
505 (void) this->next();
506 if (fChar >= fEnd) {
507 return false;
508 }
509 }
510 return true;
511 }
512
513 bool startsWith(const char* str) const {
514 size_t len = strlen(str);
Cary Clarkce101242017-09-01 15:51:02 -0400515 ptrdiff_t lineLen = fEnd - fChar;
Cary Clark8032b982017-07-28 11:04:54 -0400516 return len <= (size_t) lineLen && 0 == strncmp(str, fChar, len);
517 }
518
Cary Clarkbad5ad72017-08-03 17:14:08 -0400519 // ignores minor white space differences
520 bool startsWith(const char* str, size_t oLen) const {
521 size_t tIndex = 0;
522 size_t tLen = fEnd - fChar;
523 size_t oIndex = 0;
524 while (oIndex < oLen && tIndex < tLen) {
525 bool tSpace = ' ' >= fChar[tIndex];
526 bool oSpace = ' ' >= str[oIndex];
527 if (tSpace != oSpace) {
528 break;
529 }
530 if (tSpace) {
531 do {
532 ++tIndex;
533 } while (tIndex < tLen && ' ' >= fChar[tIndex]);
534 do {
535 ++oIndex;
536 } while (oIndex < oLen && ' ' >= str[oIndex]);
537 continue;
538 }
539 if (fChar[tIndex] != str[oIndex]) {
540 break;
541 }
542 ++tIndex;
543 ++oIndex;
544 }
545 return oIndex >= oLen;
546 }
547
Cary Clark8032b982017-07-28 11:04:54 -0400548 const char* strnchr(char ch, const char* end) const {
549 const char* ptr = fChar;
550 while (ptr < end) {
551 if (ptr[0] == ch) {
552 return ptr;
553 }
554 ++ptr;
555 }
556 return nullptr;
557 }
558
559 const char* strnstr(const char *match, const char* end) const {
560 size_t matchLen = strlen(match);
561 SkASSERT(matchLen > 0);
562 ptrdiff_t len = end - fChar;
563 SkASSERT(len >= 0);
564 if ((size_t) len < matchLen ) {
565 return nullptr;
566 }
567 size_t count = len - matchLen;
568 for (size_t index = 0; index <= count; index++) {
569 if (0 == strncmp(&fChar[index], match, matchLen)) {
570 return &fChar[index];
571 }
572 }
573 return nullptr;
574 }
575
Cary Clarkce101242017-09-01 15:51:02 -0400576 const char* trimmedBracketEnd(const char bracket) const {
577 int max = (int) (this->lineLength());
Cary Clark8032b982017-07-28 11:04:54 -0400578 int index = 0;
579 while (index < max && bracket != fChar[index]) {
580 ++index;
581 }
582 SkASSERT(index < max);
583 while (index > 0 && ' ' >= fChar[index - 1]) {
584 --index;
585 }
586 return fChar + index;
587 }
588
589 const char* trimmedLineEnd() const {
590 const char* result = this->lineEnd();
591 while (result > fChar && ' ' >= result[-1]) {
592 --result;
593 }
594 return result;
595 }
596
597 void trimEnd() {
598 while (fEnd > fStart && ' ' >= fEnd[-1]) {
599 --fEnd;
600 }
601 }
602
603 const char* wordEnd() const {
604 const char* end = fChar;
605 while (isalnum(end[0]) || '_' == end[0] || '-' == end[0]) {
606 ++end;
607 }
608 return end;
609 }
610
611 string fFileName;
612 const char* fStart;
613 const char* fLine;
614 const char* fChar;
615 const char* fEnd;
616 size_t fLineCount;
617};
618
619class EscapeParser : public TextParser {
620public:
621 EscapeParser(const char* start, const char* end) :
622 TextParser("", start, end, 0) {
623 const char* reader = fStart;
624 fStorage = new char[end - start];
625 char* writer = fStorage;
626 while (reader < fEnd) {
627 char ch = *reader++;
628 if (ch != '\\') {
629 *writer++ = ch;
630 } else {
631 char ctrl = *reader++;
632 if (ctrl == 'u') {
633 unsigned unicode = 0;
634 for (int i = 0; i < 4; ++i) {
635 unicode <<= 4;
636 SkASSERT((reader[0] >= '0' && reader[0] <= '9') ||
637 (reader[0] >= 'A' && reader[0] <= 'F'));
638 int nibble = *reader++ - '0';
639 if (nibble > 9) {
640 nibble = 'A'- '9' + 1;
641 }
642 unicode |= nibble;
643 }
644 SkASSERT(unicode < 256);
645 *writer++ = (unsigned char) unicode;
646 } else {
647 SkASSERT(ctrl == 'n');
648 *writer++ = '\n';
649 }
650 }
651 }
652 fStart = fLine = fChar = fStorage;
653 fEnd = writer;
654 }
655
656 virtual ~EscapeParser() {
657 delete fStorage;
658 }
659private:
660 char* fStorage;
661};
662
663class RootDefinition;
664
665class Definition : public NonAssignable {
666public:
667 enum Type {
668 kNone,
669 kWord,
670 kMark,
671 kKeyWord,
672 kBracket,
673 kPunctuation,
674 kFileType,
675 };
676
677 enum class TrimExtract {
678 kNo,
679 kYes
680 };
681
682 enum class MethodType {
683 kNone,
684 kConstructor,
685 kDestructor,
686 kOperator,
687 };
688
689 Definition() {}
690
691 Definition(const char* start, const char* end, int line, Definition* parent)
692 : fStart(start)
693 , fContentStart(start)
694 , fContentEnd(end)
695 , fParent(parent)
696 , fLineCount(line)
697 , fType(Type::kWord) {
698 if (parent) {
699 SkASSERT(parent->fFileName.length() > 0);
700 fFileName = parent->fFileName;
701 }
702 this->setParentIndex();
703 }
704
705 Definition(MarkType markType, const char* start, int line, Definition* parent)
706 : Definition(markType, start, nullptr, line, parent) {
707 }
708
709 Definition(MarkType markType, const char* start, const char* end, int line, Definition* parent)
710 : Definition(start, end, line, parent) {
711 fMarkType = markType;
712 fType = Type::kMark;
713 }
714
715 Definition(Bracket bracket, const char* start, int lineCount, Definition* parent)
716 : Definition(start, nullptr, lineCount, parent) {
717 fBracket = bracket;
718 fType = Type::kBracket;
719 }
720
721 Definition(KeyWord keyWord, const char* start, const char* end, int lineCount,
722 Definition* parent)
723 : Definition(start, end, lineCount, parent) {
724 fKeyWord = keyWord;
725 fType = Type::kKeyWord;
726 }
727
728 Definition(Punctuation punctuation, const char* start, int lineCount, Definition* parent)
729 : Definition(start, nullptr, lineCount, parent) {
730 fPunctuation = punctuation;
731 fType = Type::kPunctuation;
732 }
733
734 virtual ~Definition() {}
735
736 virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; }
737 virtual const RootDefinition* asRoot() const { SkASSERT(0); return nullptr; }
738
739 bool boilerplateIfDef(Definition* parent) {
740 const Definition& label = fTokens.front();
741 if (Type::kWord != label.fType) {
742 return false;
743 }
744 fName = string(label.fContentStart, label.fContentEnd - label.fContentStart);
745 return true;
746 }
747
748 // todo: this is matching #ifndef SkXXX_DEFINED for no particular reason
749 // it doesn't do anything useful with arbitrary input, e.g. #ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS
750// also doesn't know what to do with SK_REQUIRE_LOCAL_VAR()
751 bool boilerplateDef(Definition* parent) {
752 if (!this->boilerplateIfDef(parent)) {
753 return false;
754 }
755 const char* s = fName.c_str();
756 const char* e = strchr(s, '_');
757 return true; // fixme: if this is trying to do something useful with define, do it here
758 if (!e) {
759 return false;
760 }
761 string prefix(s, e - s);
762 const char* inName = strstr(parent->fName.c_str(), prefix.c_str());
763 if (!inName) {
764 return false;
765 }
766 if ('/' != inName[-1] && '\\' != inName[-1]) {
767 return false;
768 }
769 if (strcmp(inName + prefix.size(), ".h")) {
770 return false;
771 }
772 return true;
773 }
774
775 bool boilerplateEndIf() {
776 return true;
777 }
778
779 bool checkMethod() const;
780
781 void setCanonicalFiddle();
Cary Clark73fa9722017-08-29 17:36:51 -0400782 bool crossCheck2(const Definition& includeToken) const;
Cary Clark8032b982017-07-28 11:04:54 -0400783 bool crossCheck(const Definition& includeToken) const;
784 bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const;
785 bool exampleToScript(string* result) const;
786
787 string extractText(TrimExtract trimExtract) const {
788 string result;
789 TextParser parser(fFileName, fContentStart, fContentEnd, fLineCount);
790 int childIndex = 0;
791 char mc = '#';
792 while (parser.fChar < parser.fEnd) {
793 if (TrimExtract::kYes == trimExtract && !parser.skipWhiteSpace()) {
794 break;
795 }
796 if (parser.next() == mc) {
797 if (parser.next() == mc) {
798 if (parser.next() == mc) {
799 mc = parser.next();
800 }
801 } else {
802 // fixme : more work to do if # style comment is in text
803 // if in method definition, could be alternate method name
804 --parser.fChar;
805 if (' ' < parser.fChar[0]) {
806 if (islower(parser.fChar[0])) {
807 result += '\n';
808 parser.skipLine();
809 } else {
810 SkASSERT(isupper(parser.fChar[0]));
811 parser.skipTo(fChildren[childIndex]->fTerminator);
812 if (mc == parser.fChar[0] && mc == parser.fChar[1]) {
813 parser.next();
814 parser.next();
815 }
816 childIndex++;
817 }
818 } else {
819 parser.skipLine();
820 }
821 continue;
822 }
823 } else {
824 --parser.fChar;
825 }
826 const char* end = parser.fEnd;
827 const char* mark = parser.strnchr(mc, end);
828 if (mark) {
829 end = mark;
830 }
831 string fragment(parser.fChar, end - parser.fChar);
832 trim_end(fragment);
833 if (TrimExtract::kYes == trimExtract) {
834 trim_start(fragment);
835 if (result.length()) {
836 result += '\n';
837 result += '\n';
838 }
839 }
840 if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) {
841 result += fragment;
842 }
843 parser.skipTo(end);
844 }
845 return result;
846 }
847
848 string fiddleName() const;
849 string formatFunction() const;
850 const Definition* hasChild(MarkType markType) const;
851 const Definition* hasParam(const string& ref) const;
852 bool isClone() const { return fClone; }
853
854 Definition* iRootParent() {
855 Definition* test = fParent;
856 while (test) {
857 if (Type::kKeyWord == test->fType && KeyWord::kClass == test->fKeyWord) {
858 return test;
859 }
860 test = test->fParent;
861 }
862 return nullptr;
863 }
864
865 virtual bool isRoot() const { return false; }
866
867 int length() const {
868 return (int) (fContentEnd - fContentStart);
869 }
870
871 bool methodHasReturn(const string& name, TextParser* methodParser) const;
872 string methodName() const;
873 bool nextMethodParam(TextParser* methodParser, const char** nextEndPtr,
874 string* paramName) const;
875 bool paramsMatch(const string& fullRef, const string& name) const;
876
877 string printableName() const {
878 string result(fName);
879 std::replace(result.begin(), result.end(), '_', ' ');
880 return result;
881 }
882
883 virtual RootDefinition* rootParent() { SkASSERT(0); return nullptr; }
884
885 void setParentIndex() {
886 fParentIndex = fParent ? (int) fParent->fTokens.size() : -1;
887 }
888
889 string fText; // if text is constructed instead of in a file, it's put here
890 const char* fStart = nullptr; // .. in original text file, or the start of fText
891 const char* fContentStart; // start past optional markup name
892 string fName;
893 string fFiddle; // if its a constructor or operator, fiddle name goes here
894 const char* fContentEnd = nullptr; // the end of the contained text
895 const char* fTerminator = nullptr; // the end of the markup, normally ##\n or \n
896 Definition* fParent = nullptr;
897 list<Definition> fTokens;
898 vector<Definition*> fChildren;
899 string fHash; // generated by fiddle
900 string fFileName;
901 size_t fLineCount = 0;
902 int fParentIndex = 0;
903 MarkType fMarkType = MarkType::kNone;
904 KeyWord fKeyWord = KeyWord::kNone;
905 Bracket fBracket = Bracket::kNone;
906 Punctuation fPunctuation = Punctuation::kNone;
907 MethodType fMethodType = MethodType::kNone;
908 Type fType = Type::kNone;
909 bool fClone = false;
910 bool fCloned = false;
911 bool fPrivate = false;
912 bool fShort = false;
913 bool fMemberStart = false;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400914 bool fAnonymous = false;
Cary Clark8032b982017-07-28 11:04:54 -0400915 mutable bool fVisited = false;
916};
917
918class RootDefinition : public Definition {
919public:
Cary Clarkce101242017-09-01 15:51:02 -0400920 enum class AllowParens {
921 kNo,
922 kYes,
923 };
924
Cary Clark8032b982017-07-28 11:04:54 -0400925 RootDefinition() {
926 }
927
928 RootDefinition(MarkType markType, const char* start, int line, Definition* parent)
929 : Definition(markType, start, line, parent) {
930 }
931
932 RootDefinition(MarkType markType, const char* start, const char* end, int line,
933 Definition* parent) : Definition(markType, start, end, line, parent) {
934 }
935
936 ~RootDefinition() override {
937 for (auto& iter : fBranches) {
938 delete iter.second;
939 }
940 }
941
942 RootDefinition* asRoot() override { return this; }
943 const RootDefinition* asRoot() const override { return this; }
944 void clearVisited();
945 bool dumpUnVisited();
Cary Clarkce101242017-09-01 15:51:02 -0400946 const Definition* find(const string& ref, AllowParens ) const;
Cary Clark8032b982017-07-28 11:04:54 -0400947 bool isRoot() const override { return true; }
948 RootDefinition* rootParent() override { return fRootParent; }
949 void setRootParent(RootDefinition* rootParent) { fRootParent = rootParent; }
950
951 unordered_map<string, RootDefinition*> fBranches;
952 unordered_map<string, Definition> fLeaves;
953private:
954 RootDefinition* fRootParent = nullptr;
955};
956
957struct IClassDefinition : public Definition {
958 unordered_map<string, Definition*> fEnums;
959 unordered_map<string, Definition*> fMembers;
960 unordered_map<string, Definition*> fMethods;
961 unordered_map<string, Definition*> fStructs;
962};
963
964struct Reference {
965 Reference()
966 : fLocation(nullptr)
967 , fDefinition(nullptr) {
968 }
969
970 const char* fLocation; // .. in original text file
971 const Definition* fDefinition;
972};
973
974struct TypeNames {
975 const char* fName;
976 MarkType fMarkType;
977};
978
979class ParserCommon : public TextParser {
980public:
981
982 ParserCommon() : TextParser()
983 , fParent(nullptr)
Cary Clarkd0530ba2017-09-14 11:25:39 -0400984 , fDebugOut(false)
Cary Clark8032b982017-07-28 11:04:54 -0400985 {
986 }
987
988 virtual ~ParserCommon() {
989 }
990
991 void addDefinition(Definition* def) {
992 fParent->fChildren.push_back(def);
993 fParent = def;
994 }
995
996 void indentToColumn(int column) {
997 SkASSERT(column >= fColumn);
Cary Clarkd0530ba2017-09-14 11:25:39 -0400998 if (fDebugOut) {
999 SkDebugf("%*s", column - fColumn, "");
1000 }
Cary Clark8032b982017-07-28 11:04:54 -04001001 fprintf(fOut, "%*s", column - fColumn, "");
1002 fColumn = column;
1003 fSpaces += column - fColumn;
1004 }
1005
1006 bool leadingPunctuation(const char* str, size_t len) const {
1007 if (!fPendingSpace) {
1008 return false;
1009 }
1010 if (len < 2) {
1011 return false;
1012 }
1013 if ('.' != str[0] && ',' != str[0] && ';' != str[0] && ':' != str[0]) {
1014 return false;
1015 }
1016 return ' ' >= str[1];
1017 }
1018
1019 void lf(int count) {
1020 fPendingLF = SkTMax(fPendingLF, count);
1021 this->nl();
1022 }
1023
1024 void lfAlways(int count) {
1025 this->lf(count);
1026 this->writePending();
1027 }
1028
1029 void lfcr() {
1030 this->lf(1);
1031 }
1032
1033 void nl() {
1034 fLinefeeds = 0;
1035 fSpaces = 0;
1036 fColumn = 0;
1037 fPendingSpace = false;
1038 }
1039
1040 bool parseFile(const char* file, const char* suffix);
1041 virtual bool parseFromFile(const char* path) = 0;
1042 bool parseSetup(const char* path);
1043
1044 void popObject() {
1045 fParent->fContentEnd = fParent->fTerminator = fChar;
1046 fParent = fParent->fParent;
1047 }
1048
1049 virtual void reset() = 0;
1050
1051 void resetCommon() {
1052 fLine = fChar = fStart;
1053 fLineCount = 0;
1054 fParent = nullptr;
1055 fIndent = 0;
1056 fOut = nullptr;
1057 fMaxLF = 2;
1058 fPendingLF = 0;
1059 fPendingSpace = false;
1060 nl();
1061 }
1062
1063 void setAsParent(Definition* definition) {
1064 if (fParent) {
1065 fParent->fChildren.push_back(definition);
1066 definition->fParent = fParent;
1067 }
1068 fParent = definition;
1069 }
1070
1071 void singleLF() {
1072 fMaxLF = 1;
1073 }
1074
1075 bool writeBlockTrim(int size, const char* data) {
1076 while (size && ' ' >= data[0]) {
1077 ++data;
1078 --size;
1079 }
1080 while (size && ' ' >= data[size - 1]) {
1081 --size;
1082 }
1083 if (size <= 0) {
1084 return false;
1085 }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001086 SkASSERT(size < 16000);
Cary Clark8032b982017-07-28 11:04:54 -04001087 if (size > 3 && !strncmp("#end", data, 4)) {
1088 fMaxLF = 1;
1089 }
1090 if (this->leadingPunctuation(data, (size_t) size)) {
1091 fPendingSpace = false;
1092 }
1093 writePending();
Cary Clarkd0530ba2017-09-14 11:25:39 -04001094 if (fDebugOut) {
1095 if (!strncmp("SK_SUPPORT", data, 10)) {
1096 SkDebugf("");
1097 }
1098 string check(data, size);
1099 SkDebugf("%s", check.c_str());
1100 }
Cary Clark8032b982017-07-28 11:04:54 -04001101 fprintf(fOut, "%.*s", size, data);
1102 int added = 0;
1103 while (size > 0 && '\n' != data[--size]) {
1104 ++added;
1105 }
1106 fColumn = size ? added : fColumn + added;
1107 fSpaces = 0;
1108 fLinefeeds = 0;
1109 fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2;
1110 return true;
1111 }
1112
1113 void writeBlock(int size, const char* data) {
1114 SkAssertResult(writeBlockTrim(size, data));
1115 }
1116 void writeCommentHeader() {
1117 this->lf(2);
1118 this->writeString("/**");
1119 this->writeSpace();
1120 }
1121
1122 void writeCommentTrailer() {
1123 this->writeString("*/");
1124 this->lfcr();
1125 }
1126
1127 // write a pending space, so that two consecutive calls
1128 // don't double write, and trailing spaces on lines aren't written
1129 void writeSpace() {
1130 SkASSERT(!fPendingLF);
1131 SkASSERT(!fLinefeeds);
1132 SkASSERT(fColumn > 0);
1133 SkASSERT(!fSpaces);
1134 fPendingSpace = true;
1135 }
1136
1137 void writeString(const char* str) {
1138 SkASSERT(strlen(str) > 0);
1139 SkASSERT(' ' < str[0]);
1140 SkASSERT(' ' < str[strlen(str) - 1]);
1141 if (this->leadingPunctuation(str, strlen(str))) {
1142 fPendingSpace = false;
1143 }
1144 writePending();
Cary Clarkd0530ba2017-09-14 11:25:39 -04001145 if (fDebugOut) {
1146 if (!strncmp("SK_SUPPORT", str, 10)) {
1147 SkDebugf("");
1148 }
1149 SkDebugf("%s", str);
1150 }
Cary Clark8032b982017-07-28 11:04:54 -04001151 SkASSERT(!strchr(str, '\n'));
1152 fprintf(fOut, "%s", str);
1153 fColumn += strlen(str);
1154 fSpaces = 0;
1155 fLinefeeds = 0;
1156 fMaxLF = 2;
1157 }
1158
1159 void writePending() {
1160 fPendingLF = SkTMin(fPendingLF, fMaxLF);
1161 bool wroteLF = false;
1162 while (fLinefeeds < fPendingLF) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001163 if (fDebugOut) {
1164 SkDebugf("\n");
1165 }
Cary Clark8032b982017-07-28 11:04:54 -04001166 fprintf(fOut, "\n");
1167 ++fLinefeeds;
1168 wroteLF = true;
1169 }
1170 fPendingLF = 0;
1171 if (wroteLF) {
1172 SkASSERT(0 == fColumn);
1173 SkASSERT(fIndent >= fSpaces);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001174 if (fDebugOut) {
1175 SkDebugf("%*s", fIndent - fSpaces, "");
1176 }
Cary Clark8032b982017-07-28 11:04:54 -04001177 fprintf(fOut, "%*s", fIndent - fSpaces, "");
1178 fColumn = fIndent;
1179 fSpaces = fIndent;
1180 }
1181 if (fPendingSpace) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001182 if (fDebugOut) {
1183 SkDebugf(" ");
1184 }
Cary Clark8032b982017-07-28 11:04:54 -04001185 fprintf(fOut, " ");
1186 ++fColumn;
1187 fPendingSpace = false;
1188 }
1189 }
1190
1191 unordered_map<string, sk_sp<SkData>> fRawData;
1192 unordered_map<string, vector<char>> fLFOnly;
1193 Definition* fParent;
1194 FILE* fOut;
1195 int fLinefeeds; // number of linefeeds last written, zeroed on non-space
1196 int fMaxLF; // number of linefeeds allowed
1197 int fPendingLF; // number of linefeeds to write (can be suppressed)
1198 int fSpaces; // number of spaces (indent) last written, zeroed on non-space
1199 int fColumn; // current column; number of chars past last linefeed
1200 int fIndent; // desired indention
1201 bool fPendingSpace; // a space should preceed the next string or block
Cary Clarkd0530ba2017-09-14 11:25:39 -04001202 bool fDebugOut; // set true to write to std out
Cary Clark8032b982017-07-28 11:04:54 -04001203private:
1204 typedef TextParser INHERITED;
1205};
1206
1207
1208
1209class BmhParser : public ParserCommon {
1210public:
1211 enum class MarkLookup {
1212 kRequire,
1213 kAllowUnknown,
1214 };
1215
1216 enum class Resolvable {
1217 kNo, // neither resolved nor output
1218 kYes, // resolved, output
1219 kOut, // not resolved, but output
1220 };
1221
1222 enum class Exemplary {
1223 kNo,
1224 kYes,
1225 kOptional,
1226 };
1227
Cary Clarkce101242017-09-01 15:51:02 -04001228 enum class TableState {
1229 kNone,
1230 kColumnStart,
1231 kColumnEnd,
1232 };
1233
Cary Clark8032b982017-07-28 11:04:54 -04001234#define M(mt) (1LL << (int) MarkType::k##mt)
1235#define M_D M(Description)
1236#define M_CS M(Class) | M(Struct)
1237#define M_ST M(Subtopic) | M(Topic)
1238#define M_CSST M_CS | M_ST
1239#ifdef M_E
1240#undef M_E
1241#endif
1242#define M_E M(Enum) | M(EnumClass)
1243
1244#define R_Y Resolvable::kYes
1245#define R_N Resolvable::kNo
1246#define R_O Resolvable::kOut
1247
1248#define E_Y Exemplary::kYes
1249#define E_N Exemplary::kNo
1250#define E_O Exemplary::kOptional
1251
1252 BmhParser() : ParserCommon()
1253 , fMaps {
1254// names without formal definitions (e.g. Column) aren't included
1255// fill in other names once they're actually used
Cary Clarkce101242017-09-01 15:51:02 -04001256 { "", nullptr, MarkType::kNone, R_Y, E_N, 0 }
1257, { "A", nullptr, MarkType::kAnchor, R_Y, E_N, 0 }
1258, { "Alias", nullptr, MarkType::kAlias, R_N, E_N, 0 }
1259, { "Bug", nullptr, MarkType::kBug, R_N, E_N, 0 }
1260, { "Class", &fClassMap, MarkType::kClass, R_Y, E_O, M_CSST | M(Root) }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001261, { "Code", nullptr, MarkType::kCode, R_O, E_N, M_CSST | M_E }
Cary Clarkce101242017-09-01 15:51:02 -04001262, { "", nullptr, MarkType::kColumn, R_Y, E_N, M(Row) }
1263, { "", nullptr, MarkType::kComment, R_N, E_N, 0 }
1264, { "Const", &fConstMap, MarkType::kConst, R_Y, E_N, M_E | M_ST }
1265, { "Define", nullptr, MarkType::kDefine, R_O, E_N, M_ST }
1266, { "DefinedBy", nullptr, MarkType::kDefinedBy, R_N, E_N, M(Method) }
1267, { "Deprecated", nullptr, MarkType::kDeprecated, R_Y, E_N, 0 }
1268, { "Description", nullptr, MarkType::kDescription, R_Y, E_N, M(Example) }
1269, { "Doxygen", nullptr, MarkType::kDoxygen, R_Y, E_N, 0 }
1270, { "Enum", &fEnumMap, MarkType::kEnum, R_Y, E_O, M_CSST | M(Root) }
1271, { "EnumClass", &fClassMap, MarkType::kEnumClass, R_Y, E_O, M_CSST | M(Root) }
1272, { "Error", nullptr, MarkType::kError, R_N, E_N, M(Example) }
1273, { "Example", nullptr, MarkType::kExample, R_O, E_N, M_CSST | M_E | M(Method) }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001274, { "Experimental", nullptr, MarkType::kExperimental, R_Y, E_N, 0 }
Cary Clarkce101242017-09-01 15:51:02 -04001275, { "External", nullptr, MarkType::kExternal, R_Y, E_N, M(Root) }
1276, { "File", nullptr, MarkType::kFile, R_N, E_N, M(Track) }
1277, { "Formula", nullptr, MarkType::kFormula, R_O, E_N,
1278 M(Column) | M_ST | M(Member) | M(Method) | M_D }
1279, { "Function", nullptr, MarkType::kFunction, R_O, E_N, M(Example) }
1280, { "Height", nullptr, MarkType::kHeight, R_N, E_N, M(Example) }
1281, { "Image", nullptr, MarkType::kImage, R_N, E_N, M(Example) }
1282, { "Legend", nullptr, MarkType::kLegend, R_Y, E_N, M(Table) }
1283, { "", nullptr, MarkType::kLink, R_N, E_N, M(Anchor) }
1284, { "List", nullptr, MarkType::kList, R_Y, E_N, M(Method) | M_CSST | M_E | M_D }
1285, { "", nullptr, MarkType::kMarkChar, R_N, E_N, 0 }
1286, { "Member", nullptr, MarkType::kMember, R_Y, E_N, M(Class) | M(Struct) }
1287, { "Method", &fMethodMap, MarkType::kMethod, R_Y, E_Y, M_CSST }
1288, { "NoExample", nullptr, MarkType::kNoExample, R_Y, E_N, 0 }
1289, { "Param", nullptr, MarkType::kParam, R_Y, E_N, M(Method) }
1290, { "Platform", nullptr, MarkType::kPlatform, R_N, E_N, M(Example) }
1291, { "Private", nullptr, MarkType::kPrivate, R_N, E_N, 0 }
1292, { "Return", nullptr, MarkType::kReturn, R_Y, E_N, M(Method) }
1293, { "", nullptr, MarkType::kRoot, R_Y, E_N, 0 }
1294, { "", nullptr, MarkType::kRow, R_Y, E_N, M(Table) | M(List) }
1295, { "SeeAlso", nullptr, MarkType::kSeeAlso, R_Y, E_N, M_CSST | M_E | M(Method) }
1296, { "StdOut", nullptr, MarkType::kStdOut, R_N, E_N, M(Example) }
1297, { "Struct", &fClassMap, MarkType::kStruct, R_Y, E_O, M(Class) | M(Root) | M_ST }
1298, { "Substitute", nullptr, MarkType::kSubstitute, R_N, E_N, M_ST }
1299, { "Subtopic", nullptr, MarkType::kSubtopic, R_Y, E_Y, M_CSST }
1300, { "Table", nullptr, MarkType::kTable, R_Y, E_N, M(Method) | M_CSST | M_E }
1301, { "Template", nullptr, MarkType::kTemplate, R_Y, E_N, 0 }
1302, { "", nullptr, MarkType::kText, R_Y, E_N, 0 }
1303, { "Time", nullptr, MarkType::kTime, R_Y, E_N, M(Track) }
1304, { "ToDo", nullptr, MarkType::kToDo, R_N, E_N, 0 }
1305, { "Topic", nullptr, MarkType::kTopic, R_Y, E_Y, M_CS | M(Root) | M(Topic) }
1306, { "Track", nullptr, MarkType::kTrack, R_Y, E_N, M_E | M_ST }
1307, { "Typedef", &fTypedefMap, MarkType::kTypedef, R_Y, E_N, M(Subtopic) | M(Topic) }
1308, { "", nullptr, MarkType::kUnion, R_Y, E_N, 0 }
1309, { "Volatile", nullptr, MarkType::kVolatile, R_N, E_N, M(StdOut) }
1310, { "Width", nullptr, MarkType::kWidth, R_N, E_N, M(Example) } }
Cary Clark8032b982017-07-28 11:04:54 -04001311 {
1312 this->reset();
1313 }
1314
1315#undef R_O
1316#undef R_N
1317#undef R_Y
1318
1319#undef M_E
1320#undef M_CSST
1321#undef M_ST
1322#undef M_CS
1323#undef M_D
1324#undef M
1325
1326 ~BmhParser() override {}
1327
1328 bool addDefinition(const char* defStart, bool hasEnd, MarkType markType,
1329 const vector<string>& typeNameBuilder);
Cary Clark73fa9722017-08-29 17:36:51 -04001330 bool checkExamples() const;
Cary Clarka523d2d2017-08-30 08:58:10 -04001331 bool checkParamReturn(const Definition* definition) const;
Cary Clark73fa9722017-08-29 17:36:51 -04001332 bool dumpExamples(const char* fiddleJsonFileName) const;
Cary Clark8032b982017-07-28 11:04:54 -04001333 bool childOf(MarkType markType) const;
1334 string className(MarkType markType);
1335 bool collectExternals();
1336 int endHashCount() const;
Cary Clarkce101242017-09-01 15:51:02 -04001337 bool endTableColumn(const char* end, const char* terminator);
Cary Clark8032b982017-07-28 11:04:54 -04001338
1339 RootDefinition* findBmhObject(MarkType markType, const string& typeName) {
1340 auto map = fMaps[(int) markType].fBmh;
1341 if (!map) {
1342 return nullptr;
1343 }
1344 return &(*map)[typeName];
1345 }
1346
1347 bool findDefinitions();
1348 MarkType getMarkType(MarkLookup lookup) const;
1349 bool hasEndToken() const;
1350 string memberName();
1351 string methodName();
1352 const Definition* parentSpace() const;
1353
1354 bool parseFromFile(const char* path) override {
1355 if (!INHERITED::parseSetup(path)) {
1356 return false;
1357 }
1358 fCheckMethods = !strstr(path, "undocumented.bmh");
1359 return findDefinitions();
1360 }
1361
1362 bool popParentStack(Definition* definition);
Cary Clark73fa9722017-08-29 17:36:51 -04001363 void reportDuplicates(const Definition& def, const string& dup) const;
Cary Clark8032b982017-07-28 11:04:54 -04001364
1365 void reset() override {
1366 INHERITED::resetCommon();
1367 fRoot = nullptr;
Cary Clarkce101242017-09-01 15:51:02 -04001368 fWorkingColumn = nullptr;
1369 fRow = nullptr;
1370 fTableState = TableState::kNone;
Cary Clark8032b982017-07-28 11:04:54 -04001371 fMC = '#';
1372 fInChar = false;
1373 fInCharCommentString = false;
1374 fInComment = false;
1375 fInEnum = false;
1376 fInString = false;
1377 fCheckMethods = false;
1378 }
1379
1380 bool skipNoName();
1381 bool skipToDefinitionEnd(MarkType markType);
Cary Clarkce101242017-09-01 15:51:02 -04001382 void spellCheck(const char* match, SkCommandLineFlags::StringArray report) const;
Cary Clark8032b982017-07-28 11:04:54 -04001383 vector<string> topicName();
1384 vector<string> typeName(MarkType markType, bool* expectEnd);
1385 string uniqueName(const string& base, MarkType markType);
1386 string uniqueRootName(const string& base, MarkType markType);
1387 void validate() const;
1388 string word(const string& prefix, const string& delimiter);
1389
1390public:
1391 struct DefinitionMap {
1392 const char* fName;
1393 unordered_map<string, RootDefinition>* fBmh;
1394 MarkType fMarkType;
1395 Resolvable fResolve;
1396 Exemplary fExemplary; // worthy of an example
1397 uint64_t fParentMask;
1398 };
1399
1400 DefinitionMap fMaps[Last_MarkType + 1];
1401 forward_list<RootDefinition> fTopics;
1402 forward_list<Definition> fMarkup;
1403 forward_list<RootDefinition> fExternals;
1404 vector<string> fInputFiles;
1405 unordered_map<string, RootDefinition> fClassMap;
1406 unordered_map<string, RootDefinition> fConstMap;
1407 unordered_map<string, RootDefinition> fEnumMap;
1408 unordered_map<string, RootDefinition> fMethodMap;
1409 unordered_map<string, RootDefinition> fTypedefMap;
1410 unordered_map<string, Definition*> fTopicMap;
1411 unordered_map<string, Definition*> fAliasMap;
1412 RootDefinition* fRoot;
Cary Clarkce101242017-09-01 15:51:02 -04001413 Definition* fWorkingColumn;
1414 Definition* fRow;
1415 const char* fColStart;
1416 TableState fTableState;
Cary Clark8032b982017-07-28 11:04:54 -04001417 mutable char fMC; // markup character
1418 bool fAnonymous;
1419 bool fCloned;
1420 bool fInChar;
1421 bool fInCharCommentString;
1422 bool fInEnum;
1423 bool fInComment;
1424 bool fInString;
1425 bool fCheckMethods;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001426 bool fWroteOut = false;
Cary Clark8032b982017-07-28 11:04:54 -04001427private:
1428 typedef ParserCommon INHERITED;
1429};
1430
1431class IncludeParser : public ParserCommon {
1432public:
1433 enum class IsStruct {
1434 kNo,
1435 kYes,
1436 };
1437
1438 IncludeParser() : ParserCommon()
1439 , fMaps {
1440 { nullptr, MarkType::kNone }
1441 , { nullptr, MarkType::kAnchor }
1442 , { nullptr, MarkType::kAlias }
1443 , { nullptr, MarkType::kBug }
1444 , { nullptr, MarkType::kClass }
1445 , { nullptr, MarkType::kCode }
1446 , { nullptr, MarkType::kColumn }
1447 , { nullptr, MarkType::kComment }
1448 , { nullptr, MarkType::kConst }
1449 , { &fIDefineMap, MarkType::kDefine }
1450 , { nullptr, MarkType::kDefinedBy }
1451 , { nullptr, MarkType::kDeprecated }
1452 , { nullptr, MarkType::kDescription }
1453 , { nullptr, MarkType::kDoxygen }
1454 , { &fIEnumMap, MarkType::kEnum }
1455 , { &fIEnumMap, MarkType::kEnumClass }
1456 , { nullptr, MarkType::kError }
1457 , { nullptr, MarkType::kExample }
1458 , { nullptr, MarkType::kExperimental }
1459 , { nullptr, MarkType::kExternal }
1460 , { nullptr, MarkType::kFile }
1461 , { nullptr, MarkType::kFormula }
1462 , { nullptr, MarkType::kFunction }
1463 , { nullptr, MarkType::kHeight }
1464 , { nullptr, MarkType::kImage }
1465 , { nullptr, MarkType::kLegend }
1466 , { nullptr, MarkType::kLink }
1467 , { nullptr, MarkType::kList }
1468 , { nullptr, MarkType::kMarkChar }
1469 , { nullptr, MarkType::kMember }
1470 , { nullptr, MarkType::kMethod }
1471 , { nullptr, MarkType::kNoExample }
1472 , { nullptr, MarkType::kParam }
1473 , { nullptr, MarkType::kPlatform }
1474 , { nullptr, MarkType::kPrivate }
1475 , { nullptr, MarkType::kReturn }
1476 , { nullptr, MarkType::kRoot }
1477 , { nullptr, MarkType::kRow }
1478 , { nullptr, MarkType::kSeeAlso }
1479 , { nullptr, MarkType::kStdOut }
1480 , { &fIStructMap, MarkType::kStruct }
1481 , { nullptr, MarkType::kSubstitute }
1482 , { nullptr, MarkType::kSubtopic }
1483 , { nullptr, MarkType::kTable }
1484 , { &fITemplateMap, MarkType::kTemplate }
1485 , { nullptr, MarkType::kText }
1486 , { nullptr, MarkType::kTime }
1487 , { nullptr, MarkType::kToDo }
1488 , { nullptr, MarkType::kTopic }
1489 , { nullptr, MarkType::kTrack }
1490 , { &fITypedefMap, MarkType::kTypedef }
1491 , { &fIUnionMap, MarkType::kUnion }
1492 , { nullptr, MarkType::kVolatile }
1493 , { nullptr, MarkType::kWidth } }
1494 {
1495 this->reset();
1496 }
1497
1498 ~IncludeParser() override {}
1499
1500 void addKeyword(KeyWord keyWord);
1501
1502 void addPunctuation(Punctuation punctuation) {
1503 fParent->fTokens.emplace_back(punctuation, fChar, fLineCount, fParent);
1504 }
1505
1506 void addWord() {
1507 fParent->fTokens.emplace_back(fIncludeWord, fChar, fLineCount, fParent);
1508 fIncludeWord = nullptr;
1509 }
1510
1511 void checkForMissingParams(const vector<string>& methodParams,
1512 const vector<string>& foundParams);
1513 bool checkForWord();
1514 string className() const;
1515 bool crossCheck(BmhParser& );
1516 IClassDefinition* defineClass(const Definition& includeDef, const string& className);
1517 void dumpClassTokens(IClassDefinition& classDef);
1518 void dumpComment(Definition* token);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001519 bool dumpTokens(const string& directory);
Cary Clark8032b982017-07-28 11:04:54 -04001520 bool findComments(const Definition& includeDef, Definition* markupDef);
1521
1522 Definition* findIncludeObject(const Definition& includeDef, MarkType markType,
1523 const string& typeName) {
1524 typedef Definition* DefinitionPtr;
1525 unordered_map<string, Definition>* map = fMaps[(int) markType].fInclude;
1526 if (!map) {
1527 return reportError<DefinitionPtr>("invalid mark type");
1528 }
1529 string name = this->uniqueName(*map, typeName);
1530 Definition& markupDef = (*map)[name];
1531 if (markupDef.fStart) {
1532 return reportError<DefinitionPtr>("definition already defined");
1533 }
1534 markupDef.fFileName = fFileName;
1535 markupDef.fStart = includeDef.fStart;
1536 markupDef.fContentStart = includeDef.fStart;
1537 markupDef.fName = name;
1538 markupDef.fContentEnd = includeDef.fContentEnd;
1539 markupDef.fTerminator = includeDef.fTerminator;
1540 markupDef.fParent = fParent;
1541 markupDef.fLineCount = includeDef.fLineCount;
1542 markupDef.fMarkType = markType;
1543 markupDef.fKeyWord = includeDef.fKeyWord;
1544 markupDef.fType = Definition::Type::kMark;
1545 return &markupDef;
1546 }
1547
1548 static KeyWord FindKey(const char* start, const char* end);
Cary Clarkbad5ad72017-08-03 17:14:08 -04001549 bool internalName(const Definition& ) const;
Cary Clark8032b982017-07-28 11:04:54 -04001550 void keywordEnd();
1551 void keywordStart(const char* keyword);
1552 bool parseChar();
1553 bool parseComment(const string& filename, const char* start, const char* end, int lineCount,
1554 Definition* markupDef);
1555 bool parseClass(Definition* def, IsStruct);
1556 bool parseDefine();
1557 bool parseEnum(Definition* child, Definition* markupDef);
1558
1559 bool parseFromFile(const char* path) override {
1560 if (!INHERITED::parseSetup(path)) {
1561 return false;
1562 }
1563 string name(path);
1564 return parseInclude(name);
1565 }
1566
1567 bool parseInclude(const string& name);
1568 bool parseMember(Definition* child, Definition* markupDef);
1569 bool parseMethod(Definition* child, Definition* markupDef);
1570 bool parseObject(Definition* child, Definition* markupDef);
1571 bool parseObjects(Definition* parent, Definition* markupDef);
1572 bool parseTemplate();
1573 bool parseTypedef();
1574 bool parseUnion();
1575
1576 void popBracket() {
1577 SkASSERT(Definition::Type::kBracket == fParent->fType);
1578 this->popObject();
1579 Bracket bracket = this->topBracket();
1580 this->setBracketShortCuts(bracket);
1581 }
1582
1583 void pushBracket(Bracket bracket) {
1584 this->setBracketShortCuts(bracket);
1585 fParent->fTokens.emplace_back(bracket, fChar, fLineCount, fParent);
1586 Definition* container = &fParent->fTokens.back();
1587 this->addDefinition(container);
1588 }
1589
1590 void reset() override {
1591 INHERITED::resetCommon();
1592 fRootTopic = nullptr;
1593 fInBrace = nullptr;
1594 fIncludeWord = nullptr;
Cary Clark73fa9722017-08-29 17:36:51 -04001595 fLastObject = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001596 fPrev = '\0';
1597 fInChar = false;
1598 fInCharCommentString = false;
1599 fInComment = false;
1600 fInEnum = false;
1601 fInFunction = false;
1602 fInString = false;
1603 }
1604
1605 void setBracketShortCuts(Bracket bracket) {
1606 fInComment = Bracket::kSlashSlash == bracket || Bracket::kSlashStar == bracket;
1607 fInString = Bracket::kString == bracket;
1608 fInChar = Bracket::kChar == bracket;
1609 fInCharCommentString = fInChar || fInComment || fInString;
1610 }
1611
1612 Bracket topBracket() const {
1613 Definition* parent = fParent;
1614 while (parent && Definition::Type::kBracket != parent->fType) {
1615 parent = parent->fParent;
1616 }
1617 return parent ? parent->fBracket : Bracket::kNone;
1618 }
1619
1620 template <typename T>
1621 string uniqueName(const unordered_map<string, T>& m, const string& typeName) {
1622 string base(typeName.size() > 0 ? typeName : "_anonymous");
1623 string name(base);
1624 int anonCount = 1;
1625 do {
1626 auto iter = m.find(name);
1627 if (iter == m.end()) {
1628 return name;
1629 }
1630 name = base + '_';
1631 name += to_string(++anonCount);
1632 } while (true);
1633 // should never get here
1634 return string();
1635 }
1636
1637 void validate() const;
1638
1639protected:
1640 static void ValidateKeyWords();
1641
1642 struct DefinitionMap {
1643 unordered_map<string, Definition>* fInclude;
1644 MarkType fMarkType;
1645 };
1646
1647 DefinitionMap fMaps[Last_MarkType + 1];
1648 unordered_map<string, Definition> fIncludeMap;
1649 unordered_map<string, IClassDefinition> fIClassMap;
1650 unordered_map<string, Definition> fIDefineMap;
1651 unordered_map<string, Definition> fIEnumMap;
1652 unordered_map<string, Definition> fIStructMap;
1653 unordered_map<string, Definition> fITemplateMap;
1654 unordered_map<string, Definition> fITypedefMap;
1655 unordered_map<string, Definition> fIUnionMap;
1656 Definition* fRootTopic;
1657 Definition* fInBrace;
Cary Clark73fa9722017-08-29 17:36:51 -04001658 Definition* fLastObject;
Cary Clark8032b982017-07-28 11:04:54 -04001659 const char* fIncludeWord;
1660 char fPrev;
1661 bool fInChar;
1662 bool fInCharCommentString;
1663 bool fInComment;
1664 bool fInEnum;
1665 bool fInFunction;
1666 bool fInString;
1667
1668 typedef ParserCommon INHERITED;
1669};
1670
1671class IncludeWriter : public IncludeParser {
1672public:
1673 enum class Word {
1674 kStart,
1675 kCap,
1676 kFirst,
1677 kUnderline,
1678 kMixed,
1679 };
1680
Cary Clarkd0530ba2017-09-14 11:25:39 -04001681 enum class Phrase {
1682 kNo,
1683 kYes,
1684 };
1685
Cary Clark8032b982017-07-28 11:04:54 -04001686 enum class PunctuationState {
1687 kStart,
1688 kDelimiter,
1689 kPeriod,
Cary Clark1eace2d2017-07-31 07:52:43 -04001690 kSpace,
Cary Clark8032b982017-07-28 11:04:54 -04001691 };
1692
Cary Clarkce101242017-09-01 15:51:02 -04001693 enum class RefType {
1694 kUndefined,
1695 kNormal,
1696 kExternal,
1697 };
1698
Cary Clark8032b982017-07-28 11:04:54 -04001699 enum class Wrote {
1700 kNone,
1701 kLF,
1702 kChars,
1703 };
1704
Cary Clarkbad5ad72017-08-03 17:14:08 -04001705 struct IterState {
1706 IterState (list<Definition>::iterator tIter, list<Definition>::iterator tIterEnd)
1707 : fDefIter(tIter)
1708 , fDefEnd(tIterEnd) {
1709 }
1710 list<Definition>::iterator fDefIter;
1711 list<Definition>::iterator fDefEnd;
1712 };
1713
Cary Clark73fa9722017-08-29 17:36:51 -04001714 struct ParentPair {
1715 const Definition* fParent;
1716 const ParentPair* fPrev;
1717 };
1718
Cary Clark8032b982017-07-28 11:04:54 -04001719 IncludeWriter() : IncludeParser() {}
1720 ~IncludeWriter() override {}
1721
1722 bool contentFree(int size, const char* data) const {
1723 while (size > 0 && data[0] <= ' ') {
1724 --size;
1725 ++data;
1726 }
1727 while (size > 0 && data[size - 1] <= ' ') {
1728 --size;
1729 }
1730 return 0 == size;
1731 }
1732
1733 void enumHeaderOut(const RootDefinition* root, const Definition& child);
Cary Clarkbad5ad72017-08-03 17:14:08 -04001734 void enumMembersOut(const RootDefinition* root, Definition& child);
Cary Clark8032b982017-07-28 11:04:54 -04001735 void enumSizeItems(const Definition& child);
1736 int lookupMethod(const PunctuationState punctuation, const Word word,
Cary Clark579985c2017-07-31 11:48:27 -04001737 const int start, const int run, int lastWrite,
Cary Clark8032b982017-07-28 11:04:54 -04001738 const char* data);
1739 int lookupReference(const PunctuationState punctuation, const Word word,
1740 const int start, const int run, int lastWrite, const char last,
1741 const char* data);
Cary Clark579985c2017-07-31 11:48:27 -04001742 void methodOut(const Definition* method, const Definition& child);
Cary Clark73fa9722017-08-29 17:36:51 -04001743 bool populate(Definition* def, ParentPair* parentPair, RootDefinition* root);
Cary Clark8032b982017-07-28 11:04:54 -04001744 bool populate(BmhParser& bmhParser);
1745
1746 void reset() override {
1747 INHERITED::resetCommon();
Cary Clark579985c2017-07-31 11:48:27 -04001748 fBmhMethod = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001749 fBmhParser = nullptr;
1750 fEnumDef = nullptr;
Cary Clark579985c2017-07-31 11:48:27 -04001751 fMethodDef = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001752 fStructDef = nullptr;
Cary Clark73fa9722017-08-29 17:36:51 -04001753 fAttrDeprecated = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001754 fAnonymousEnumCount = 1;
1755 fInStruct = false;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001756 fWroteMethod = false;
Cary Clark8032b982017-07-28 11:04:54 -04001757 }
1758
1759 string resolveMethod(const char* start, const char* end, bool first);
Cary Clarkce101242017-09-01 15:51:02 -04001760 string resolveRef(const char* start, const char* end, bool first, RefType* refType);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001761 Wrote rewriteBlock(int size, const char* data, Phrase phrase);
Cary Clarkbad5ad72017-08-03 17:14:08 -04001762 Definition* structMemberOut(const Definition* memberStart, const Definition& child);
Cary Clark8032b982017-07-28 11:04:54 -04001763 void structOut(const Definition* root, const Definition& child,
1764 const char* commentStart, const char* commentEnd);
1765 void structSizeMembers(Definition& child);
1766
1767private:
1768 BmhParser* fBmhParser;
1769 Definition* fDeferComment;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001770 Definition* fLastComment;
Cary Clark579985c2017-07-31 11:48:27 -04001771 const Definition* fBmhMethod;
Cary Clark8032b982017-07-28 11:04:54 -04001772 const Definition* fEnumDef;
Cary Clark579985c2017-07-31 11:48:27 -04001773 const Definition* fMethodDef;
Cary Clark8032b982017-07-28 11:04:54 -04001774 const Definition* fStructDef;
Cary Clark73fa9722017-08-29 17:36:51 -04001775 const Definition* fAttrDeprecated;
Cary Clark8032b982017-07-28 11:04:54 -04001776 const char* fContinuation; // used to construct paren-qualified method name
1777 int fAnonymousEnumCount;
1778 int fEnumItemValueTab;
1779 int fEnumItemCommentTab;
1780 int fStructMemberTab;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001781 int fStructValueTab;
Cary Clark8032b982017-07-28 11:04:54 -04001782 int fStructCommentTab;
1783 bool fInStruct;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001784 bool fWroteMethod;
Cary Clark8032b982017-07-28 11:04:54 -04001785
1786 typedef IncludeParser INHERITED;
1787};
1788
1789class FiddleParser : public ParserCommon {
1790public:
1791 FiddleParser(BmhParser* bmh) : ParserCommon()
1792 , fBmhParser(bmh) {
1793 this->reset();
1794 }
1795
1796 Definition* findExample(const string& name) const;
1797
1798 bool parseFromFile(const char* path) override {
1799 if (!INHERITED::parseSetup(path)) {
1800 return false;
1801 }
1802 return parseFiddles();
1803 }
1804
1805 void reset() override {
1806 INHERITED::resetCommon();
1807 }
1808
1809private:
1810 bool parseFiddles();
1811
1812 BmhParser* fBmhParser; // must be writable; writes example hash
1813
1814 typedef ParserCommon INHERITED;
1815};
1816
1817class HackParser : public ParserCommon {
1818public:
1819 HackParser() : ParserCommon() {
1820 this->reset();
1821 }
1822
1823 bool parseFromFile(const char* path) override {
1824 if (!INHERITED::parseSetup(path)) {
1825 return false;
1826 }
1827 return hackFiles();
1828 }
1829
1830 void reset() override {
1831 INHERITED::resetCommon();
1832 }
1833
1834private:
1835 bool hackFiles();
1836
1837 typedef ParserCommon INHERITED;
1838};
1839
1840class MdOut : public ParserCommon {
1841public:
1842 MdOut(const BmhParser& bmh) : ParserCommon()
1843 , fBmhParser(bmh) {
1844 this->reset();
1845 }
1846
1847 bool buildReferences(const char* path, const char* outDir);
1848private:
1849 enum class TableState {
1850 kNone,
1851 kRow,
1852 kColumn,
1853 };
1854
1855 string addReferences(const char* start, const char* end, BmhParser::Resolvable );
1856 bool buildRefFromFile(const char* fileName, const char* outDir);
Cary Clarka523d2d2017-08-30 08:58:10 -04001857 bool checkParamReturnBody(const Definition* def) const;
Cary Clark8032b982017-07-28 11:04:54 -04001858 void childrenOut(const Definition* def, const char* contentStart);
Cary Clarkd0530ba2017-09-14 11:25:39 -04001859 const Definition* findParamType();
Cary Clark8032b982017-07-28 11:04:54 -04001860 const Definition* isDefined(const TextParser& parser, const string& ref, bool report) const;
1861 string linkName(const Definition* ) const;
1862 string linkRef(const string& leadingSpaces, const Definition*, const string& ref) const;
1863 void markTypeOut(Definition* );
1864 void mdHeaderOut(int depth) { mdHeaderOutLF(depth, 2); }
1865 void mdHeaderOutLF(int depth, int lf);
1866 bool parseFromFile(const char* path) override {
1867 return true;
1868 }
1869
1870 void reset() override {
1871 INHERITED::resetCommon();
Cary Clarkbad5ad72017-08-03 17:14:08 -04001872 fEnumClass = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001873 fMethod = nullptr;
1874 fRoot = nullptr;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001875 fLastParam = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001876 fTableState = TableState::kNone;
1877 fHasFiddle = false;
1878 fInDescription = false;
1879 fInList = false;
1880 }
1881
1882 BmhParser::Resolvable resolvable(MarkType markType) {
1883 if ((MarkType::kExample == markType
1884 || MarkType::kFunction == markType) && fHasFiddle) {
1885 return BmhParser::Resolvable::kNo;
1886 }
1887 return fBmhParser.fMaps[(int) markType].fResolve;
1888 }
1889
1890 void resolveOut(const char* start, const char* end, BmhParser::Resolvable );
1891
1892 const BmhParser& fBmhParser;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001893 const Definition* fEnumClass;
Cary Clark8032b982017-07-28 11:04:54 -04001894 Definition* fMethod;
1895 RootDefinition* fRoot;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001896 const Definition* fLastParam;
Cary Clark8032b982017-07-28 11:04:54 -04001897 TableState fTableState;
1898 bool fHasFiddle;
1899 bool fInDescription; // FIXME: for now, ignore unfound camelCase in description since it may
1900 // be defined in example which at present cannot be linked to
1901 bool fInList;
1902 typedef ParserCommon INHERITED;
1903};
1904
1905
1906// some methods cannot be trivially parsed; look for class-name / ~ / operator
1907class MethodParser : public TextParser {
1908public:
1909 MethodParser(const string& className, const string& fileName,
1910 const char* start, const char* end, int lineCount)
1911 : TextParser(fileName, start, end, lineCount)
1912 , fClassName(className) {
1913 }
1914
1915 void skipToMethodStart() {
1916 if (!fClassName.length()) {
1917 this->skipToAlphaNum();
1918 return;
1919 }
1920 while (!this->eof() && !isalnum(this->peek()) && '~' != this->peek()) {
1921 this->next();
1922 }
1923 }
1924
1925 void skipToMethodEnd() {
1926 if (this->eof()) {
1927 return;
1928 }
1929 if (fClassName.length()) {
1930 if ('~' == this->peek()) {
1931 this->next();
1932 if (!this->startsWith(fClassName.c_str())) {
1933 --fChar;
1934 return;
1935 }
1936 }
1937 if (this->startsWith(fClassName.c_str()) || this->startsWith("operator")) {
1938 const char* ptr = this->anyOf(" (");
1939 if (ptr && '(' == *ptr) {
1940 this->skipToEndBracket(')');
1941 SkAssertResult(')' == this->next());
1942 return;
1943 }
1944 }
1945 }
1946 if (this->startsWith("Sk") && this->wordEndsWith(".h")) { // allow include refs
1947 this->skipToNonAlphaNum();
1948 } else {
1949 this->skipFullName();
1950 }
1951 }
1952
1953 bool wordEndsWith(const char* str) const {
1954 const char* space = this->strnchr(' ', fEnd);
1955 if (!space) {
1956 return false;
1957 }
1958 size_t len = strlen(str);
1959 if (space < fChar + len) {
1960 return false;
1961 }
1962 return !strncmp(str, space - len, len);
1963 }
1964
1965private:
1966 string fClassName;
1967 typedef TextParser INHERITED;
1968};
1969
1970#endif