blob: 9ecd4665cd57c2b84c430f6e07ea6ec47a643f2f [file] [log] [blame]
Cary Clark2da9fb82018-11-01 09:29:36 -04001/*
2 * Copyright 2018 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 textParser_DEFINED
9#define textParser_DEFINED
10
11#include <functional>
12
13#include "bookmaker.h"
14
15class BmhParser;
16class Definition;
17
18class TextParser : public NonAssignable {
19 TextParser() {} // only for ParserCommon, TextParserSave
20 friend class ParserCommon;
21 friend class TextParserSave;
22public:
23 virtual ~TextParser() {}
24
25 TextParser(string fileName, const char* start, const char* end, int lineCount)
26 : fFileName(fileName)
27 , fStart(start)
28 , fLine(start)
29 , fChar(start)
30 , fEnd(end)
31 , fLineCount(lineCount)
32 {
33 }
34
35 TextParser(const Definition* );
36
37 const char* anyOf(const char* str) const {
38 const char* ptr = fChar;
39 while (ptr < fEnd) {
40 if (strchr(str, ptr[0])) {
41 return ptr;
42 }
43 ++ptr;
44 }
45 return nullptr;
46 }
47
48 const char* anyOf(const char* wordStart, const char* wordList[], size_t wordListCount) const {
49 const char** wordPtr = wordList;
50 const char** wordEnd = wordPtr + wordListCount;
51 const size_t matchLen = fChar - wordStart;
52 while (wordPtr < wordEnd) {
53 const char* word = *wordPtr++;
54 if (strlen(word) == matchLen && !strncmp(wordStart, word, matchLen)) {
55 return word;
56 }
57 }
58 return nullptr;
59 }
60
Cary Clarkabaffd82018-11-15 08:25:12 -050061 // words must be alpha only
62 string anyWord(const vector<string>& wordList, int spaces) const {
63 const char* matchStart = fChar;
64 do {
65 int count = spaces;
66 while (matchStart < fEnd && !isalpha(matchStart[0])) {
67 ++matchStart;
68 }
69 const char* matchEnd = matchStart;
70 const char* nextWord = nullptr;
71 while (matchEnd < fEnd) {
72 if (isalpha(matchEnd[0])) {
73 ;
74 } else if (' ' == matchEnd[0] && --count >= 0) {
75 if (!nextWord) {
76 nextWord = matchEnd;
77 }
78 } else {
79 break;
80 }
81 ++matchEnd;
82 }
83 size_t matchLen = matchEnd - matchStart;
84 for (auto word : wordList) {
85 if (word.length() != matchLen) {
86 continue;
87 }
88 for (unsigned index = 0; index < matchLen; ++index) {
89 if (tolower(matchStart[index]) != word[index]) {
90 goto nextWord;
91 }
92 }
93 return word;
94 nextWord: ;
95 }
96 matchStart = nextWord ? nextWord : matchEnd;
97 } while (matchStart < fEnd);
98 return "";
99 }
100
Cary Clark2da9fb82018-11-01 09:29:36 -0400101 bool back(const char* pattern) {
102 size_t len = strlen(pattern);
103 const char* start = fChar - len;
104 if (start <= fStart) {
105 return false;
106 }
107 if (strncmp(start, pattern, len)) {
108 return false;
109 }
110 fChar = start;
111 return true;
112 }
113
114 char backup(const char* pattern) const {
115 size_t len = strlen(pattern);
116 const char* start = fChar - len;
117 if (start <= fStart) {
118 return '\0';
119 }
120 if (strncmp(start, pattern, len)) {
121 return '\0';
122 }
123 return start[-1];
124 }
125
126 void backupWord() {
127 while (fChar > fStart && isalpha(fChar[-1])) {
128 --fChar;
129 }
130 }
131
132 bool contains(const char* match, const char* lineEnd, const char** loc) const {
133 const char* result = this->strnstr(match, lineEnd);
134 if (loc) {
135 *loc = result;
136 }
137 return result;
138 }
139
140 bool containsWord(const char* match, const char* lineEnd, const char** loc) {
141 size_t len = strlen(match);
142 do {
143 const char* result = this->strnstr(match, lineEnd);
144 if (!result) {
145 return false;
146 }
147 if ((result > fStart && isalnum(result[-1])) || (result + len < fEnd
148 && isalnum(result[len]))) {
149 fChar = result + len;
150 continue;
151 }
152 if (loc) {
153 *loc = result;
154 }
155 return true;
156 } while (true);
157 }
158
159 // either /n/n or /n# will stop parsing a typedef
160 const char* doubleLF() const {
161 const char* ptr = fChar - 1;
162 const char* doubleStart = nullptr;
163 while (++ptr < fEnd) {
164 if (!doubleStart) {
165 if ('\n' == ptr[0]) {
166 doubleStart = ptr;
167 }
168 continue;
169 }
170 if ('\n' == ptr[0] || '#' == ptr[0]) {
171 return doubleStart;
172 }
173 if (' ' < ptr[0]) {
174 doubleStart = nullptr;
175 }
176 }
177 return nullptr;
178 }
179
180 bool endsWith(const char* match) {
181 int matchLen = strlen(match);
182 if (matchLen > fChar - fLine) {
183 return false;
184 }
185 return !strncmp(fChar - matchLen, match, matchLen);
186 }
187
188 bool eof() const { return fChar >= fEnd; }
189
190 const char* lineEnd() const {
191 const char* ptr = fChar;
192 do {
193 if (ptr >= fEnd) {
194 return ptr;
195 }
196 char test = *ptr++;
197 if (test == '\n' || test == '\0') {
198 break;
199 }
200 } while (true);
201 return ptr;
202 }
203
204 ptrdiff_t lineLength() const {
205 return this->lineEnd() - fLine;
206 }
207
208 bool match(TextParser* );
209
210 char next() {
211 SkASSERT(fChar < fEnd);
212 char result = *fChar++;
213 if ('\n' == result) {
214 ++fLineCount;
215 fLine = fChar;
216 }
217 return result;
218 }
219
220 char peek() const { SkASSERT(fChar < fEnd); return *fChar; }
221
222 void restorePlace(const TextParser& save) {
223 fChar = save.fChar;
224 fLine = save.fLine;
225 fLineCount = save.fLineCount;
226 }
227
228 void savePlace(TextParser* save) {
229 save->fChar = fChar;
230 save->fLine = fLine;
231 save->fLineCount = fLineCount;
232 }
233
234 void reportError(const char* errorStr) const;
235 static string ReportFilename(string file);
236 void reportWarning(const char* errorStr) const;
237
238 template <typename T> T reportError(const char* errorStr) const {
239 this->reportError(errorStr);
240 return T();
241 }
242
243 bool sentenceEnd(const char* check) const {
244 while (check > fStart) {
245 --check;
246 if (' ' < check[0] && '.' != check[0]) {
247 return false;
248 }
249 if ('.' == check[0]) {
250 return ' ' >= check[1];
251 }
252 if ('\n' == check[0] && '\n' == check[1]) {
253 return true;
254 }
255 }
256 return true;
257 }
258
259 void setForErrorReporting(const Definition* , const char* );
260
261 bool skipToBalancedEndBracket(char startB, char endB) {
262 SkASSERT(fChar < fEnd);
263 SkASSERT(startB == fChar[0]);
264 int startCount = 0;
265 do {
266 char test = this->next();
267 startCount += startB == test;
268 startCount -= endB == test;
269 } while (startCount && fChar < fEnd);
270 return !startCount;
271 }
272
273 bool skipToEndBracket(char endBracket, const char* end = nullptr) {
274 if (nullptr == end) {
275 end = fEnd;
276 }
277 while (fChar[0] != endBracket) {
278 if (fChar >= end) {
279 return false;
280 }
281 (void) this->next();
282 }
283 return true;
284 }
285
286 bool skipToEndBracket(const char* endBracket) {
287 size_t len = strlen(endBracket);
288 while (strncmp(fChar, endBracket, len)) {
289 if (fChar >= fEnd) {
290 return false;
291 }
292 (void) this->next();
293 }
294 return true;
295 }
296
297 bool skipLine() {
298 return skipToEndBracket('\n');
299 }
300
301 void skipTo(const char* skip) {
302 while (fChar < skip) {
303 this->next();
304 }
305 }
306
307 void skipToAlpha() {
308 while (fChar < fEnd && !isalpha(fChar[0])) {
309 fChar++;
310 }
311 }
312
313 // returns true if saw close brace
314 bool skipToAlphaNum() {
315 bool sawCloseBrace = false;
316 while (fChar < fEnd && !isalnum(fChar[0])) {
317 sawCloseBrace |= '}' == *fChar++;
318 }
319 return sawCloseBrace;
320 }
321
322 bool skipExact(const char* pattern) {
323 if (!this->startsWith(pattern)) {
324 return false;
325 }
326 this->skipName(pattern);
327 return true;
328 }
329
330 // differs from skipToNonAlphaNum in that a.b isn't considered a full name,
331 // since a.b can't be found as a named definition
332 void skipFullName() {
333 do {
334 char last = '\0';
335 while (fChar < fEnd && (isalnum(fChar[0])
336 || '_' == fChar[0] /* || '-' == fChar[0] */
337 || (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1]))) {
338 if (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1]) {
339 fChar++;
340 }
341 last = fChar[0];
342 fChar++;
343 }
344 if (fChar + 1 >= fEnd || '/' != fChar[0] || !isalpha(last) || !isalpha(fChar[1])) {
345 break; // stop unless pattern is xxx/xxx as in I/O
346 }
347 fChar++; // skip slash
348 } while (true);
349 }
350
351 int skipToLineBalance(char open, char close) {
352 int match = 0;
353 while (!this->eof() && '\n' != fChar[0]) {
354 match += open == this->peek();
355 match -= close == this->next();
356 }
357 return match;
358 }
359
360 bool skipToLineStart() {
361 if (!this->skipLine()) {
362 return false;
363 }
364 if (!this->eof()) {
365 return this->skipWhiteSpace();
366 }
367 return true;
368 }
369
370 void skipToLineStart(int* indent, bool* sawReturn) {
371 SkAssertResult(this->skipLine());
372 this->skipWhiteSpace(indent, sawReturn);
373 }
374
375 void skipLower() {
376 while (fChar < fEnd && (islower(fChar[0]) || '_' == fChar[0])) {
377 fChar++;
378 }
379 }
380
381 void skipToNonAlphaNum() {
382 while (fChar < fEnd && (isalnum(fChar[0]) || '_' == fChar[0])) {
383 fChar++;
384 }
385 }
386
387 void skipToNonName() {
388 while (fChar < fEnd && (isalnum(fChar[0])
389 || '_' == fChar[0] || '-' == fChar[0]
390 || (':' == fChar[0] && fChar + 1 < fEnd && ':' == fChar[1])
391 || ('.' == fChar[0] && fChar + 1 < fEnd && isalpha(fChar[1])))) {
392 if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) {
393 fChar++;
394 }
395 fChar++;
396 }
397 }
398
399 void skipPhraseName() {
400 while (fChar < fEnd && (islower(fChar[0]) || '_' == fChar[0])) {
401 fChar++;
402 }
403 }
404
405 void skipToSpace() {
406 while (fChar < fEnd && ' ' != fChar[0]) {
407 fChar++;
408 }
409 }
410
411 void skipToWhiteSpace() {
412 while (fChar < fEnd && ' ' < fChar[0]) {
413 fChar++;
414 }
415 }
416
417 bool skipName(const char* word) {
418 size_t len = strlen(word);
419 if (len <= (size_t) (fEnd - fChar) && !strncmp(word, fChar, len)) {
420 for (size_t i = 0; i < len; ++i) {
421 this->next();
422 }
423 }
424 return this->eof() || ' ' >= fChar[0];
425 }
426
427 bool skipSpace() {
428 while (' ' == this->peek()) {
429 (void) this->next();
430 if (fChar >= fEnd) {
431 return false;
432 }
433 }
434 return true;
435 }
436
437 bool skipWord(const char* word) {
438 if (!this->skipWhiteSpace()) {
439 return false;
440 }
441 const char* save = fChar;
442 if (!this->skipName(word)) {
443 fChar = save;
444 return false;
445 }
446 if (!this->skipWhiteSpace()) {
447 return false;
448 }
449 return true;
450 }
451
452 bool skipWhiteSpace() {
453 while (' ' >= this->peek()) {
454 (void) this->next();
455 if (fChar >= fEnd) {
456 return false;
457 }
458 }
459 return true;
460 }
461
462 void skipWhiteSpace(int* indent, bool* skippedReturn) {
463 while (' ' >= this->peek()) {
464 *indent = *skippedReturn ? *indent + 1 : 1;
465 if ('\n' == this->peek()) {
466 *skippedReturn |= true;
467 *indent = 0;
468 }
469 (void) this->next();
470 SkASSERT(fChar < fEnd);
471 }
472 }
473
474 bool startsWith(const char* str) const {
475 size_t len = strlen(str);
476 ptrdiff_t lineLen = fEnd - fChar;
477 return len <= (size_t) lineLen && 0 == strncmp(str, fChar, len);
478 }
479
480 // ignores minor white space differences
481 bool startsWith(const char* str, size_t oLen) const {
482 size_t tIndex = 0;
483 size_t tLen = fEnd - fChar;
484 size_t oIndex = 0;
485 while (oIndex < oLen && tIndex < tLen) {
486 bool tSpace = ' ' >= fChar[tIndex];
487 bool oSpace = ' ' >= str[oIndex];
488 if (tSpace != oSpace) {
489 break;
490 }
491 if (tSpace) {
492 do {
493 ++tIndex;
494 } while (tIndex < tLen && ' ' >= fChar[tIndex]);
495 do {
496 ++oIndex;
497 } while (oIndex < oLen && ' ' >= str[oIndex]);
498 continue;
499 }
500 if (fChar[tIndex] != str[oIndex]) {
501 break;
502 }
503 ++tIndex;
504 ++oIndex;
505 }
506 return oIndex >= oLen;
507 }
508
509 const char* strnchr(char ch, const char* end) const {
510 const char* ptr = fChar;
511 while (ptr < end) {
512 if (ptr[0] == ch) {
513 return ptr;
514 }
515 ++ptr;
516 }
517 return nullptr;
518 }
519
520 const char* strnstr(const char *match, const char* end) const {
521 size_t matchLen = strlen(match);
522 SkASSERT(matchLen > 0);
523 ptrdiff_t len = end - fChar;
524 SkASSERT(len >= 0);
525 if ((size_t) len < matchLen ) {
526 return nullptr;
527 }
528 size_t count = len - matchLen;
529 for (size_t index = 0; index <= count; index++) {
530 if (0 == strncmp(&fChar[index], match, matchLen)) {
531 return &fChar[index];
532 }
533 }
534 return nullptr;
535 }
536
537 const char* trimmedBracketEnd(const char bracket) const {
538 int max = (int) (this->lineLength());
539 int index = 0;
540 while (index < max && bracket != fChar[index]) {
541 ++index;
542 }
543 SkASSERT(index < max);
544 while (index > 0 && ' ' >= fChar[index - 1]) {
545 --index;
546 }
547 return fChar + index;
548 }
549
550 const char* trimmedBracketEnd(string bracket) const {
551 size_t max = (size_t) (this->lineLength());
552 string line(fChar, max);
553 size_t index = line.find(bracket);
554 SkASSERT(index < max);
555 while (index > 0 && ' ' >= fChar[index - 1]) {
556 --index;
557 }
558 return fChar + index;
559 }
560
561 const char* trimmedBracketNoEnd(const char bracket) const {
562 int max = (int) (fEnd - fChar);
563 int index = 0;
564 while (index < max && bracket != fChar[index]) {
565 ++index;
566 }
567 SkASSERT(index < max);
568 while (index > 0 && ' ' >= fChar[index - 1]) {
569 --index;
570 }
571 return fChar + index;
572 }
573
574 const char* trimmedLineEnd() const {
575 const char* result = this->lineEnd();
576 while (result > fChar && ' ' >= result[-1]) {
577 --result;
578 }
579 return result;
580 }
581
582 void trimEnd() {
583 while (fEnd > fStart && ' ' >= fEnd[-1]) {
584 --fEnd;
585 }
586 }
587
588 // FIXME: nothing else in TextParser knows from C++ --
589 // there could be a class between TextParser and ParserCommon
590 virtual string typedefName();
591
592 const char* wordEnd() const {
593 const char* end = fChar;
594 while (isalnum(end[0]) || '_' == end[0] || '-' == end[0]) {
595 ++end;
596 }
597 return end;
598 }
599
600 string fFileName;
601 const char* fStart;
602 const char* fLine;
603 const char* fChar;
604 const char* fEnd;
605 size_t fLineCount;
606};
607
608class TextParserSave {
609public:
610 TextParserSave(TextParser* parser) {
611 fParser = parser;
612 fSave.fFileName = parser->fFileName;
613 fSave.fStart = parser->fStart;
614 fSave.fLine = parser->fLine;
615 fSave.fChar = parser->fChar;
616 fSave.fEnd = parser->fEnd;
617 fSave.fLineCount = parser->fLineCount;
618 }
619
620 void restore() const {
621 fParser->fFileName = fSave.fFileName;
622 fParser->fStart = fSave.fStart;
623 fParser->fLine = fSave.fLine;
624 fParser->fChar = fSave.fChar;
625 fParser->fEnd = fSave.fEnd;
626 fParser->fLineCount = fSave.fLineCount;
627 }
628
629private:
630 TextParser* fParser;
631 TextParser fSave;
632};
633
634static inline bool has_nonwhitespace(string s) {
635 bool nonwhite = false;
636 for (const char& c : s) {
637 if (' ' < c) {
638 nonwhite = true;
639 break;
640 }
641 }
642 return nonwhite;
643}
644
645static inline void trim_end(string &s) {
646 s.erase(std::find_if(s.rbegin(), s.rend(),
647 std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
648}
649
650static inline void trim_end_spaces(string &s) {
651 while (s.length() > 0 && ' ' == s.back()) {
652 s.pop_back();
653 }
654}
655
656static inline void trim_start(string &s) {
657 s.erase(s.begin(), std::find_if(s.begin(), s.end(),
658 std::not1(std::ptr_fun<int, int>(std::isspace))));
659}
660
661static inline void trim_start_end(string& s) {
662 trim_start(s);
663 trim_end(s);
664}
665
Cary Clark77b3f3a2018-11-07 14:59:03 -0500666static inline string trim_inline_spaces(string s) {
667 bool lastSpace = false;
668 string trimmed;
669 for (const char* ptr = &s.front(); ptr <= &s.back(); ++ptr) {
670 char c = *ptr;
671 if (' ' >= c) {
672 if (!lastSpace) {
673 trimmed += ' ';
674 }
675 lastSpace = true;
676 continue;
677 }
678 lastSpace = false;
679 trimmed += c;
680 }
681 return trimmed;
682}
683
Cary Clark2da9fb82018-11-01 09:29:36 -0400684class EscapeParser : public TextParser {
685public:
686 EscapeParser(const char* start, const char* end) :
687 TextParser("", start, end, 0) {
688 const char* reader = fStart;
689 fStorage = new char[end - start];
690 char* writer = fStorage;
691 while (reader < fEnd) {
692 char ch = *reader++;
693 if (ch != '\\') {
694 *writer++ = ch;
695 } else {
696 char ctrl = *reader++;
697 if (ctrl == 'u') {
698 unsigned unicode = 0;
699 for (int i = 0; i < 4; ++i) {
700 unicode <<= 4;
701 SkASSERT((reader[0] >= '0' && reader[0] <= '9') ||
702 (reader[0] >= 'A' && reader[0] <= 'F') ||
703 (reader[0] >= 'a' && reader[0] <= 'f'));
704 int nibble = *reader++ - '0';
705 if (nibble > 9) {
706 nibble = (nibble & ~('a' - 'A')) - 'A' + '9' + 1;
707 }
708 unicode |= nibble;
709 }
710 SkASSERT(unicode < 256);
711 *writer++ = (unsigned char) unicode;
712 } else {
713 SkASSERT(ctrl == 'n');
714 *writer++ = '\n';
715 }
716 }
717 }
718 fStart = fLine = fChar = fStorage;
719 fEnd = writer;
720 }
721
722 ~EscapeParser() override {
723 delete fStorage;
724 }
725private:
726 char* fStorage;
727};
728
729// some methods cannot be trivially parsed; look for class-name / ~ / operator
730class MethodParser : public TextParser {
731public:
732 MethodParser(string className, string fileName,
733 const char* start, const char* end, int lineCount)
734 : TextParser(fileName, start, end, lineCount)
735 , fClassName(className) {
736 size_t doubleColons = className.find_last_of("::");
737 if (string::npos != doubleColons) {
738 fLocalName = className.substr(doubleColons + 1);
739 SkASSERT(fLocalName.length() > 0);
740 }
741 }
742
743 ~MethodParser() override {}
744
745 string localName() const {
746 return fLocalName;
747 }
748
749 void setLocalName(string name) {
750 if (name == fClassName) {
751 fLocalName = "";
752 } else {
753 fLocalName = name;
754 }
755 }
756
757 // returns true if close brace was skipped
758 int skipToMethodStart() {
759 if (!fClassName.length()) {
760 return this->skipToAlphaNum();
761 }
762 int braceCount = 0;
763 while (!this->eof() && !isalnum(this->peek()) && '~' != this->peek()) {
764 braceCount += '{' == this->peek();
765 braceCount -= '}' == this->peek();
766 this->next();
767 }
768 return braceCount;
769 }
770
771 void skipToMethodEnd(Resolvable resolvable);
772
773 bool wordEndsWith(const char* str) const {
774 const char* space = this->strnchr(' ', fEnd);
775 if (!space) {
776 return false;
777 }
778 size_t len = strlen(str);
779 if (space < fChar + len) {
780 return false;
781 }
782 return !strncmp(str, space - len, len);
783 }
784
785private:
786 string fClassName;
787 string fLocalName;
788 typedef TextParser INHERITED;
789};
790
791#endif