diff options
author | Cary Clark <caryclark@skia.org> | 2017-11-27 10:44:06 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-11-27 16:12:56 +0000 |
commit | a560c4796f5b83a2e55cf564dc847ad6498164b0 (patch) | |
tree | 1e47f0055e1a19621046ecbc601e999e67c34465 /tools | |
parent | 56536c42f561f6d8902703a33cf178deb91f9d4d (diff) |
bookmaker refresh
Add support for more operator overloads.
Add SkSurface, SkPoint, SkIPoint, SkIPoint16 docs.
(SkImage doc skeleton added, but not really started.)
Force recompile all examples.
Docs-Preview: https://skia.org/?cl=67726
Bug: skia:6898
Change-Id: If9e2d23f79d5db64146dd22588f5cac970614b8a
Reviewed-on: https://skia-review.googlesource.com/67726
Commit-Queue: Cary Clark <caryclark@google.com>
Reviewed-by: Cary Clark <caryclark@google.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/bookmaker/bookmaker.cpp | 975 | ||||
-rw-r--r-- | tools/bookmaker/bookmaker.h | 194 | ||||
-rw-r--r-- | tools/bookmaker/definition.cpp | 1274 | ||||
-rw-r--r-- | tools/bookmaker/includeParser.cpp | 84 | ||||
-rw-r--r-- | tools/bookmaker/includeWriter.cpp | 11 | ||||
-rw-r--r-- | tools/bookmaker/mdOut.cpp | 50 | ||||
-rw-r--r-- | tools/bookmaker/parserCommon.cpp | 28 | ||||
-rw-r--r-- | tools/bookmaker/spellCheck.cpp | 6 |
8 files changed, 1622 insertions, 1000 deletions
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp index b0b052355b..e048375860 100644 --- a/tools/bookmaker/bookmaker.cpp +++ b/tools/bookmaker/bookmaker.cpp @@ -7,9 +7,6 @@ #include "bookmaker.h" -#include "SkOSFile.h" -#include "SkOSPath.h" - DEFINE_string2(bmh, b, "", "Path to a *.bmh file or a directory."); DEFINE_bool2(catalog, c, false, "Write example catalog.htm. (Requires -b -f -r)"); DEFINE_string2(examples, e, "", "File of fiddlecli input, usually fiddle.json (For now, disables -r -f -s)"); @@ -20,7 +17,7 @@ DEFINE_bool2(stdout, o, false, "Write file out to standard out."); DEFINE_bool2(populate, p, false, "Populate include from bmh. (Requires -b -i)"); DEFINE_string2(ref, r, "", "Resolve refs and write bmh_*.md files to path. (Requires -b -f)"); DEFINE_string2(spellcheck, s, "", "Spell-check [once, all, mispelling]. (Requires -b)"); -DEFINE_string2(tokens, t, "", "Directory to write bmh from include. (Requires -i)"); +DEFINE_bool2(tokens, t, false, "Write bmh from include. (Requires -b -i)"); DEFINE_bool2(crosscheck, x, false, "Check bmh against includes. (Requires -b -i)"); DEFINE_bool2(skip, z, false, "Skip degenerate missed in legacy preprocessor."); @@ -43,853 +40,6 @@ trouble with aliases, plurals check for summary containing all methods */ -static string normalized_name(string name) { - string normalizedName = name; - std::replace(normalizedName.begin(), normalizedName.end(), '-', '_'); - do { - size_t doubleColon = normalizedName.find("::", 0); - if (string::npos == doubleColon) { - break; - } - normalizedName = normalizedName.substr(0, doubleColon) - + '_' + normalizedName.substr(doubleColon + 2); - } while (true); - return normalizedName; -} - -static size_t count_indent(const string& text, size_t test, size_t end) { - size_t result = test; - while (test < end) { - if (' ' != text[test]) { - break; - } - ++test; - } - return test - result; -} - -static void add_code(const string& text, int pos, int end, - size_t outIndent, size_t textIndent, string& example) { - do { - // fix this to move whole paragraph in, out, but preserve doc indent - int nextIndent = count_indent(text, pos, end); - size_t len = text.find('\n', pos); - if (string::npos == len) { - len = end; - } - if ((size_t) (pos + nextIndent) < len) { - size_t indent = outIndent + nextIndent; - SkASSERT(indent >= textIndent); - indent -= textIndent; - for (size_t index = 0; index < indent; ++index) { - example += ' '; - } - pos += nextIndent; - while ((size_t) pos < len) { - example += '"' == text[pos] ? "\\\"" : - '\\' == text[pos] ? "\\\\" : - text.substr(pos, 1); - ++pos; - } - example += "\\n"; - } else { - pos += nextIndent; - } - if ('\n' == text[pos]) { - ++pos; - } - } while (pos < end); -} - -// fixme: this will need to be more complicated to handle all of Skia -// for now, just handle paint -- maybe fiddle will loosen naming restrictions -void Definition::setCanonicalFiddle() { - fMethodType = Definition::MethodType::kNone; - size_t doubleColons = fName.find("::", 0); - SkASSERT(string::npos != doubleColons); - string base = fName.substr(0, doubleColons); - string result = base + "_"; - doubleColons += 2; - if (string::npos != fName.find('~', doubleColons)) { - fMethodType = Definition::MethodType::kDestructor; - result += "destructor"; - } else { - bool isMove = string::npos != fName.find("&&", doubleColons); - const char operatorStr[] = "operator"; - size_t opPos = fName.find(operatorStr, doubleColons); - if (string::npos != opPos) { - fMethodType = Definition::MethodType::kOperator; - opPos += sizeof(operatorStr) - 1; - if ('!' == fName[opPos]) { - SkASSERT('=' == fName[opPos + 1]); - result += "not_equal_operator"; - } else if ('=' == fName[opPos]) { - if ('(' == fName[opPos + 1]) { - result += isMove ? "move_" : "copy_"; - result += "assignment_operator"; - } else { - SkASSERT('=' == fName[opPos + 1]); - result += "equal_operator"; - } - } else if ('[' == fName[opPos]) { - result += "subscript_operator"; - const char* end = fContentStart; - while (end > fStart && ' ' >= end[-1]) { - --end; - } - string constCheck(fStart, end - fStart); - size_t constPos = constCheck.rfind("const"); - if (constCheck.length() == constPos + 5) { - result += "_const"; - } - } else { - SkASSERT(0); // todo: incomplete - } - } else { - size_t parens = fName.find("()", doubleColons); - if (string::npos != parens) { - string methodName = fName.substr(doubleColons, parens - doubleColons); - do { - size_t nextDouble = methodName.find("::"); - if (string::npos == nextDouble) { - break; - } - base = methodName.substr(0, nextDouble); - result += base + '_'; - methodName = methodName.substr(nextDouble + 2); - doubleColons += nextDouble + 2; - } while (true); - if (base == methodName) { - fMethodType = Definition::MethodType::kConstructor; - result += "empty_constructor"; - } else { - result += fName.substr(doubleColons, fName.length() - doubleColons - 2); - } - } else { - size_t openParen = fName.find('(', doubleColons); - if (string::npos == openParen) { - result += fName.substr(doubleColons); - } else { - size_t comma = fName.find(',', doubleColons); - if (string::npos == comma) { - result += isMove ? "move_" : "copy_"; - } - fMethodType = Definition::MethodType::kConstructor; - // name them by their param types, - // e.g. SkCanvas__int_int_const_SkSurfaceProps_star - // TODO: move forward until parens are balanced and terminator =,) - TextParser params("", &fName[openParen] + 1, &*fName.end(), 0); - bool underline = false; - while (!params.eof()) { - // SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now - // SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses - if (params.startsWith("const") || params.startsWith("int") - || params.startsWith("Sk")) { - const char* wordStart = params.fChar; - params.skipToNonAlphaNum(); - if (underline) { - result += '_'; - } else { - underline = true; - } - result += string(wordStart, params.fChar - wordStart); - } else { - params.skipToNonAlphaNum(); - } - if (!params.eof() && '*' == params.peek()) { - if (underline) { - result += '_'; - } else { - underline = true; - } - result += "star"; - params.next(); - params.skipSpace(); - } - params.skipToAlpha(); - } - } - } - } - } - fFiddle = normalized_name(result); -} - -bool Definition::exampleToScript(string* result, ExampleOptions exampleOptions) const { - bool hasFiddle = true; - const Definition* platform = this->hasChild(MarkType::kPlatform); - if (platform) { - TextParser platParse(platform); - hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd); - } - if (!hasFiddle) { - *result = ""; - return true; - } - string text = this->extractText(Definition::TrimExtract::kNo); - const char drawWrapper[] = "void draw(SkCanvas* canvas) {"; - const char drawNoCanvas[] = "void draw(SkCanvas* ) {"; - size_t nonSpace = 0; - while (nonSpace < text.length() && ' ' >= text[nonSpace]) { - ++nonSpace; - } - bool hasFunc = !text.compare(nonSpace, sizeof(drawWrapper) - 1, drawWrapper); - bool noCanvas = !text.compare(nonSpace, sizeof(drawNoCanvas) - 1, drawNoCanvas); - bool hasCanvas = string::npos != text.find("SkCanvas canvas"); - SkASSERT(!hasFunc || !noCanvas); - bool textOut = string::npos != text.find("SkDebugf(") - || string::npos != text.find("dump(") - || string::npos != text.find("dumpHex("); - string heightStr = "256"; - string widthStr = "256"; - bool preprocessor = text[0] == '#'; - string normalizedName(fFiddle); - string code; - string imageStr = "0"; - for (auto const& iter : fChildren) { - switch (iter->fMarkType) { - case MarkType::kError: - result->clear(); - return true; - case MarkType::kHeight: - heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); - break; - case MarkType::kWidth: - widthStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); - break; - case MarkType::kDescription: - // ignore for now - break; - case MarkType::kFunction: { - // emit this, but don't wrap this in draw() - string funcText(iter->fContentStart, iter->fContentEnd - iter->fContentStart - 1); - size_t pos = 0; - while (pos < funcText.length() && ' ' > funcText[pos]) { - ++pos; - } - size_t indent = count_indent(funcText, pos, funcText.length()); - add_code(funcText, pos, funcText.length(), 0, indent, code); - code += "\\n"; - } break; - case MarkType::kComment: - break; - case MarkType::kImage: - imageStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); - break; - case MarkType::kToDo: - break; - case MarkType::kMarkChar: - case MarkType::kPlatform: - // ignore for now - break; - case MarkType::kStdOut: - textOut = true; - break; - default: - SkASSERT(0); // more coding to do - } - } - string textOutStr = textOut ? "true" : "false"; - size_t pos = 0; - while (pos < text.length() && ' ' > text[pos]) { - ++pos; - } - size_t end = text.length(); - size_t outIndent = 0; - size_t textIndent = count_indent(text, pos, end); - bool wrapCode = !hasFunc && !noCanvas && !preprocessor; - if (wrapCode) { - code += hasCanvas ? drawNoCanvas : drawWrapper; - code += "\\n"; - outIndent = 4; - } - add_code(text, pos, end, outIndent, textIndent, code); - if (wrapCode) { - code += "}"; - } - string example = "\"" + normalizedName + "\": {\n"; - - string baseFile = [this]() { - string baseNameExt = fFileName.substr(fFileName.find_last_of("/\\") + 1); - size_t p = baseNameExt.find_last_of('.'); - return (p > 0 && p != string::npos) ? baseNameExt.substr(0, p) : baseNameExt; - }(); - if (ExampleOptions::kText == exampleOptions) { - example += " \"code\": \"" + code + "\",\n"; - example += " \"hash\": \"" + fHash + "\",\n"; - example += " \"file\": \"" + baseFile + "\",\n"; - example += " \"name\": \"" + fName + "\","; - } else { - example += " \"code\": \"" + code + "\",\n"; - if (ExampleOptions::kPng == exampleOptions) { - example += " \"width\": " + widthStr + ",\n"; - example += " \"height\": " + heightStr + ",\n"; - example += " \"hash\": \"" + fHash + "\",\n"; - example += " \"file\": \"" + baseFile + "\",\n"; - example += " \"name\": \"" + fName + "\"\n"; - example += "}"; - } else { - example += " \"options\": {\n"; - example += " \"width\": " + widthStr + ",\n"; - example += " \"height\": " + heightStr + ",\n"; - example += " \"source\": " + imageStr + ",\n"; - example += " \"srgb\": false,\n"; - example += " \"f16\": false,\n"; - example += " \"textOnly\": " + textOutStr + ",\n"; - example += " \"animated\": false,\n"; - example += " \"duration\": 0\n"; - example += " },\n"; - example += " \"fast\": true"; - } - } - *result = example; - return true; -} - -static void space_pad(string* str) { - size_t len = str->length(); - if (len == 0) { - return; - } - char last = (*str)[len - 1]; - if ('~' == last || ' ' >= last) { - return; - } - *str += ' '; -} - -//start here; -// see if it possible to abstract this a little bit so it can -// additionally be used to find params and return in method prototype that -// does not have corresponding doxygen comments -bool Definition::checkMethod() const { - SkASSERT(MarkType::kMethod == fMarkType); - // if method returns a value, look for a return child - // for each parameter, look for a corresponding child - const char* end = fContentStart; - while (end > fStart && ' ' >= end[-1]) { - --end; - } - TextParser methodParser(fFileName, fStart, end, fLineCount); - methodParser.skipWhiteSpace(); - SkASSERT(methodParser.startsWith("#Method")); - methodParser.skipName("#Method"); - methodParser.skipSpace(); - string name = this->methodName(); - if (MethodType::kNone == fMethodType && name.length() > 2 && - "()" == name.substr(name.length() - 2)) { - name = name.substr(0, name.length() - 2); - } - bool expectReturn = this->methodHasReturn(name, &methodParser); - bool foundReturn = false; - bool foundException = false; - for (auto& child : fChildren) { - foundException |= MarkType::kDeprecated == child->fMarkType - || MarkType::kExperimental == child->fMarkType; - if (MarkType::kReturn != child->fMarkType) { - if (MarkType::kParam == child->fMarkType) { - child->fVisited = false; - } - continue; - } - if (!expectReturn) { - return methodParser.reportError<bool>("no #Return expected"); - } - if (foundReturn) { - return methodParser.reportError<bool>("multiple #Return markers"); - } - foundReturn = true; - } - if (expectReturn && !foundReturn && !foundException) { - return methodParser.reportError<bool>("missing #Return marker"); - } - const char* paren = methodParser.strnchr('(', methodParser.fEnd); - if (!paren) { - return methodParser.reportError<bool>("missing #Method function definition"); - } - const char* nextEnd = paren; - do { - string paramName; - methodParser.fChar = nextEnd + 1; - methodParser.skipSpace(); - if (!this->nextMethodParam(&methodParser, &nextEnd, ¶mName)) { - continue; - } - bool foundParam = false; - for (auto& child : fChildren) { - if (MarkType::kParam != child->fMarkType) { - continue; - } - if (paramName != child->fName) { - continue; - } - if (child->fVisited) { - return methodParser.reportError<bool>("multiple #Method param with same name"); - } - child->fVisited = true; - if (foundParam) { - TextParser paramError(child); - return methodParser.reportError<bool>("multiple #Param with same name"); - } - foundParam = true; - - } - if (!foundParam && !foundException) { - return methodParser.reportError<bool>("no #Param found"); - } - if (')' == nextEnd[0]) { - break; - } - } while (')' != nextEnd[0]); - for (auto& child : fChildren) { - if (MarkType::kParam != child->fMarkType) { - continue; - } - if (!child->fVisited) { - TextParser paramError(child); - return paramError.reportError<bool>("#Param without param in #Method"); - } - } - return true; -} - -bool Definition::crossCheck2(const Definition& includeToken) const { - TextParser parser(fFileName, fStart, fContentStart, fLineCount); - parser.skipExact("#"); - bool isMethod = parser.skipName("Method"); - const char* contentEnd; - if (isMethod) { - contentEnd = fContentStart; - } else if (parser.skipName("DefinedBy")) { - contentEnd = fContentEnd; - while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) { - --contentEnd; - } - if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) { - contentEnd -= 2; - } - } else { - return parser.reportError<bool>("unexpected crosscheck marktype"); - } - return crossCheckInside(parser.fChar, contentEnd, includeToken); -} - -bool Definition::crossCheck(const Definition& includeToken) const { - return crossCheckInside(fContentStart, fContentEnd, includeToken); -} - -bool Definition::crossCheckInside(const char* start, const char* end, - const Definition& includeToken) const { - TextParser def(fFileName, start, end, fLineCount); - TextParser inc("", includeToken.fContentStart, includeToken.fContentEnd, 0); - if (inc.startsWith("SK_API")) { - inc.skipWord("SK_API"); - } - if (inc.startsWith("friend")) { - inc.skipWord("friend"); - } - if (inc.startsWith("SK_API")) { - inc.skipWord("SK_API"); - } - do { - bool defEof; - bool incEof; - do { - defEof = def.eof() || !def.skipWhiteSpace(); - incEof = inc.eof() || !inc.skipWhiteSpace(); - if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) { - inc.next(); - if ('*' == inc.peek()) { - inc.skipToEndBracket("*/"); - inc.next(); - } else if ('/' == inc.peek()) { - inc.skipToEndBracket('\n'); - } - } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) { - inc.next(); - if (inc.startsWith("if")) { - inc.skipToEndBracket("\n"); - } else if (inc.startsWith("endif")) { - inc.skipToEndBracket("\n"); - } else { - SkASSERT(0); // incomplete - return false; - } - } else { - break; - } - inc.next(); - } while (true); - if (defEof || incEof) { - if (defEof == incEof || (!defEof && ';' == def.peek())) { - return true; - } - return false; // allow setting breakpoint on failure - } - char defCh; - do { - defCh = def.next(); - char incCh = inc.next(); - if (' ' >= defCh && ' ' >= incCh) { - break; - } - if (defCh != incCh) { - return false; - } - if (';' == defCh) { - return true; - } - } while (!def.eof() && !inc.eof()); - } while (true); - return false; -} - -string Definition::formatFunction() const { - const char* end = fContentStart; - while (end > fStart && ' ' >= end[-1]) { - --end; - } - TextParser methodParser(fFileName, fStart, end, fLineCount); - methodParser.skipWhiteSpace(); - SkASSERT(methodParser.startsWith("#Method")); - methodParser.skipName("#Method"); - methodParser.skipSpace(); - const char* lastStart = methodParser.fChar; - const int limit = 80; // todo: allow this to be set by caller or in global or something - string methodStr; - string name = this->methodName(); - const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd); - methodParser.skipTo(nameInParser); - const char* lastEnd = methodParser.fChar; - const char* paren = methodParser.strnchr('(', methodParser.fEnd); - size_t indent; - if (paren) { - indent = (size_t) (paren - lastStart) + 1; - } else { - indent = (size_t) (lastEnd - lastStart); - } - int written = 0; - do { - const char* nextStart = lastEnd; - SkASSERT(written < limit); - const char* delimiter = methodParser.anyOf(",)"); - const char* nextEnd = delimiter ? delimiter : methodParser.fEnd; - if (delimiter) { - while (nextStart < nextEnd && ' ' >= nextStart[0]) { - ++nextStart; - } - } - while (nextEnd > nextStart && ' ' >= nextEnd[-1]) { - --nextEnd; - } - if (delimiter) { - nextEnd += 1; - delimiter += 1; - } - if (lastEnd > lastStart) { - if (lastStart[0] != ' ') { - space_pad(&methodStr); - } - methodStr += string(lastStart, (size_t) (lastEnd - lastStart)); - written += (size_t) (lastEnd - lastStart); - } - if (delimiter) { - if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) { - written = indent; - methodStr += '\n'; - methodStr += string(indent, ' '); - } - methodParser.skipTo(delimiter); - } - lastStart = nextStart; - lastEnd = nextEnd; - } while (lastStart < lastEnd); - return methodStr; -} - -string Definition::fiddleName() const { - string result; - size_t start = 0; - string parent; - const Definition* parentDef = this; - while ((parentDef = parentDef->fParent)) { - if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { - parent = parentDef->fFiddle; - break; - } - } - if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) { - start = parent.length(); - while (start < fFiddle.length() && '_' == fFiddle[start]) { - ++start; - } - } - size_t end = fFiddle.find_first_of('(', start); - return fFiddle.substr(start, end - start); -} - -const Definition* Definition::hasChild(MarkType markType) const { - for (auto iter : fChildren) { - if (markType == iter->fMarkType) { - return iter; - } - } - return nullptr; -} - -const Definition* Definition::hasParam(const string& ref) const { - SkASSERT(MarkType::kMethod == fMarkType); - for (auto iter : fChildren) { - if (MarkType::kParam != iter->fMarkType) { - continue; - } - if (iter->fName == ref) { - return &*iter; - } - - } - return nullptr; -} - -bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const { - if (methodParser->skipExact("static")) { - methodParser->skipWhiteSpace(); - } - const char* lastStart = methodParser->fChar; - const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd); - methodParser->skipTo(nameInParser); - const char* lastEnd = methodParser->fChar; - const char* returnEnd = lastEnd; - while (returnEnd > lastStart && ' ' == returnEnd[-1]) { - --returnEnd; - } - bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4); - if (MethodType::kNone != fMethodType && !expectReturn) { - return methodParser->reportError<bool>("unexpected void"); - } - switch (fMethodType) { - case MethodType::kNone: - case MethodType::kOperator: - // either is fine - break; - case MethodType::kConstructor: - expectReturn = true; - break; - case MethodType::kDestructor: - expectReturn = false; - break; - } - return expectReturn; -} - -string Definition::methodName() const { - string result; - size_t start = 0; - string parent; - const Definition* parentDef = this; - while ((parentDef = parentDef->fParent)) { - if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { - parent = parentDef->fName; - break; - } - } - if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) { - start = parent.length(); - while (start < fName.length() && ':' == fName[start]) { - ++start; - } - } - if (fClone) { - int lastUnder = fName.rfind('_'); - return fName.substr(start, (size_t) (lastUnder - start)); - } - size_t end = fName.find_first_of('(', start); - if (string::npos == end) { - return fName.substr(start); - } - return fName.substr(start, end - start); -} - -bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr, - string* paramName) const { - int parenCount = 0; - TextParser::Save saveState(methodParser); - while (true) { - if (methodParser->eof()) { - return methodParser->reportError<bool>("#Method function missing close paren"); - } - char ch = methodParser->peek(); - if ('(' == ch) { - ++parenCount; - } - if (parenCount == 0 && (')' == ch || ',' == ch)) { - *nextEndPtr = methodParser->fChar; - break; - } - if (')' == ch) { - if (0 > --parenCount) { - return this->reportError<bool>("mismatched parentheses"); - } - } - methodParser->next(); - } - saveState.restore(); - const char* nextEnd = *nextEndPtr; - const char* paramEnd = nextEnd; - const char* assign = methodParser->strnstr(" = ", paramEnd); - if (assign) { - paramEnd = assign; - } - const char* closeBracket = methodParser->strnstr("]", paramEnd); - if (closeBracket) { - const char* openBracket = methodParser->strnstr("[", paramEnd); - if (openBracket && openBracket < closeBracket) { - while (openBracket < --closeBracket && isdigit(closeBracket[0])) - ; - if (openBracket == closeBracket) { - paramEnd = openBracket; - } - } - } - const char* function = methodParser->strnstr(")(", paramEnd); - if (function) { - paramEnd = function; - } - while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) { - --paramEnd; - } - const char* paramStart = paramEnd; - while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) { - --paramStart; - } - if (paramStart > methodParser->fChar && paramStart >= paramEnd) { - return methodParser->reportError<bool>("#Method missing param name"); - } - *paramName = string(paramStart, paramEnd - paramStart); - if (!paramName->length()) { - if (')' != nextEnd[0]) { - return methodParser->reportError<bool>("#Method malformed param"); - } - return false; - } - return true; -} - - bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix) { -// this->reset(); - if (!sk_isdir(fileOrPath)) { - if (!this->parseFromFile(fileOrPath)) { - SkDebugf("failed to parse %s\n", fileOrPath); - return false; - } - } else { - SkOSFile::Iter it(fileOrPath, suffix); - for (SkString file; it.next(&file); ) { - SkString p = SkOSPath::Join(fileOrPath, file.c_str()); - const char* hunk = p.c_str(); - if (!SkStrEndsWith(hunk, suffix)) { - continue; - } - if (!this->parseFromFile(hunk)) { - SkDebugf("failed to parse %s\n", hunk); - return false; - } - } - } - return true; -} - -bool Definition::paramsMatch(const string& match, const string& name) const { - TextParser def(fFileName, fStart, fContentStart, fLineCount); - const char* dName = def.strnstr(name.c_str(), fContentStart); - if (!dName) { - return false; - } - def.skipTo(dName); - TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount); - const char* mName = m.strnstr(name.c_str(), m.fEnd); - if (!mName) { - return false; - } - m.skipTo(mName); - while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) { - const char* ds = def.fChar; - const char* ms = m.fChar; - const char* de = def.anyOf(") \n"); - const char* me = m.anyOf(") \n"); - def.skipTo(de); - m.skipTo(me); - if (def.fChar - ds != m.fChar - ms) { - return false; - } - if (strncmp(ds, ms, (int) (def.fChar - ds))) { - return false; - } - def.skipWhiteSpace(); - m.skipWhiteSpace(); - } - return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek(); -} - -void RootDefinition::clearVisited() { - fVisited = false; - for (auto& leaf : fLeaves) { - leaf.second.fVisited = false; - } - for (auto& branch : fBranches) { - branch.second->clearVisited(); - } -} - -bool RootDefinition::dumpUnVisited() { - bool allStructElementsFound = true; - for (auto& leaf : fLeaves) { - if (!leaf.second.fVisited) { - // TODO: parse embedded struct in includeParser phase, then remove this condition - if (FLAGS_skip) { - const Definition& def = leaf.second; - if (def.fChildren.size() > 0 && - MarkType::kDeprecated == def.fChildren[0]->fMarkType) { - continue; - } - } - SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str()); - } - } - for (auto& branch : fBranches) { - allStructElementsFound &= branch.second->dumpUnVisited(); - } - return allStructElementsFound; -} - -const Definition* RootDefinition::find(const string& ref, AllowParens allowParens) const { - const auto leafIter = fLeaves.find(ref); - if (leafIter != fLeaves.end()) { - return &leafIter->second; - } - if (AllowParens::kYes == allowParens && string::npos == ref.find("()")) { - string withParens = ref + "()"; - const auto parensIter = fLeaves.find(withParens); - if (parensIter != fLeaves.end()) { - return &parensIter->second; - } - } - const auto branchIter = fBranches.find(ref); - if (branchIter != fBranches.end()) { - const RootDefinition* rootDef = branchIter->second; - return rootDef; - } - const Definition* result = nullptr; - for (const auto& branch : fBranches) { - const RootDefinition* rootDef = branch.second; - result = rootDef->find(ref, allowParens); - if (result) { - break; - } - } - return result; -} - /* class contains named struct, enum, enum-member, method, topic, subtopic everything contained by class is uniquely named @@ -1008,7 +158,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy definition->fFiddle = name; } } else { - definition->fFiddle = normalized_name(name); + definition->fFiddle = Definition::NormalizedName(name); } definition->fMarkType = markType; definition->fAnonymous = fAnonymous; @@ -1034,7 +184,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy parent = parent->fParent; } definition->fFiddle = parent ? parent->fFiddle + '_' : ""; - definition->fFiddle += normalized_name(typeNameBuilder[0]); + definition->fFiddle += Definition::NormalizedName(typeNameBuilder[0]); this->setAsParent(definition); } { @@ -1075,7 +225,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy definition->fStart = defStart; definition->fContentStart = start; definition->fName = name; - definition->fFiddle = normalized_name(name); + definition->fFiddle = Definition::NormalizedName(name); definition->fContentEnd = fChar; this->skipToEndBracket('\n'); definition->fTerminator = fChar; @@ -1147,6 +297,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy return this->reportError<bool>("missing example body"); } } + definition->setWrapper(); } } else { fMarkup.emplace_front(markType, defStart, fLineCount, fParent); @@ -1202,7 +353,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy fMarkup.emplace_front(markType, defStart, fLineCount, fParent); definition = &fMarkup.front(); definition->fName = typeNameBuilder[0]; - definition->fFiddle = normalized_name(typeNameBuilder[0]); + definition->fFiddle = Definition::NormalizedName(typeNameBuilder[0]); definition->fContentStart = fChar; definition->fContentEnd = this->trimmedBracketEnd('\n'); definition->fTerminator = this->lineEnd() - 1; @@ -1412,7 +563,7 @@ bool BmhParser::collectExternals() { RootDefinition* definition = &fExternals.front(); definition->fFileName = fFileName; definition->fName = string(wordStart ,fChar - wordStart); - definition->fFiddle = normalized_name(definition->fName); + definition->fFiddle = Definition::NormalizedName(definition->fName); } } while (!this->eof()); return true; @@ -1631,7 +782,7 @@ bool BmhParser::findDefinitions() { } } if (fParent) { - return this->reportError<bool>("mismatched end"); + return fParent->reportError<bool>("mismatched end"); } return true; } @@ -1799,10 +950,20 @@ string BmhParser::methodName() { } builder = parent->fName + "::"; } + bool addConst = false; if (isConstructor || expectOperator) { paren = this->strnchr(')', end) + 1; + TextParser::Save saveState(this); + this->skipTo(paren); + if (this->skipExact("_const")) { + addConst = true; + } + saveState.restore(); } builder.append(nameStart, paren - nameStart); + if (addConst) { + builder.append("_const"); + } if (!expectOperator && allLower) { builder.append("()"); } @@ -1998,7 +1159,6 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { case MarkType::kEnumClass: case MarkType::kClass: case MarkType::kStruct: - case MarkType::kTypedef: // expect name builder = this->className(markType); break; @@ -2052,6 +1212,9 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { case MarkType::kMethod: builder = this->methodName(); break; + case MarkType::kTypedef: + builder = this->typedefName(); + break; case MarkType::kParam: // fixme: expect camelCase builder = this->word("", ""); @@ -2078,6 +1241,82 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) { return result; } +string BmhParser::typedefName() { + if (this->hasEndToken()) { + if (!fParent || !fParent->fName.length()) { + return this->reportError<string>("missing parent typedef name"); + } + SkASSERT(fMC == this->peek()); + this->next(); + SkASSERT(fMC == this->peek()); + this->next(); + SkASSERT(fMC != this->peek()); + return fParent->fName; + } + // look for typedef as one of three forms: + // typedef return-type (*NAME)(params); + // typedef alias NAME; + // typedef std::function<alias> NAME; + string builder; + const char* end = this->doubleLF(); + if (!end) { + end = fEnd; + } + const char* altEnd = this->strnstr("#Typedef ##", end); + if (altEnd) { + end = this->strnchr('\n', end); + } + if (!end) { + return this->reportError<string>("missing typedef std::function end bracket >"); + } + + if (this->startsWith("std::function")) { + if (!this->skipToEndBracket('>')) { + return this->reportError<string>("missing typedef std::function end bracket >"); + } + this->next(); + this->skipWhiteSpace(); + builder = string(fChar, end - fChar); + } else { + const char* paren = this->strnchr('(', end); + if (!paren) { + const char* lastWord = nullptr; + do { + this->skipToWhiteSpace(); + if (fChar < end && isspace(fChar[0])) { + this->skipWhiteSpace(); + lastWord = fChar; + } else { + break; + } + } while (true); + if (!lastWord) { + return this->reportError<string>("missing typedef name"); + } + builder = string(lastWord, end - lastWord); + } else { + this->skipTo(paren); + this->next(); + if ('*' != this->next()) { + return this->reportError<string>("missing typedef function asterisk"); + } + const char* nameStart = fChar; + if (!this->skipToEndBracket(')')) { + return this->reportError<string>("missing typedef function )"); + } + builder = string(nameStart, fChar - nameStart); + if (!this->skipToEndBracket('(')) { + return this->reportError<string>("missing typedef params ("); + } + if (! this->skipToEndBracket(')')) { + return this->reportError<string>("missing typedef params )"); + } + this->skipTo(end); + } + } + return uniqueRootName(builder, MarkType::kTypedef); +} + string BmhParser::uniqueName(const string& base, MarkType markType) { string builder(base); if (!builder.length()) { @@ -2109,6 +1348,9 @@ string BmhParser::uniqueRootName(const string& base, MarkType markType) { return markType == def.fMarkType && def.fName == numBuilder; }; + if (string::npos != base.find("SkMatrix::operator")) { + SkDebugf(""); + } string builder(base); if (!builder.length()) { builder = fParent->fName; @@ -2147,6 +1389,9 @@ tryNext: ; cloned->fCloned = true; } fCloned = true; + if (string::npos != builder.find("operator")) { + SkDebugf(""); + } numBuilder = builder + '_' + to_string(number); } while (++number); return numBuilder; @@ -2232,11 +1477,11 @@ static int count_children(const Definition& def, MarkType markType) { } int main(int argc, char** const argv) { - BmhParser bmhParser; + BmhParser bmhParser(FLAGS_skip); bmhParser.validate(); SkCommandLineFlags::SetUsage( - "Common Usage: bookmaker -i path/to/include.h -t\n" + "Common Usage: bookmaker -b path/to/bmh_files -i path/to/include.h -t\n" " bookmaker -b path/to/bmh_files -e fiddle.json\n" " ~/go/bin/fiddlecli --input fiddle.json --output fiddleout.json\n" " bookmaker -b path/to/bmh_files -f fiddleout.json -r path/to/md_files\n" @@ -2308,8 +1553,8 @@ int main(int argc, char** const argv) { SkCommandLineFlags::PrintUsage(); return 1; } - if (FLAGS_include.isEmpty() && !FLAGS_tokens.isEmpty()) { - SkDebugf("-t requires -i\n"); + if ((FLAGS_include.isEmpty() || FLAGS_bmh.isEmpty()) && FLAGS_tokens) { + SkDebugf("-t requires -b -i\n"); SkCommandLineFlags::PrintUsage(); return 1; } @@ -2326,15 +1571,15 @@ int main(int argc, char** const argv) { } bool done = false; if (!FLAGS_include.isEmpty()) { - if (!FLAGS_tokens.isEmpty() || FLAGS_crosscheck) { + if (FLAGS_tokens || FLAGS_crosscheck) { IncludeParser includeParser; includeParser.validate(); if (!includeParser.parseFile(FLAGS_include[0], ".h")) { return -1; } - if (!FLAGS_tokens.isEmpty()) { + if (FLAGS_tokens) { includeParser.fDebugOut = FLAGS_stdout; - if (includeParser.dumpTokens(FLAGS_tokens[0])) { + if (includeParser.dumpTokens(FLAGS_bmh[0])) { bmhParser.fWroteOut = true; } done = true; diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h index 167fcb24f7..8cd128804c 100644 --- a/tools/bookmaker/bookmaker.h +++ b/tools/bookmaker/bookmaker.h @@ -303,6 +303,33 @@ public: return *loc; } + const char* doubleLF() const { + int count = 0; + const char* ptr = fChar; + const char* doubleStart = nullptr; + while (ptr < fEnd) { + if ('\n' == ptr[0]) { + if (++count == 1) { + doubleStart = ptr; + } else { + return doubleStart; + } + } else if (' ' < ptr[0]) { + count = 0; + } + ++ptr; + } + return nullptr; + } + + bool endsWith(const char* match) { + int matchLen = strlen(match); + if (matchLen > fChar - fLine) { + return false; + } + return !strncmp(fChar - matchLen, match, matchLen); + } + bool eof() const { return fChar >= fEnd; } const char* lineEnd() const { @@ -431,7 +458,7 @@ public: // since a.b can't be found as a named definition void skipFullName() { while (fChar < fEnd && (isalnum(fChar[0]) - || '_' == fChar[0] || '-' == fChar[0] + || '_' == fChar[0] /* || '-' == fChar[0] */ || (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]))) { if (':' == fChar[0] && fChar +1 < fEnd && ':' == fChar[1]) { fChar++; @@ -468,6 +495,12 @@ public: } } + void skipToWhiteSpace() { + while (fChar < fEnd && ' ' < fChar[0]) { + fChar++; + } + } + bool skipName(const char* word) { size_t len = strlen(word); if (len <= (size_t) (fEnd - fChar) && !strncmp(word, fChar, len)) { @@ -637,10 +670,11 @@ public: for (int i = 0; i < 4; ++i) { unicode <<= 4; SkASSERT((reader[0] >= '0' && reader[0] <= '9') || - (reader[0] >= 'A' && reader[0] <= 'F')); + (reader[0] >= 'A' && reader[0] <= 'F') || + (reader[0] >= 'a' && reader[0] <= 'f')); int nibble = *reader++ - '0'; if (nibble > 9) { - nibble = 'A'- '9' + 1; + nibble = (nibble & ~('a' - 'A')) - 'A' + '9' + 1; } unicode |= nibble; } @@ -695,6 +729,26 @@ public: kOperator, }; + enum class Operator { + kUnknown, + kAdd, + kAddTo, + kArray, + kCast, + kCopy, + kDelete, + kDereference, + kEqual, + kMinus, + kMove, + kMultiply, + kMultiplyBy, + kNew, + kNotEqual, + kSubtract, + kSubtractFrom, + }; + Definition() {} Definition(const char* start, const char* end, int line, Definition* parent) @@ -744,116 +798,19 @@ public: virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; } virtual const RootDefinition* asRoot() const { SkASSERT(0); return nullptr; } - - bool boilerplateIfDef(Definition* parent) { - const Definition& label = fTokens.front(); - if (Type::kWord != label.fType) { - return false; - } - fName = string(label.fContentStart, label.fContentEnd - label.fContentStart); - return true; - } - - // todo: this is matching #ifndef SkXXX_DEFINED for no particular reason - // it doesn't do anything useful with arbitrary input, e.g. #ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS -// also doesn't know what to do with SK_REQUIRE_LOCAL_VAR() - bool boilerplateDef(Definition* parent) { - if (!this->boilerplateIfDef(parent)) { - return false; - } - const char* s = fName.c_str(); - const char* e = strchr(s, '_'); - return true; // fixme: if this is trying to do something useful with define, do it here - if (!e) { - return false; - } - string prefix(s, e - s); - const char* inName = strstr(parent->fName.c_str(), prefix.c_str()); - if (!inName) { - return false; - } - if ('/' != inName[-1] && '\\' != inName[-1]) { - return false; - } - if (strcmp(inName + prefix.size(), ".h")) { - return false; - } - return true; - } + bool boilerplateIfDef(Definition* parent); + bool boilerplateDef(Definition* parent); bool boilerplateEndIf() { return true; } bool checkMethod() const; - - void setCanonicalFiddle(); bool crossCheck2(const Definition& includeToken) const; bool crossCheck(const Definition& includeToken) const; bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const; bool exampleToScript(string* result, ExampleOptions ) const; - - string extractText(TrimExtract trimExtract) const { - string result; - TextParser parser(fFileName, fContentStart, fContentEnd, fLineCount); - int childIndex = 0; - char mc = '#'; - while (parser.fChar < parser.fEnd) { - if (TrimExtract::kYes == trimExtract && !parser.skipWhiteSpace()) { - break; - } - if (parser.next() == mc) { - if (parser.next() == mc) { - if (parser.next() == mc) { - mc = parser.next(); - } - } else { - // fixme : more work to do if # style comment is in text - // if in method definition, could be alternate method name - --parser.fChar; - if (' ' < parser.fChar[0]) { - if (islower(parser.fChar[0])) { - result += '\n'; - parser.skipLine(); - } else { - SkASSERT(isupper(parser.fChar[0])); - parser.skipTo(fChildren[childIndex]->fTerminator); - if (mc == parser.fChar[0] && mc == parser.fChar[1]) { - parser.next(); - parser.next(); - } - childIndex++; - } - } else { - parser.skipLine(); - } - continue; - } - } else { - --parser.fChar; - } - const char* end = parser.fEnd; - const char* mark = parser.strnchr(mc, end); - if (mark) { - end = mark; - } - string fragment(parser.fChar, end - parser.fChar); - trim_end(fragment); - if (TrimExtract::kYes == trimExtract) { - trim_start(fragment); - if (result.length()) { - result += '\n'; - result += '\n'; - } - } - if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) { - result += fragment; - } - parser.skipTo(end); - } - return result; - } - + string extractText(TrimExtract trimExtract) const; string fiddleName() const; string formatFunction() const; const Definition* hasChild(MarkType markType) const; @@ -881,7 +838,9 @@ public: string methodName() const; bool nextMethodParam(TextParser* methodParser, const char** nextEndPtr, string* paramName) const; + static string NormalizedName(string name); bool paramsMatch(const string& fullRef, const string& name) const; + bool parseOperator(size_t doubleColons, string& result); string printableName() const { string result(fName); @@ -896,11 +855,14 @@ public: } virtual RootDefinition* rootParent() { SkASSERT(0); return nullptr; } + void setCanonicalFiddle(); void setParentIndex() { fParentIndex = fParent ? (int) fParent->fTokens.size() : -1; } + void setWrapper(); + string fText; // if text is constructed instead of in a file, it's put here const char* fStart = nullptr; // .. in original text file, or the start of fText const char* fContentStart; // start past optional markup name @@ -913,6 +875,7 @@ public: vector<Definition*> fChildren; string fHash; // generated by fiddle string fFileName; + mutable string fWrapper; // used by Example to wrap into proper function size_t fLineCount = 0; int fParentIndex = 0; MarkType fMarkType = MarkType::kNone; @@ -920,9 +883,11 @@ public: Bracket fBracket = Bracket::kNone; Punctuation fPunctuation = Punctuation::kNone; MethodType fMethodType = MethodType::kNone; + Operator fOperator = Operator::kUnknown; Type fType = Type::kNone; bool fClone = false; bool fCloned = false; + bool fOperatorConst = false; bool fPrivate = false; bool fShort = false; bool fMemberStart = false; @@ -957,7 +922,7 @@ public: RootDefinition* asRoot() override { return this; } const RootDefinition* asRoot() const override { return this; } void clearVisited(); - bool dumpUnVisited(); + bool dumpUnVisited(bool skip); const Definition* find(const string& ref, AllowParens ) const; bool isRoot() const override { return true; } RootDefinition* rootParent() override { return fRootParent; } @@ -1190,7 +1155,7 @@ public: #define E_N Exemplary::kNo #define E_O Exemplary::kOptional - BmhParser() : ParserCommon() + BmhParser(bool skip) : ParserCommon() , fMaps { // names without formal definitions (e.g. Column) aren't included // fill in other names once they're actually used @@ -1247,10 +1212,11 @@ public: , { "ToDo", nullptr, MarkType::kToDo, R_N, E_N, 0 } , { "Topic", nullptr, MarkType::kTopic, R_Y, E_Y, M_CS | M(Root) | M(Topic) } , { "Track", nullptr, MarkType::kTrack, R_Y, E_N, M_E | M_ST } -, { "Typedef", &fTypedefMap, MarkType::kTypedef, R_Y, E_N, M(Subtopic) | M(Topic) } +, { "Typedef", &fTypedefMap, MarkType::kTypedef, R_Y, E_N, M(Class) | M_ST } , { "", nullptr, MarkType::kUnion, R_Y, E_N, 0 } , { "Volatile", nullptr, MarkType::kVolatile, R_N, E_N, M(StdOut) } , { "Width", nullptr, MarkType::kWidth, R_N, E_N, M(Example) } } +, fSkip(skip) { this->reset(); } @@ -1325,6 +1291,7 @@ public: void spellCheck(const char* match, SkCommandLineFlags::StringArray report) const; vector<string> topicName(); vector<string> typeName(MarkType markType, bool* expectEnd); + string typedefName(); string uniqueName(const string& base, MarkType markType); string uniqueRootName(const string& base, MarkType markType); void validate() const; @@ -1366,6 +1333,7 @@ public: bool fInComment; bool fInString; bool fCheckMethods; + bool fSkip = false; bool fWroteOut = false; private: typedef ParserCommon INHERITED; @@ -1707,6 +1675,7 @@ protected: unordered_map<string, IClassDefinition> fIClassMap; unordered_map<string, Definition> fIDefineMap; unordered_map<string, Definition> fIEnumMap; + unordered_map<string, Definition> fIFunctionMap; unordered_map<string, Definition> fIStructMap; unordered_map<string, Definition> fITemplateMap; unordered_map<string, Definition> fITypedefMap; @@ -1988,6 +1957,7 @@ private: fHasFiddle = false; fInDescription = false; fInList = false; + fRespectLeadingSpace = false; } BmhParser::Resolvable resolvable(const Definition* definition) const { @@ -2018,6 +1988,7 @@ private: bool fInDescription; // FIXME: for now, ignore unfound camelCase in description since it may // be defined in example which at present cannot be linked to bool fInList; + bool fRespectLeadingSpace; typedef ParserCommon INHERITED; }; @@ -2054,10 +2025,11 @@ public: } } if (this->startsWith(fClassName.c_str()) || this->startsWith("operator")) { - const char* ptr = this->anyOf(" ("); + const char* ptr = this->anyOf("\n ("); if (ptr && '(' == *ptr) { this->skipToEndBracket(')'); SkAssertResult(')' == this->next()); + this->skipExact("_const"); return; } } @@ -2066,6 +2038,14 @@ public: this->skipToNonAlphaNum(); } else { this->skipFullName(); + if (this->endsWith("operator")) { + const char* ptr = this->anyOf("\n ("); + if (ptr && '(' == *ptr) { + this->skipToEndBracket(')'); + SkAssertResult(')' == this->next()); + this->skipExact("_const"); + } + } } } diff --git a/tools/bookmaker/definition.cpp b/tools/bookmaker/definition.cpp new file mode 100644 index 0000000000..44da2df268 --- /dev/null +++ b/tools/bookmaker/definition.cpp @@ -0,0 +1,1274 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "bookmaker.h" + +static size_t count_indent(const string& text, size_t test, size_t end) { + size_t result = test; + while (test < end) { + if (' ' != text[test]) { + break; + } + ++test; + } + return test - result; +} + +static void add_code(const string& text, int pos, int end, + size_t outIndent, size_t textIndent, string& example) { + do { + // fix this to move whole paragraph in, out, but preserve doc indent + int nextIndent = count_indent(text, pos, end); + size_t len = text.find('\n', pos); + if (string::npos == len) { + len = end; + } + if ((size_t) (pos + nextIndent) < len) { + size_t indent = outIndent + nextIndent; + SkASSERT(indent >= textIndent); + indent -= textIndent; + for (size_t index = 0; index < indent; ++index) { + example += ' '; + } + pos += nextIndent; + while ((size_t) pos < len) { + example += '"' == text[pos] ? "\\\"" : + '\\' == text[pos] ? "\\\\" : + text.substr(pos, 1); + ++pos; + } + example += "\\n"; + } else { + pos += nextIndent; + } + if ('\n' == text[pos]) { + ++pos; + } + } while (pos < end); +} + +#ifdef CONST +#undef CONST +#endif + +#ifdef FRIEND +#undef FRIEND +#endif + +#ifdef BLANK +#undef BLANK +#endif + +#ifdef ANY +#undef ANY +#endif + +#ifdef DEFOP +#undef DEFOP +#endif + +#define CONST 1 +#define STATIC 2 +#define BLANK 0 +#define ANY -1 +#define DEFOP Definition::Operator + +enum class OpType : int8_t { + kNone, + kVoid, + kBool, + kChar, + kFloat, + kInt, + kScalar, + kSizeT, + kThis, + kAny, +}; + +enum class OpMod : int8_t { + kNone, + kArray, + kMove, + kPointer, + kReference, + kAny, +}; + +const struct OperatorParser { + DEFOP fOperator; + const char* fSymbol; + const char* fName; + int8_t fFriend; + OpType fReturnType; + OpMod fReturnMod; + int8_t fConstMethod; + struct Param { + int8_t fConst; + OpType fType; + OpMod fMod; + } fParams[2]; +} opData[] = { + { DEFOP::kUnknown, "??", "???", BLANK, OpType::kNone, OpMod::kNone, BLANK, + { } }, + { DEFOP::kAdd, "+", "add", BLANK, OpType::kThis, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kAddTo, "+=", "addto", BLANK, OpType::kVoid, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kAddTo, "+=", "addto1", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kAddTo, "+=", "addto2", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kChar, OpMod::kArray, }}}, + { DEFOP::kAddTo, "+=", "addto3", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kChar, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array", BLANK, OpType::kScalar, OpMod::kNone, CONST, + {{ BLANK, OpType::kInt, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array1", BLANK, OpType::kScalar, OpMod::kReference, BLANK, + {{ BLANK, OpType::kInt, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array2", BLANK, OpType::kChar, OpMod::kNone, CONST, + {{ BLANK, OpType::kSizeT, OpMod::kNone, }}}, + { DEFOP::kArray, "[]", "array3", BLANK, OpType::kChar, OpMod::kReference, BLANK, + {{ BLANK, OpType::kSizeT, OpMod::kNone, }}}, + { DEFOP::kCast, "()", "cast", BLANK, OpType::kAny, OpMod::kAny, ANY, + {{ ANY, OpType::kAny, OpMod::kAny, }}}, + { DEFOP::kCopy, "=", "copy", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kCopy, "=", "copy1", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ CONST, OpType::kChar, OpMod::kArray, }}}, + { DEFOP::kDelete, "delete", "delete", BLANK, OpType::kVoid, OpMod::kNone, BLANK, + {{ BLANK, OpType::kVoid, OpMod::kPointer, }}}, + { DEFOP::kDereference, "->", "deref", ANY, OpType::kThis, OpMod::kPointer, CONST, + { } }, + { DEFOP::kDereference, "*", "deref", BLANK, OpType::kThis, OpMod::kReference, CONST, + { } }, + { DEFOP::kEqual, "==", "equal", BLANK, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kEqual, "==", "equal1", BLANK, OpType::kBool, OpMod::kNone, CONST, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kEqual, "==", "equal2", ANY, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kMinus, "-", "minus", BLANK, OpType::kThis, OpMod::kNone, CONST, + { } }, + { DEFOP::kMove, "=", "move", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ BLANK, OpType::kThis, OpMod::kMove, }}}, + { DEFOP::kMultiply, "*", "multiply", BLANK, OpType::kThis, OpMod::kNone, CONST, + {{ BLANK, OpType::kScalar, OpMod::kNone, }}}, + { DEFOP::kMultiply, "*", "multiply1", BLANK, OpType::kThis, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kMultiplyBy, "*=", "multiplyby", BLANK, OpType::kThis, OpMod::kReference, BLANK, + {{ BLANK, OpType::kScalar, OpMod::kNone, }}}, + { DEFOP::kNew, "new", "new", BLANK, OpType::kVoid, OpMod::kPointer, BLANK, + {{ BLANK, OpType::kSizeT, OpMod::kNone, }}}, + { DEFOP::kNotEqual, "!=", "notequal", BLANK, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kNotEqual, "!=", "notequal1", BLANK, OpType::kBool, OpMod::kNone, CONST, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kNotEqual, "!=", "notequal2", ANY, OpType::kBool, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kSubtract, "-", "subtract", BLANK, OpType::kThis, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }, + { CONST, OpType::kThis, OpMod::kReference, }}}, + { DEFOP::kSubtractFrom, "-=", "subtractfrom", BLANK, OpType::kVoid, OpMod::kNone, BLANK, + {{ CONST, OpType::kThis, OpMod::kReference, }}}, +}; + +OpType lookup_type(const string& typeWord, const string& name) { + if (typeWord == name || (typeWord == "SkIVector" && name == "SkIPoint") + || (typeWord == "SkVector" && name == "SkPoint")) { + return OpType::kThis; + } + const char* keyWords[] = { "void", "bool", "char", "float", "int", "SkScalar", "size_t" }; + for (unsigned i = 0; i < SK_ARRAY_COUNT(keyWords); ++i) { + if (typeWord == keyWords[i]) { + return (OpType) (i + 1); + } + } + return OpType::kNone; +} + +OpMod lookup_mod(TextParser& iParser) { + OpMod mod = OpMod::kNone; + if ('&' == iParser.peek()) { + mod = OpMod::kReference; + iParser.next(); + if ('&' == iParser.peek()) { + mod = OpMod::kMove; + iParser.next(); + } + } else if ('*' == iParser.peek()) { + mod = OpMod::kPointer; + iParser.next(); + } + iParser.skipWhiteSpace(); + return mod; +} + +bool Definition::parseOperator(size_t doubleColons, string& result) { + const char operatorStr[] = "operator"; + size_t opPos = fName.find(operatorStr, doubleColons); + if (string::npos == opPos) { + return false; + } + string className(fName, 0, doubleColons - 2); + TextParser iParser(fFileName, fStart, fContentStart, fLineCount); + SkAssertResult(iParser.skipWord("#Method")); + iParser.skipExact("SK_API"); + iParser.skipWhiteSpace(); + bool isStatic = iParser.skipExact("static"); + iParser.skipWhiteSpace(); + iParser.skipExact("SK_API"); + iParser.skipWhiteSpace(); + bool returnsConst = iParser.skipExact("const"); + if (returnsConst) { + SkASSERT(0); // incomplete + } + SkASSERT(isStatic == false || returnsConst == false); + iParser.skipWhiteSpace(); + const char* returnTypeStart = iParser.fChar; + iParser.skipToNonAlphaNum(); + SkASSERT(iParser.fChar > returnTypeStart); + string returnType(returnTypeStart, iParser.fChar - returnTypeStart); + OpType returnOpType = lookup_type(returnType, className); + iParser.skipWhiteSpace(); + OpMod returnMod = lookup_mod(iParser); + SkAssertResult(iParser.skipExact("operator")); + iParser.skipWhiteSpace(); + fMethodType = Definition::MethodType::kOperator; + TextParser::Save save(&iParser); + for (auto parser : opData) { + save.restore(); + if (!iParser.skipExact(parser.fSymbol)) { + continue; + } + iParser.skipWhiteSpace(); + if ('(' != iParser.peek()) { + continue; + } + if (parser.fFriend != ANY && (parser.fFriend == STATIC) != isStatic) { + continue; + } + if (parser.fReturnType != OpType::kAny && parser.fReturnType != returnOpType) { + continue; + } + if (parser.fReturnMod != OpMod::kAny && parser.fReturnMod != returnMod) { + continue; + } + iParser.next(); // skip '(' + iParser.skipWhiteSpace(); + int parserCount = (parser.fParams[0].fType != OpType::kNone) + + (parser.fParams[1].fType != OpType::kNone); + bool countsMatch = true; + for (int pIndex = 0; pIndex < 2; ++pIndex) { + if (')' == iParser.peek()) { + countsMatch = pIndex == parserCount; + break; + } + if (',' == iParser.peek()) { + iParser.next(); + iParser.skipWhiteSpace(); + } + bool paramConst = iParser.skipExact("const"); + if (parser.fParams[pIndex].fConst != ANY && + paramConst != (parser.fParams[pIndex].fConst == CONST)) { + countsMatch = false; + break; + } + iParser.skipWhiteSpace(); + const char* paramStart = iParser.fChar; + iParser.skipToNonAlphaNum(); + SkASSERT(iParser.fChar > paramStart); + string paramType(paramStart, iParser.fChar - paramStart); + OpType paramOpType = lookup_type(paramType, className); + if (parser.fParams[pIndex].fType != OpType::kAny && + parser.fParams[pIndex].fType != paramOpType) { + countsMatch = false; + break; + } + iParser.skipWhiteSpace(); + OpMod paramMod = lookup_mod(iParser); + if (parser.fParams[pIndex].fMod != OpMod::kAny && + parser.fParams[pIndex].fMod != paramMod) { + countsMatch = false; + break; + } + iParser.skipToNonAlphaNum(); + if ('[' == iParser.peek()) { + paramMod = OpMod::kArray; + SkAssertResult(iParser.skipExact("[]")); + } + iParser.skipWhiteSpace(); + } + if (!countsMatch) { + continue; + } + if (')' != iParser.peek()) { + continue; + } + iParser.next(); + bool constMethod = iParser.skipExact("_const"); + if (parser.fConstMethod != ANY && (parser.fConstMethod == CONST) != constMethod) { + continue; + } + result += parser.fName; + result += "_operator"; + fOperator = parser.fOperator; + fOperatorConst = constMethod; + return true; + } + SkASSERT(0); // incomplete + return false; +#if 0 + if ('!' == fName[opPos]) { + SkASSERT('=' == fName[opPos + 1]); + result += "not_equal_operator"; + } else if ('=' == fName[opPos]) { + if ('(' == fName[opPos + 1]) { + result += isMove ? "move_" : "copy_"; + result += "assignment_operator"; + } else { + SkASSERT('=' == fName[opPos + 1]); + result += "equal_operator"; + } + } else if ('[' == fName[opPos]) { + result += "subscript_operator"; + const char* end = fContentStart; + while (end > fStart && ' ' >= end[-1]) { + --end; + } + string constCheck(fStart, end - fStart); + size_t constPos = constCheck.rfind("const"); + if (constCheck.length() == constPos + 5) { + result += "_const"; + } + } else if ('*' == fName[opPos]) { + result += "multiply_operator"; + } else if ('-' == fName[opPos]) { + result += "subtract_operator"; + } else if ('+' == fName[opPos]) { + result += "add_operator"; + } else { + SkASSERT(0); // todo: incomplete + } +#endif + return true; +} + +#undef CONST +#undef FRIEND +#undef BLANK +#undef DEFOP + +bool Definition::boilerplateIfDef(Definition* parent) { + const Definition& label = fTokens.front(); + if (Type::kWord != label.fType) { + return false; + } + fName = string(label.fContentStart, label.fContentEnd - label.fContentStart); + return true; +} + +// todo: this is matching #ifndef SkXXX_DEFINED for no particular reason +// it doesn't do anything useful with arbitrary input, e.g. #ifdef SK_SUPPORT_LEGACY_CANVAS_HELPERS +// also doesn't know what to do with SK_REQUIRE_LOCAL_VAR() +bool Definition::boilerplateDef(Definition* parent) { + if (!this->boilerplateIfDef(parent)) { + return false; + } + const char* s = fName.c_str(); + const char* e = strchr(s, '_'); + return true; // fixme: if this is trying to do something useful with define, do it here + if (!e) { + return false; + } + string prefix(s, e - s); + const char* inName = strstr(parent->fName.c_str(), prefix.c_str()); + if (!inName) { + return false; + } + if ('/' != inName[-1] && '\\' != inName[-1]) { + return false; + } + if (strcmp(inName + prefix.size(), ".h")) { + return false; + } + return true; +} + +// fixme: this will need to be more complicated to handle all of Skia +// for now, just handle paint -- maybe fiddle will loosen naming restrictions +void Definition::setCanonicalFiddle() { + fMethodType = Definition::MethodType::kNone; + size_t doubleColons = fName.find("::", 0); + SkASSERT(string::npos != doubleColons); + string base = fName.substr(0, doubleColons); + string result = base + "_"; + doubleColons += 2; + if (string::npos != fName.find('~', doubleColons)) { + fMethodType = Definition::MethodType::kDestructor; + result += "destructor"; + } else if (!this->parseOperator(doubleColons, result)) { + bool isMove = string::npos != fName.find("&&", doubleColons); + size_t parens = fName.find("()", doubleColons); + if (string::npos != parens) { + string methodName = fName.substr(doubleColons, parens - doubleColons); + do { + size_t nextDouble = methodName.find("::"); + if (string::npos == nextDouble) { + break; + } + base = methodName.substr(0, nextDouble); + result += base + '_'; + methodName = methodName.substr(nextDouble + 2); + doubleColons += nextDouble + 2; + } while (true); + if (base == methodName) { + fMethodType = Definition::MethodType::kConstructor; + result += "empty_constructor"; + } else { + result += fName.substr(doubleColons, fName.length() - doubleColons - 2); + } + } else { + size_t openParen = fName.find('(', doubleColons); + if (string::npos == openParen) { + result += fName.substr(doubleColons); + } else { + size_t comma = fName.find(',', doubleColons); + if (string::npos == comma) { + result += isMove ? "move_" : "copy_"; + } + fMethodType = Definition::MethodType::kConstructor; + // name them by their param types, + // e.g. SkCanvas__int_int_const_SkSurfaceProps_star + // TODO: move forward until parens are balanced and terminator =,) + TextParser params("", &fName[openParen] + 1, &*fName.end(), 0); + bool underline = false; + while (!params.eof()) { +// SkDEBUGCODE(const char* end = params.anyOf("(),=")); // unused for now +// SkASSERT(end[0] != '('); // fixme: put off handling nested parentheseses + if (params.startsWith("const") || params.startsWith("int") + || params.startsWith("Sk")) { + const char* wordStart = params.fChar; + params.skipToNonAlphaNum(); + if (underline) { + result += '_'; + } else { + underline = true; + } + result += string(wordStart, params.fChar - wordStart); + } else { + params.skipToNonAlphaNum(); + } + if (!params.eof() && '*' == params.peek()) { + if (underline) { + result += '_'; + } else { + underline = true; + } + result += "star"; + params.next(); + params.skipSpace(); + } + params.skipToAlpha(); + } + } + } + } + fFiddle = Definition::NormalizedName(result); +} + +void Definition::setWrapper() { + const char drawWrapper[] = "void draw(SkCanvas* canvas) {"; + const char drawNoCanvas[] = "void draw(SkCanvas* ) {"; + string text = this->extractText(Definition::TrimExtract::kNo); + size_t nonSpace = 0; + while (nonSpace < text.length() && ' ' >= text[nonSpace]) { + ++nonSpace; + } + bool hasFunc = !text.compare(nonSpace, sizeof(drawWrapper) - 1, drawWrapper); + bool noCanvas = !text.compare(nonSpace, sizeof(drawNoCanvas) - 1, drawNoCanvas); + bool hasCanvas = string::npos != text.find("SkCanvas canvas"); + SkASSERT(!hasFunc || !noCanvas); + bool preprocessor = text[0] == '#'; + bool wrapCode = !hasFunc && !noCanvas && !preprocessor; + if (wrapCode) { + fWrapper = hasCanvas ? string(drawNoCanvas) : string(drawWrapper); + } +} + +bool Definition::exampleToScript(string* result, ExampleOptions exampleOptions) const { + bool hasFiddle = true; + const Definition* platform = this->hasChild(MarkType::kPlatform); + if (platform) { + TextParser platParse(platform); + hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd); + } + if (!hasFiddle) { + *result = ""; + return true; + } + string text = this->extractText(Definition::TrimExtract::kNo); + bool textOut = string::npos != text.find("SkDebugf(") + || string::npos != text.find("dump(") + || string::npos != text.find("dumpHex("); + string heightStr = "256"; + string widthStr = "256"; + string normalizedName(fFiddle); + string code; + string imageStr = "0"; + for (auto const& iter : fChildren) { + switch (iter->fMarkType) { + case MarkType::kError: + result->clear(); + return true; + case MarkType::kHeight: + heightStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); + break; + case MarkType::kWidth: + widthStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); + break; + case MarkType::kDescription: + // ignore for now + break; + case MarkType::kFunction: { + // emit this, but don't wrap this in draw() + string funcText(iter->fContentStart, iter->fContentEnd - iter->fContentStart - 1); + size_t pos = 0; + while (pos < funcText.length() && ' ' > funcText[pos]) { + ++pos; + } + size_t indent = count_indent(funcText, pos, funcText.length()); + add_code(funcText, pos, funcText.length(), 0, indent, code); + code += "\\n"; + } break; + case MarkType::kComment: + break; + case MarkType::kImage: + imageStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart); + break; + case MarkType::kToDo: + break; + case MarkType::kMarkChar: + case MarkType::kPlatform: + // ignore for now + break; + case MarkType::kStdOut: + textOut = true; + break; + default: + SkASSERT(0); // more coding to do + } + } + string textOutStr = textOut ? "true" : "false"; + size_t pos = 0; + while (pos < text.length() && ' ' > text[pos]) { + ++pos; + } + size_t end = text.length(); + size_t outIndent = 0; + size_t textIndent = count_indent(text, pos, end); + if ("MakeFromBackendTexture" == fName) { + SkDebugf(""); + } + if (fWrapper.length() > 0) { + code += fWrapper; + code += "\\n"; + outIndent = 4; + } + add_code(text, pos, end, outIndent, textIndent, code); + if (fWrapper.length() > 0) { + code += "}"; + } + string example = "\"" + normalizedName + "\": {\n"; + size_t nameStart = fFileName.find("\\", 0); + SkASSERT(string::npos != nameStart); + string baseFile = fFileName.substr(nameStart + 1, fFileName.length() - nameStart - 5); + if (ExampleOptions::kText == exampleOptions) { + example += " \"code\": \"" + code + "\",\n"; + example += " \"hash\": \"" + fHash + "\",\n"; + example += " \"file\": \"" + baseFile + "\",\n"; + example += " \"name\": \"" + fName + "\","; + } else { + example += " \"code\": \"" + code + "\",\n"; + if (ExampleOptions::kPng == exampleOptions) { + example += " \"width\": " + widthStr + ",\n"; + example += " \"height\": " + heightStr + ",\n"; + example += " \"hash\": \"" + fHash + "\",\n"; + example += " \"file\": \"" + baseFile + "\",\n"; + example += " \"name\": \"" + fName + "\"\n"; + example += "}"; + } else { + example += " \"options\": {\n"; + example += " \"width\": " + widthStr + ",\n"; + example += " \"height\": " + heightStr + ",\n"; + example += " \"source\": " + imageStr + ",\n"; + example += " \"srgb\": false,\n"; + example += " \"f16\": false,\n"; + example += " \"textOnly\": " + textOutStr + ",\n"; + example += " \"animated\": false,\n"; + example += " \"duration\": 0\n"; + example += " },\n"; + example += " \"fast\": true"; + } + } + *result = example; + return true; +} + +string Definition::extractText(TrimExtract trimExtract) const { + string result; + TextParser parser(fFileName, fContentStart, fContentEnd, fLineCount); + int childIndex = 0; + char mc = '#'; + while (parser.fChar < parser.fEnd) { + if (TrimExtract::kYes == trimExtract && !parser.skipWhiteSpace()) { + break; + } + if (parser.next() == mc) { + if (parser.next() == mc) { + if (parser.next() == mc) { + mc = parser.next(); + } + } else { + // fixme : more work to do if # style comment is in text + // if in method definition, could be alternate method name + --parser.fChar; + if (' ' < parser.fChar[0]) { + if (islower(parser.fChar[0])) { + result += '\n'; + parser.skipLine(); + } else { + SkASSERT(isupper(parser.fChar[0])); + parser.skipTo(fChildren[childIndex]->fTerminator); + if (mc == parser.fChar[0] && mc == parser.fChar[1]) { + parser.next(); + parser.next(); + } + childIndex++; + } + } else { + parser.skipLine(); + } + continue; + } + } else { + --parser.fChar; + } + const char* end = parser.fEnd; + const char* mark = parser.strnchr(mc, end); + if (mark) { + end = mark; + } + string fragment(parser.fChar, end - parser.fChar); + trim_end(fragment); + if (TrimExtract::kYes == trimExtract) { + trim_start(fragment); + if (result.length()) { + result += '\n'; + result += '\n'; + } + } + if (TrimExtract::kYes == trimExtract || has_nonwhitespace(fragment)) { + result += fragment; + } + parser.skipTo(end); + } + return result; +} + +static void space_pad(string* str) { + size_t len = str->length(); + if (len == 0) { + return; + } + char last = (*str)[len - 1]; + if ('~' == last || ' ' >= last) { + return; + } + *str += ' '; +} + +//start here; +// see if it possible to abstract this a little bit so it can +// additionally be used to find params and return in method prototype that +// does not have corresponding doxygen comments +bool Definition::checkMethod() const { + SkASSERT(MarkType::kMethod == fMarkType); + // if method returns a value, look for a return child + // for each parameter, look for a corresponding child + const char* end = fContentStart; + while (end > fStart && ' ' >= end[-1]) { + --end; + } + TextParser methodParser(fFileName, fStart, end, fLineCount); + methodParser.skipWhiteSpace(); + SkASSERT(methodParser.startsWith("#Method")); + methodParser.skipName("#Method"); + methodParser.skipSpace(); + string name = this->methodName(); + if (MethodType::kNone == fMethodType && name.length() > 2 && + "()" == name.substr(name.length() - 2)) { + name = name.substr(0, name.length() - 2); + } + bool expectReturn = this->methodHasReturn(name, &methodParser); + bool foundReturn = false; + bool foundException = false; + for (auto& child : fChildren) { + foundException |= MarkType::kDeprecated == child->fMarkType + || MarkType::kExperimental == child->fMarkType; + if (MarkType::kReturn != child->fMarkType) { + if (MarkType::kParam == child->fMarkType) { + child->fVisited = false; + } + continue; + } + if (!expectReturn) { + return methodParser.reportError<bool>("no #Return expected"); + } + if (foundReturn) { + return methodParser.reportError<bool>("multiple #Return markers"); + } + foundReturn = true; + } + if (expectReturn && !foundReturn && !foundException) { + return methodParser.reportError<bool>("missing #Return marker"); + } + const char* paren = methodParser.strnchr('(', methodParser.fEnd); + if (!paren) { + return methodParser.reportError<bool>("missing #Method function definition"); + } + const char* nextEnd = paren; + do { + string paramName; + methodParser.fChar = nextEnd + 1; + methodParser.skipSpace(); + if (!this->nextMethodParam(&methodParser, &nextEnd, ¶mName)) { + continue; + } + bool foundParam = false; + for (auto& child : fChildren) { + if (MarkType::kParam != child->fMarkType) { + continue; + } + if (paramName != child->fName) { + continue; + } + if (child->fVisited) { + return methodParser.reportError<bool>("multiple #Method param with same name"); + } + child->fVisited = true; + if (foundParam) { + TextParser paramError(child); + return methodParser.reportError<bool>("multiple #Param with same name"); + } + foundParam = true; + + } + if (!foundParam && !foundException) { + return methodParser.reportError<bool>("no #Param found"); + } + if (')' == nextEnd[0]) { + break; + } + } while (')' != nextEnd[0]); + for (auto& child : fChildren) { + if (MarkType::kParam != child->fMarkType) { + continue; + } + if (!child->fVisited) { + TextParser paramError(child); + return paramError.reportError<bool>("#Param without param in #Method"); + } + } + return true; +} + +bool Definition::crossCheck2(const Definition& includeToken) const { + TextParser parser(fFileName, fStart, fContentStart, fLineCount); + parser.skipExact("#"); + bool isMethod = parser.skipName("Method"); + const char* contentEnd; + if (isMethod) { + contentEnd = fContentStart; + } else if (parser.skipName("DefinedBy")) { + contentEnd = fContentEnd; + while (parser.fChar < contentEnd && ' ' >= contentEnd[-1]) { + --contentEnd; + } + if (parser.fChar < contentEnd - 1 && ')' == contentEnd[-1] && '(' == contentEnd[-2]) { + contentEnd -= 2; + } + } else { + return parser.reportError<bool>("unexpected crosscheck marktype"); + } + return crossCheckInside(parser.fChar, contentEnd, includeToken); +} + +bool Definition::crossCheck(const Definition& includeToken) const { + return crossCheckInside(fContentStart, fContentEnd, includeToken); +} + +bool Definition::crossCheckInside(const char* start, const char* end, + const Definition& includeToken) const { + TextParser def(fFileName, start, end, fLineCount); + TextParser inc("", includeToken.fContentStart, includeToken.fContentEnd, 0); + if (inc.startsWith("SK_API")) { + inc.skipWord("SK_API"); + } + if (inc.startsWith("friend")) { + inc.skipWord("friend"); + } + if (inc.startsWith("SK_API")) { + inc.skipWord("SK_API"); + } + inc.skipExact("SkDEBUGCODE("); + do { + bool defEof; + bool incEof; + do { + defEof = def.eof() || !def.skipWhiteSpace(); + incEof = inc.eof() || !inc.skipWhiteSpace(); + if (!incEof && '/' == inc.peek() && (defEof || '/' != def.peek())) { + inc.next(); + if ('*' == inc.peek()) { + inc.skipToEndBracket("*/"); + inc.next(); + } else if ('/' == inc.peek()) { + inc.skipToEndBracket('\n'); + } + } else if (!incEof && '#' == inc.peek() && (defEof || '#' != def.peek())) { + inc.next(); + if (inc.startsWith("if")) { + inc.skipToEndBracket("\n"); + } else if (inc.startsWith("endif")) { + inc.skipToEndBracket("\n"); + } else { + SkASSERT(0); // incomplete + return false; + } + } else { + break; + } + inc.next(); + } while (true); + if (defEof || incEof) { + if (defEof == incEof || (!defEof && ';' == def.peek())) { + return true; + } + return false; // allow setting breakpoint on failure + } + char defCh; + do { + defCh = def.next(); + char incCh = inc.next(); + if (' ' >= defCh && ' ' >= incCh) { + break; + } + if (defCh != incCh) { + if ('_' != defCh || ' ' != incCh || !fOperatorConst || !def.startsWith("const")) { + return false; + } + } + if (';' == defCh) { + return true; + } + } while (!def.eof() && !inc.eof()); + } while (true); + return false; +} + +string Definition::formatFunction() const { + const char* end = fContentStart; + while (end > fStart && ' ' >= end[-1]) { + --end; + } + TextParser methodParser(fFileName, fStart, end, fLineCount); + methodParser.skipWhiteSpace(); + SkASSERT(methodParser.startsWith("#Method")); + methodParser.skipName("#Method"); + methodParser.skipSpace(); + const char* lastStart = methodParser.fChar; + const int limit = 100; // todo: allow this to be set by caller or in global or something + string name = this->methodName(); + if ("MakeFromBackendTextureAsRenderTarget" == name) { + SkDebugf(""); + } + const char* nameInParser = methodParser.strnstr(name.c_str(), methodParser.fEnd); + methodParser.skipTo(nameInParser); + const char* lastEnd = methodParser.fChar; + const char* paren = methodParser.strnchr('(', methodParser.fEnd); + size_t indent; + if (paren) { + indent = (size_t) (paren - lastStart) + 1; + } else { + indent = (size_t) (lastEnd - lastStart); + } + // trim indent so longest line doesn't exceed box width + TextParser::Save savePlace(&methodParser); + const char* saveStart = lastStart; + ptrdiff_t maxLine = 0; + do { + const char* nextStart = lastEnd; + const char* delimiter = methodParser.anyOf(",)"); + const char* nextEnd = delimiter ? delimiter : methodParser.fEnd; + if (delimiter) { + while (nextStart < nextEnd && ' ' >= nextStart[0]) { + ++nextStart; + } + } + while (nextEnd > nextStart && ' ' >= nextEnd[-1]) { + --nextEnd; + } + if (delimiter) { + nextEnd += 1; + delimiter += 1; + } + if (lastEnd > lastStart) { + maxLine = SkTMax(maxLine, lastEnd - lastStart); + } + if (delimiter) { + methodParser.skipTo(delimiter); + } + lastStart = nextStart; + lastEnd = nextEnd; + } while (lastStart < lastEnd); + savePlace.restore(); + lastStart = saveStart; + lastEnd = methodParser.fChar; + indent = SkTMin(indent, (size_t) (limit - maxLine)); + // write string wtih trimmmed indent + string methodStr; + int written = 0; + do { + const char* nextStart = lastEnd; + SkASSERT(written < limit); + const char* delimiter = methodParser.anyOf(",)"); + const char* nextEnd = delimiter ? delimiter : methodParser.fEnd; + if (delimiter) { + while (nextStart < nextEnd && ' ' >= nextStart[0]) { + ++nextStart; + } + } + while (nextEnd > nextStart && ' ' >= nextEnd[-1]) { + --nextEnd; + } + if (delimiter) { + nextEnd += 1; + delimiter += 1; + } + if (lastEnd > lastStart) { + if (lastStart[0] != ' ') { + space_pad(&methodStr); + } + methodStr += string(lastStart, (size_t) (lastEnd - lastStart)); + written += (size_t) (lastEnd - lastStart); + } + if (delimiter) { + if (nextEnd - nextStart >= (ptrdiff_t) (limit - written)) { + written = indent; + methodStr += '\n'; + methodStr += string(indent, ' '); + } + methodParser.skipTo(delimiter); + } + lastStart = nextStart; + lastEnd = nextEnd; + } while (lastStart < lastEnd); + return methodStr; +} + +string Definition::fiddleName() const { + string result; + size_t start = 0; + string parent; + const Definition* parentDef = this; + while ((parentDef = parentDef->fParent)) { + if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { + parent = parentDef->fFiddle; + break; + } + } + if (parent.length() && 0 == fFiddle.compare(0, parent.length(), parent)) { + start = parent.length(); + while (start < fFiddle.length() && '_' == fFiddle[start]) { + ++start; + } + } + size_t end = fFiddle.find_first_of('(', start); + return fFiddle.substr(start, end - start); +} + +const Definition* Definition::hasChild(MarkType markType) const { + for (auto iter : fChildren) { + if (markType == iter->fMarkType) { + return iter; + } + } + return nullptr; +} + +const Definition* Definition::hasParam(const string& ref) const { + SkASSERT(MarkType::kMethod == fMarkType); + for (auto iter : fChildren) { + if (MarkType::kParam != iter->fMarkType) { + continue; + } + if (iter->fName == ref) { + return &*iter; + } + + } + return nullptr; +} + +bool Definition::methodHasReturn(const string& name, TextParser* methodParser) const { + if (methodParser->skipExact("static")) { + methodParser->skipWhiteSpace(); + } + const char* lastStart = methodParser->fChar; + const char* nameInParser = methodParser->strnstr(name.c_str(), methodParser->fEnd); + methodParser->skipTo(nameInParser); + const char* lastEnd = methodParser->fChar; + const char* returnEnd = lastEnd; + while (returnEnd > lastStart && ' ' == returnEnd[-1]) { + --returnEnd; + } + bool expectReturn = 4 != returnEnd - lastStart || strncmp("void", lastStart, 4); + if (MethodType::kNone != fMethodType && MethodType::kOperator != fMethodType && !expectReturn) { + return methodParser->reportError<bool>("unexpected void"); + } + switch (fMethodType) { + case MethodType::kNone: + case MethodType::kOperator: + // either is fine + break; + case MethodType::kConstructor: + expectReturn = true; + break; + case MethodType::kDestructor: + expectReturn = false; + break; + } + return expectReturn; +} + +string Definition::methodName() const { + string result; + size_t start = 0; + string parent; + const Definition* parentDef = this; + while ((parentDef = parentDef->fParent)) { + if (MarkType::kClass == parentDef->fMarkType || MarkType::kStruct == parentDef->fMarkType) { + parent = parentDef->fName; + break; + } + } + if (parent.length() && 0 == fName.compare(0, parent.length(), parent)) { + start = parent.length(); + while (start < fName.length() && ':' == fName[start]) { + ++start; + } + } + if (fClone) { + int lastUnder = fName.rfind('_'); + return fName.substr(start, (size_t) (lastUnder - start)); + } + size_t end = fName.find_first_of('(', start); + if (string::npos == end) { + return fName.substr(start); + } + return fName.substr(start, end - start); +} + +bool Definition::nextMethodParam(TextParser* methodParser, const char** nextEndPtr, + string* paramName) const { + int parenCount = 0; + TextParser::Save saveState(methodParser); + while (true) { + if (methodParser->eof()) { + return methodParser->reportError<bool>("#Method function missing close paren"); + } + char ch = methodParser->peek(); + if ('(' == ch) { + ++parenCount; + } + if (parenCount == 0 && (')' == ch || ',' == ch)) { + *nextEndPtr = methodParser->fChar; + break; + } + if (')' == ch) { + if (0 > --parenCount) { + return this->reportError<bool>("mismatched parentheses"); + } + } + methodParser->next(); + } + saveState.restore(); + const char* nextEnd = *nextEndPtr; + const char* paramEnd = nextEnd; + const char* assign = methodParser->strnstr(" = ", paramEnd); + if (assign) { + paramEnd = assign; + } + const char* closeBracket = methodParser->strnstr("]", paramEnd); + if (closeBracket) { + const char* openBracket = methodParser->strnstr("[", paramEnd); + if (openBracket && openBracket < closeBracket) { + while (openBracket < --closeBracket && isdigit(closeBracket[0])) + ; + if (openBracket == closeBracket) { + paramEnd = openBracket; + } + } + } + const char* function = methodParser->strnstr(")(", paramEnd); + if (function) { + paramEnd = function; + } + while (paramEnd > methodParser->fChar && ' ' == paramEnd[-1]) { + --paramEnd; + } + const char* paramStart = paramEnd; + while (paramStart > methodParser->fChar && isalnum(paramStart[-1])) { + --paramStart; + } + if (paramStart > methodParser->fChar && paramStart >= paramEnd) { + return methodParser->reportError<bool>("#Method missing param name"); + } + *paramName = string(paramStart, paramEnd - paramStart); + if (!paramName->length()) { + if (')' != nextEnd[0]) { + return methodParser->reportError<bool>("#Method malformed param"); + } + return false; + } + return true; +} + +string Definition::NormalizedName(string name) { + string normalizedName = name; + std::replace(normalizedName.begin(), normalizedName.end(), '-', '_'); + do { + size_t doubleColon = normalizedName.find("::", 0); + if (string::npos == doubleColon) { + break; + } + normalizedName = normalizedName.substr(0, doubleColon) + + '_' + normalizedName.substr(doubleColon + 2); + } while (true); + return normalizedName; +} + +bool Definition::paramsMatch(const string& match, const string& name) const { + TextParser def(fFileName, fStart, fContentStart, fLineCount); + const char* dName = def.strnstr(name.c_str(), fContentStart); + if (!dName) { + return false; + } + def.skipTo(dName); + TextParser m(fFileName, &match.front(), &match.back() + 1, fLineCount); + const char* mName = m.strnstr(name.c_str(), m.fEnd); + if (!mName) { + return false; + } + m.skipTo(mName); + while (!def.eof() && ')' != def.peek() && !m.eof() && ')' != m.peek()) { + const char* ds = def.fChar; + const char* ms = m.fChar; + const char* de = def.anyOf(") \n"); + const char* me = m.anyOf(") \n"); + def.skipTo(de); + m.skipTo(me); + if (def.fChar - ds != m.fChar - ms) { + return false; + } + if (strncmp(ds, ms, (int) (def.fChar - ds))) { + return false; + } + def.skipWhiteSpace(); + m.skipWhiteSpace(); + } + return !def.eof() && ')' == def.peek() && !m.eof() && ')' == m.peek(); +} + +void RootDefinition::clearVisited() { + fVisited = false; + for (auto& leaf : fLeaves) { + leaf.second.fVisited = false; + } + for (auto& branch : fBranches) { + branch.second->clearVisited(); + } +} + +bool RootDefinition::dumpUnVisited(bool skip) { + bool allStructElementsFound = true; + for (auto& leaf : fLeaves) { + if (!leaf.second.fVisited) { + // TODO: parse embedded struct in includeParser phase, then remove this condition + if (skip) { + const Definition& def = leaf.second; + if (def.fChildren.size() > 0 && + MarkType::kDeprecated == def.fChildren[0]->fMarkType) { + continue; + } + } + // FIXME: bugs requiring long tail fixes, suppressed here: + // SkBitmap::validate() is wrapped in SkDEBUGCODE in .h and not parsed + if ("SkBitmap::validate()" == leaf.first) { + continue; + } + // typedef uint32_t SaveLayerFlags not seen in SkCanvas.h, don't know why + if ("SaveLayerFlags" == leaf.first) { + continue; + } + // SkPath::pathRefIsValid in #ifdef ; prefer to remove chrome dependency to fix + if ("SkPath::pathRefIsValid" == leaf.first) { + continue; + } + // FIXME: end of long tail bugs + SkDebugf("defined in bmh but missing in include: %s\n", leaf.first.c_str()); + } + } + for (auto& branch : fBranches) { + allStructElementsFound &= branch.second->dumpUnVisited(skip); + } + return allStructElementsFound; +} + +const Definition* RootDefinition::find(const string& ref, AllowParens allowParens) const { + const auto leafIter = fLeaves.find(ref); + if (leafIter != fLeaves.end()) { + return &leafIter->second; + } + if (AllowParens::kYes == allowParens && string::npos == ref.find("()")) { + string withParens = ref + "()"; + const auto parensIter = fLeaves.find(withParens); + if (parensIter != fLeaves.end()) { + return &parensIter->second; + } + } + const auto branchIter = fBranches.find(ref); + if (branchIter != fBranches.end()) { + const RootDefinition* rootDef = branchIter->second; + return rootDef; + } + const Definition* result = nullptr; + for (const auto& branch : fBranches) { + const RootDefinition* rootDef = branch.second; + result = rootDef->find(ref, allowParens); + if (result) { + break; + } + } + return result; +} diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp index c4ee8e7217..955e6ef724 100644 --- a/tools/bookmaker/includeParser.cpp +++ b/tools/bookmaker/includeParser.cpp @@ -439,7 +439,7 @@ bool IncludeParser::crossCheck(BmhParser& bmhParser) { continue; } RootDefinition* root = &finder->second; - if (!root->dumpUnVisited()) { + if (!root->dumpUnVisited(bmhParser.fSkip)) { SkDebugf("some struct elements not found; struct finding in includeParser is missing\n"); } SkDebugf("cross-checked %s\n", className.c_str()); @@ -495,6 +495,7 @@ void IncludeParser::dumpClassTokens(IClassDefinition& classDef) { } switch (token.fMarkType) { case MarkType::kEnum: + case MarkType::kEnumClass: this->dumpEnum(token); break; case MarkType::kMethod: @@ -518,7 +519,21 @@ void IncludeParser::dumpClassTokens(IClassDefinition& classDef) { this->writeSpace(); this->writeString("incomplete"); this->lf(2); - this->writeEndTag("Method"); + switch (token.fMarkType) { + case MarkType::kEnum: + case MarkType::kEnumClass: + this->writeEndTag("Enum"); + break; + case MarkType::kMethod: + this->writeEndTag("Method"); + break; + case MarkType::kMember: + this->writeEndTag("Member"); + continue; + break; + default: + SkASSERT(0); + } this->lf(2); } } @@ -540,6 +555,7 @@ void IncludeParser::dumpComment(const Definition& token) { methodName.fName = string(token.fContentStart, (int) (token.fContentEnd - token.fContentStart)); methodHasReturn = !methodParser.startsWith("void ") + && !methodParser.startsWith("static void ") && !methodParser.strnchr('~', methodParser.fEnd); const char* paren = methodParser.strnchr('(', methodParser.fEnd); const char* nextEnd = paren; @@ -835,7 +851,8 @@ bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) { this->writeTag("Alias", topicName + "_Reference"); this->lf(2); auto& classMap = fIClassMap[skClassName]; - const char* containerType = kKeyWords[(int) classMap.fKeyWord].fName; + SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord); + const char* containerType = KeyWord::kClass == classMap.fKeyWord ? "Class" : "Struct"; this->writeTag(containerType, skClassName); this->lf(2); auto& tokens = classMap.fTokens; @@ -957,7 +974,9 @@ bool IncludeParser::dumpTokens(const string& dir, const string& skClassName) { this->writeString( "# ------------------------------------------------------------------------------"); this->lf(2); - const char* containerType = kKeyWords[(int) oneClass.second.fKeyWord].fName; + KeyWord keyword = oneClass.second.fKeyWord; + SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword); + const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct"; this->writeTag(containerType, innerName); this->lf(2); this->writeTag("Code"); @@ -1396,23 +1415,26 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { std::advance(tokenIter, child->fParentIndex); tokenIter = std::prev(tokenIter); const char* nameEnd = tokenIter->fContentEnd; - bool add2 = false; - if ('[' == tokenIter->fStart[0]) { + bool addConst = false; + auto operatorCheck = tokenIter; + if ('[' == tokenIter->fStart[0] || '*' == tokenIter->fStart[0]) { + operatorCheck = std::prev(tokenIter); + } + if (KeyWord::kOperator == operatorCheck->fKeyWord) { auto closeParen = std::next(tokenIter); SkASSERT(Definition::Type::kBracket == closeParen->fType && '(' == closeParen->fContentStart[0]); nameEnd = closeParen->fContentEnd + 1; closeParen = std::next(closeParen); - add2 = true; if (Definition::Type::kKeyWord == closeParen->fType && KeyWord::kConst == closeParen->fKeyWord) { - add2 = false; + addConst = true; } - tokenIter = std::prev(tokenIter); + tokenIter = operatorCheck; } string nameStr(tokenIter->fStart, nameEnd - tokenIter->fStart); - if (add2) { - nameStr += "_2"; + if (addConst) { + nameStr += "_const"; } while (tokenIter != child->fParent->fTokens.begin()) { auto testIter = std::prev(tokenIter); @@ -1452,6 +1474,9 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { break; } tokenIter->fName = nameStr; + if ("operator" == nameStr) { + SkDebugf(""); + } tokenIter->fMarkType = MarkType::kMethod; tokenIter->fPrivate = string::npos != nameStr.find("::"); auto testIter = child->fParent->fTokens.begin(); @@ -1500,11 +1525,27 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { SkASSERT(child->fParentIndex > 0); std::advance(parentIter, child->fParentIndex - 1); Definition* methodName = &*parentIter; - TextParser name(methodName); - if (name.skipToEndBracket(':') && name.startsWith("::")) { + TextParser nameParser(methodName); + if (nameParser.skipToEndBracket(':') && nameParser.startsWith("::")) { return true; // expect this is inline class definition outside of class } - SkASSERT(0); // code incomplete + string name(nameParser.fLine, nameParser.lineLength()); + auto finder = fIFunctionMap.find(name); + if (fIFunctionMap.end() != finder) { + // create unique name + SkASSERT(0); // incomplete + } + auto globalFunction = &fIFunctionMap[name]; + globalFunction->fContentStart = start; + globalFunction->fName = name; + if ("operator+" == name) { + SkDebugf(""); + } + globalFunction->fFiddle = name; + globalFunction->fContentEnd = end; + globalFunction->fMarkType = MarkType::kMethod; + globalFunction->fLineCount = tokenIter->fLineCount; + return true; } markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount, markupDef); @@ -1514,6 +1555,9 @@ bool IncludeParser::parseMethod(Definition* child, Definition* markupDef) { SkASSERT(classDef.fStart); string uniqueName = this->uniqueName(classDef.fMethods, nameStr); markupChild->fName = uniqueName; + if ("operator+" == uniqueName) { + SkDebugf(""); + } if (!this->findComments(*child, markupChild)) { return false; } @@ -1605,6 +1649,7 @@ bool IncludeParser::parseObject(Definition* child, Definition* markupDef) { case Bracket::kPound: // special-case the #xxx xxx_DEFINED entries switch (child->fKeyWord) { + case KeyWord::kIf: case KeyWord::kIfndef: case KeyWord::kIfdef: if (child->boilerplateIfDef(fParent)) { @@ -1736,8 +1781,10 @@ bool IncludeParser::parseChar() { return reportError<bool>("malformed closing comment"); } if (Bracket::kSlashStar == this->topBracket()) { - this->next(); // include close in bracket -- FIXME? will this skip stuff? + TextParser::Save save(this); + this->next(); // include close in bracket this->popBracket(); + save.restore(); // put things back so nothing is skipped } break; } @@ -1898,6 +1945,9 @@ bool IncludeParser::parseChar() { if (fInEnum) { break; } + if (Bracket::kPound == this->topBracket()) { + break; + } if (Bracket::kAngle == this->topBracket()) { this->popBracket(); } else { @@ -1915,6 +1965,10 @@ bool IncludeParser::parseChar() { case '&': case ',': case ' ': + case '+': + case '=': + case '-': + case '!': if (fInCharCommentString || fInBrace) { break; } diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp index 399fd4688b..c462826f48 100644 --- a/tools/bookmaker/includeWriter.cpp +++ b/tools/bookmaker/includeWriter.cpp @@ -8,6 +8,9 @@ #include "bookmaker.h" void IncludeWriter::descriptionOut(const Definition* def) { + if ("SkPoint_length" == def->fFiddle) { + SkDebugf(""); + } const char* commentStart = def->fContentStart; int commentLen = (int) (def->fContentEnd - commentStart); bool breakOut = false; @@ -1024,6 +1027,9 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti --continueEnd; } methodName += string(fContinuation, continueEnd - fContinuation); + if ("SkIPoint::operator+" == methodName) { + SkDebugf(""); + } method = root->find(methodName, RootDefinition::AllowParens::kNo); if (!method) { fLineCount = child.fLineCount; @@ -1080,6 +1086,9 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti startDef = &child; fStart = child.fContentStart; methodName = root->fName + "::" + child.fName; + if ("SkIPoint::operator+" == methodName) { + SkDebugf(""); + } inConstructor = root->fName == child.fName; fContinuation = child.fContentEnd; method = root->find(methodName, RootDefinition::AllowParens::kNo); @@ -1103,7 +1112,6 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti } if (Definition::Type::kKeyWord == child.fType) { if (fIndentNext) { - SkDebugf(""); // too soon #if 0 // makes struct Lattice indent when it oughtn't if (KeyWord::kEnum == child.fKeyWord) { @@ -1701,6 +1709,7 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr if (lastPrintable >= lastWrite) { if (' ' == data[lastWrite]) { this->writeSpace(); + lastWrite++; } this->writeBlock(lastPrintable - lastWrite + 1, &data[lastWrite]); } diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp index ed3cd2bcaf..6b082ad1de 100644 --- a/tools/bookmaker/mdOut.cpp +++ b/tools/bookmaker/mdOut.cpp @@ -62,7 +62,10 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, lineStart = false; } else { leadingSpaces = string(base, wordStart - base); - } + } + if (!strncmp("SkPoint::operator-()", start, 20)) { + SkDebugf(""); + } t.skipToMethodEnd(); if (base == t.fChar) { break; @@ -74,6 +77,9 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, continue; } ref = string(start, t.fChar - start); + if (412 == t.fLineCount) { + SkDebugf(""); + } if (const Definition* def = this->isDefined(t, ref, BmhParser::Resolvable::kOut != resolvable)) { SkASSERT(def->fFiddle.length()); @@ -105,7 +111,8 @@ string MdOut::addReferences(const char* refStart, const char* refEnd, return result; } if (!foundMatch) { - if (!(def = this->isDefined(t, fullRef, true))) { + if (!(def = this->isDefined(t, fullRef, + BmhParser::Resolvable::kOut != resolvable))) { if (!result.size()) { t.reportError("missing method"); } @@ -640,7 +647,7 @@ void MdOut::markTypeOut(Definition* def) { case MarkType::kCode: this->lfAlways(2); fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;" - "width: 50em; background-color: #f0f0f0\">"); + "width: 62.5em; background-color: #f0f0f0\">"); this->lf(1); break; case MarkType::kColumn: @@ -705,17 +712,34 @@ void MdOut::markTypeOut(Definition* def) { fprintf(fOut, "Example\n" "\n"); fHasFiddle = true; + bool showGpu = false; + bool gpuAndCpu = false; const Definition* platform = def->hasChild(MarkType::kPlatform); if (platform) { TextParser platParse(platform); fHasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd); + showGpu = platParse.strnstr("gpu", platParse.fEnd); + if (showGpu) { + gpuAndCpu = platParse.strnstr("cpu", platParse.fEnd); + } } if (fHasFiddle) { - fprintf(fOut, "<div><fiddle-embed name=\"%s\">", def->fHash.c_str()); + fprintf(fOut, "<div><fiddle-embed name=\"%s\"", def->fHash.c_str()); + if (showGpu) { + fprintf(fOut, "gpu=\"true\""); + if (gpuAndCpu) { + fprintf(fOut, "cpu=\"true\""); + } + } + fprintf(fOut, ">"); } else { - fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;" - "width: 50em; background-color: #f0f0f0\">"); - this->lf(1); + fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em; font-size: 13px" + " width: 62.5em; background-color: #f0f0f0\">"); + this->lfAlways(1); + if (def->fWrapper.length() > 0) { + fprintf(fOut, "%s", def->fWrapper.c_str()); + } + fRespectLeadingSpace = true; } } break; case MarkType::kExperimental: @@ -774,7 +798,7 @@ void MdOut::markTypeOut(Definition* def) { this->writePending(); string preformattedStr = preformat(formattedStr); fprintf(fOut, "<pre style=\"padding: 1em 1em 1em 1em;" - "width: 50em; background-color: #f0f0f0\">\n" + "width: 62.5em; background-color: #f0f0f0\">\n" "%s\n" "</pre>", preformattedStr.c_str()); this->lf(2); @@ -936,9 +960,15 @@ void MdOut::markTypeOut(Definition* def) { if (fHasFiddle) { fprintf(fOut, "</fiddle-embed></div>"); } else { + this->lfAlways(1); + if (def->fWrapper.length() > 0) { + fprintf(fOut, "}"); + this->lfAlways(1); + } fprintf(fOut, "</pre>"); } this->lf(2); + fRespectLeadingSpace = false; break; case MarkType::kList: fInList = false; @@ -1008,7 +1038,7 @@ void MdOut::mdHeaderOutLF(int depth, int lf) { } void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable resolvable) { - if (BmhParser::Resolvable::kLiteral == resolvable && end > start) { + if ((BmhParser::Resolvable::kLiteral == resolvable || fRespectLeadingSpace) && end > start) { while ('\n' == *start) { ++start; } @@ -1017,7 +1047,7 @@ void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable ++start; } if (start > spaceStart) { - fIndent = start - spaceStart; + fIndent = start - spaceStart; } this->writeBlockTrim(end - start, start); if ('\n' == end[-1]) { diff --git a/tools/bookmaker/parserCommon.cpp b/tools/bookmaker/parserCommon.cpp index 7a6f71f7ed..79503a66b3 100644 --- a/tools/bookmaker/parserCommon.cpp +++ b/tools/bookmaker/parserCommon.cpp @@ -7,11 +7,39 @@ #include "bookmaker.h" +#include "SkOSFile.h" +#include "SkOSPath.h" + static void debug_out(int len, const char* data) { // convenient place to intercept arbitrary output SkDebugf("%.*s", len, data); } +bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix) { +// this->reset(); + if (!sk_isdir(fileOrPath)) { + if (!this->parseFromFile(fileOrPath)) { + SkDebugf("failed to parse %s\n", fileOrPath); + return false; + } + } else { + SkOSFile::Iter it(fileOrPath, suffix); + for (SkString file; it.next(&file); ) { + SkString p = SkOSPath::Join(fileOrPath, file.c_str()); + const char* hunk = p.c_str(); + if (!SkStrEndsWith(hunk, suffix)) { + continue; + } + if (!this->parseFromFile(hunk)) { + SkDebugf("failed to parse %s\n", hunk); + return false; + } + } + } + return true; +} + + bool ParserCommon::parseSetup(const char* path) { // this->reset(); sk_sp<SkData> data = SkData::MakeFromFileName(path); diff --git a/tools/bookmaker/spellCheck.cpp b/tools/bookmaker/spellCheck.cpp index e2c4286e18..6f50cb3bb5 100644 --- a/tools/bookmaker/spellCheck.cpp +++ b/tools/bookmaker/spellCheck.cpp @@ -214,7 +214,7 @@ bool SpellCheck::check(Definition* def) { method_name += "()"; } string formattedStr = def->formatFunction(); - if (!def->isClone()) { + if (!def->isClone() && Definition::MethodType::kOperator != def->fMethodType) { this->wordCheck(method_name); } fTableState = TableState::kNone; @@ -613,7 +613,9 @@ void SpellCheck::wordCheck(const string& str) { std::istringstream ss(str); string token; while (std::getline(ss, token, '_')) { - this->wordCheck(token); + if (token.length()) { + this->wordCheck(token); + } } return; } |