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