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