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 | |
| 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 | |
| 568 | class TextParserSave { |
| 569 | public: |
| 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 | |
| 589 | private: |
| 590 | TextParser* fParser; |
| 591 | TextParser fSave; |
| 592 | }; |
| 593 | |
| 594 | static 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 | |
| 605 | static 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 | |
| 610 | static inline void trim_end_spaces(string &s) { |
| 611 | while (s.length() > 0 && ' ' == s.back()) { |
| 612 | s.pop_back(); |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | static 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 | |
| 621 | static inline void trim_start_end(string& s) { |
| 622 | trim_start(s); |
| 623 | trim_end(s); |
| 624 | } |
| 625 | |
| 626 | class EscapeParser : public TextParser { |
| 627 | public: |
| 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 | } |
| 667 | private: |
| 668 | char* fStorage; |
| 669 | }; |
| 670 | |
| 671 | // some methods cannot be trivially parsed; look for class-name / ~ / operator |
| 672 | class MethodParser : public TextParser { |
| 673 | public: |
| 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 | |
| 727 | private: |
| 728 | string fClassName; |
| 729 | string fLocalName; |
| 730 | typedef TextParser INHERITED; |
| 731 | }; |
| 732 | |
| 733 | #endif |