Cary Clark | 2da9fb8 | 2018-11-01 09:29:36 -0400 | [diff] [blame] | 1 | /* |
| 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 | |
| 15 | class BmhParser; |
| 16 | class Definition; |
| 17 | |
| 18 | class TextParser : public NonAssignable { |
| 19 | TextParser() {} // only for ParserCommon, TextParserSave |
| 20 | friend class ParserCommon; |
| 21 | friend class TextParserSave; |
| 22 | public: |
| 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 Clark | abaffd8 | 2018-11-15 08:25:12 -0500 | [diff] [blame^] | 61 | // 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 Clark | 2da9fb8 | 2018-11-01 09:29:36 -0400 | [diff] [blame] | 101 | 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 | |
| 608 | class TextParserSave { |
| 609 | public: |
| 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 | |
| 629 | private: |
| 630 | TextParser* fParser; |
| 631 | TextParser fSave; |
| 632 | }; |
| 633 | |
| 634 | static 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 | |
| 645 | static 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 | |
| 650 | static inline void trim_end_spaces(string &s) { |
| 651 | while (s.length() > 0 && ' ' == s.back()) { |
| 652 | s.pop_back(); |
| 653 | } |
| 654 | } |
| 655 | |
| 656 | static 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 | |
| 661 | static inline void trim_start_end(string& s) { |
| 662 | trim_start(s); |
| 663 | trim_end(s); |
| 664 | } |
| 665 | |
Cary Clark | 77b3f3a | 2018-11-07 14:59:03 -0500 | [diff] [blame] | 666 | static 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 Clark | 2da9fb8 | 2018-11-01 09:29:36 -0400 | [diff] [blame] | 684 | class EscapeParser : public TextParser { |
| 685 | public: |
| 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 | } |
| 725 | private: |
| 726 | char* fStorage; |
| 727 | }; |
| 728 | |
| 729 | // some methods cannot be trivially parsed; look for class-name / ~ / operator |
| 730 | class MethodParser : public TextParser { |
| 731 | public: |
| 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 | |
| 785 | private: |
| 786 | string fClassName; |
| 787 | string fLocalName; |
| 788 | typedef TextParser INHERITED; |
| 789 | }; |
| 790 | |
| 791 | #endif |