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 | #include "definition.h" |
Cary Clark | 77b3f3a | 2018-11-07 14:59:03 -0500 | [diff] [blame] | 9 | #include "textParser.h" |
Cary Clark | 2da9fb8 | 2018-11-01 09:29:36 -0400 | [diff] [blame] | 10 | |
| 11 | #ifdef SK_BUILD_FOR_WIN |
| 12 | #include <Windows.h> |
| 13 | #endif |
| 14 | |
| 15 | TextParser::TextParser(const Definition* definition) : |
| 16 | TextParser(definition->fFileName, definition->fContentStart, definition->fContentEnd, |
| 17 | definition->fLineCount) { |
| 18 | } |
| 19 | |
| 20 | string TextParser::ReportFilename(string file) { |
| 21 | string fullName; |
| 22 | #ifdef SK_BUILD_FOR_WIN |
| 23 | TCHAR pathChars[MAX_PATH]; |
| 24 | DWORD pathLen = GetCurrentDirectory(MAX_PATH, pathChars); |
| 25 | for (DWORD index = 0; index < pathLen; ++index) { |
| 26 | fullName += pathChars[index] == (char)pathChars[index] ? (char)pathChars[index] : '?'; |
| 27 | } |
| 28 | fullName += '\\'; |
| 29 | #endif |
| 30 | fullName += file; |
| 31 | return fullName; |
| 32 | } |
| 33 | |
| 34 | void TextParser::reportError(const char* errorStr) const { |
| 35 | this->reportWarning(errorStr); |
| 36 | SkDebugf(""); // convenient place to set a breakpoint |
| 37 | } |
| 38 | |
| 39 | void TextParser::reportWarning(const char* errorStr) const { |
| 40 | const char* lineStart = fLine; |
| 41 | if (lineStart >= fEnd) { |
| 42 | lineStart = fChar; |
| 43 | } |
| 44 | SkASSERT(lineStart < fEnd); |
| 45 | TextParser err(fFileName, lineStart, fEnd, fLineCount); |
| 46 | size_t lineLen = this->lineLength(); |
| 47 | ptrdiff_t spaces = fChar - lineStart; |
| 48 | while (spaces > 0 && (size_t) spaces > lineLen) { |
| 49 | ++err.fLineCount; |
| 50 | err.fLine += lineLen; |
| 51 | spaces -= lineLen; |
| 52 | lineLen = err.lineLength(); |
| 53 | } |
| 54 | string fullName = this->ReportFilename(fFileName); |
| 55 | SkDebugf("\n%s(%zd): error: %s\n", fullName.c_str(), err.fLineCount, errorStr); |
| 56 | if (0 == lineLen) { |
| 57 | SkDebugf("[blank line]\n"); |
| 58 | } else { |
| 59 | while (lineLen > 0 && '\n' == err.fLine[lineLen - 1]) { |
| 60 | --lineLen; |
| 61 | } |
| 62 | SkDebugf("%.*s\n", (int) lineLen, err.fLine); |
| 63 | SkDebugf("%*s^\n", (int) spaces, ""); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | void TextParser::setForErrorReporting(const Definition* definition, const char* str) { |
| 68 | fFileName = definition->fFileName; |
| 69 | fStart = definition->fContentStart; |
| 70 | fLine = str; |
| 71 | while (fLine > fStart && fLine[-1] != '\n') { |
| 72 | --fLine; |
| 73 | } |
| 74 | fChar = str; |
| 75 | fEnd = definition->fContentEnd; |
| 76 | fLineCount = definition->fLineCount; |
| 77 | const char* lineInc = fStart; |
| 78 | while (lineInc < str) { |
| 79 | fLineCount += '\n' == *lineInc++; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | string TextParser::typedefName() { |
| 84 | // look for typedef as one of three forms: |
| 85 | // typedef return-type (*NAME)(params); |
| 86 | // typedef alias NAME; |
| 87 | // typedef std::function<alias> NAME; |
| 88 | string builder; |
| 89 | const char* end = this->doubleLF(); |
| 90 | if (!end) { |
| 91 | end = fEnd; |
| 92 | } |
| 93 | const char* altEnd = this->strnstr("#Typedef ##", end); |
| 94 | if (altEnd) { |
| 95 | end = this->strnchr('\n', end); |
| 96 | } |
| 97 | if (!end) { |
| 98 | return this->reportError<string>("missing typedef std::function end bracket >"); |
| 99 | } |
| 100 | bool stdFunction = this->startsWith("std::function"); |
| 101 | if (stdFunction) { |
| 102 | if (!this->skipToEndBracket('>')) { |
| 103 | return this->reportError<string>("missing typedef std::function end bracket >"); |
| 104 | } |
| 105 | this->next(); |
| 106 | this->skipWhiteSpace(); |
| 107 | builder += string(fChar, end - fChar); |
| 108 | } else { |
| 109 | const char* paren = this->strnchr('(', end); |
| 110 | if (!paren) { |
| 111 | const char* lastWord = nullptr; |
| 112 | do { |
| 113 | this->skipToWhiteSpace(); |
| 114 | if (fChar < end && isspace(fChar[0])) { |
| 115 | const char* whiteStart = fChar; |
| 116 | this->skipWhiteSpace(); |
| 117 | // FIXME: test should be for fMC |
| 118 | if ('#' == fChar[0]) { |
| 119 | end = whiteStart; |
| 120 | break; |
| 121 | } |
| 122 | lastWord = fChar; |
| 123 | } else { |
| 124 | break; |
| 125 | } |
| 126 | } while (true); |
| 127 | if (!lastWord) { |
| 128 | return this->reportError<string>("missing typedef name"); |
| 129 | } |
| 130 | builder += string(lastWord, end - lastWord); |
| 131 | } else { |
| 132 | this->skipTo(paren); |
| 133 | this->next(); |
| 134 | if ('*' != this->next()) { |
| 135 | return this->reportError<string>("missing typedef function asterisk"); |
| 136 | } |
| 137 | const char* nameStart = fChar; |
| 138 | if (!this->skipToEndBracket(')')) { |
| 139 | return this->reportError<string>("missing typedef function )"); |
| 140 | } |
| 141 | builder += string(nameStart, fChar - nameStart); |
| 142 | if (!this->skipToEndBracket('(')) { |
| 143 | return this->reportError<string>("missing typedef params ("); |
| 144 | } |
| 145 | if (! this->skipToEndBracket(')')) { |
| 146 | return this->reportError<string>("missing typedef params )"); |
| 147 | } |
| 148 | this->skipTo(end); |
| 149 | } |
| 150 | } |
| 151 | return builder; |
| 152 | } |
| 153 | |
| 154 | void MethodParser::skipToMethodEnd(Resolvable resolvable) { |
| 155 | if (this->eof()) { |
| 156 | return; |
| 157 | } |
| 158 | string name = fLocalName.length() ? fLocalName : fClassName; |
| 159 | if ('~' == this->peek()) { |
| 160 | this->next(); |
| 161 | if (!this->startsWith(name.c_str())) { |
| 162 | --fChar; |
| 163 | return; |
| 164 | } |
| 165 | } |
| 166 | if (Resolvable::kSimple != resolvable |
| 167 | && Resolvable::kInclude != resolvable |
| 168 | && (this->startsWith(name.c_str()) || this->startsWith("operator"))) { |
| 169 | const char* ptr = this->anyOf("\n ("); |
| 170 | if (ptr && '(' == *ptr && strncmp(ptr, "(...", 4)) { |
| 171 | this->skipToEndBracket(')'); |
| 172 | SkAssertResult(')' == this->next()); |
Cary Clark | cb6bef0 | 2018-11-29 12:05:25 -0500 | [diff] [blame] | 173 | Resolvable::kCode == resolvable && this->skipExact(" const"); |
Cary Clark | 2da9fb8 | 2018-11-01 09:29:36 -0400 | [diff] [blame] | 174 | return; |
| 175 | } |
| 176 | } |
| 177 | if (this->startsWith("Sk") && this->wordEndsWith(".h")) { // allow include refs |
| 178 | this->skipToNonName(); |
| 179 | } else { |
| 180 | this->skipFullName(); |
| 181 | if (this->endsWith("operator")) { |
| 182 | const char* ptr = this->anyOf("\n ("); |
| 183 | if (ptr && '(' == *ptr) { |
| 184 | this->skipToEndBracket(')'); |
| 185 | SkAssertResult(')' == this->next()); |
Cary Clark | cb6bef0 | 2018-11-29 12:05:25 -0500 | [diff] [blame] | 186 | this->skipExact(" const"); |
Cary Clark | 2da9fb8 | 2018-11-01 09:29:36 -0400 | [diff] [blame] | 187 | } |
| 188 | } |
| 189 | } |
| 190 | } |