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