aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorGravatar Cary Clark <caryclark@skia.org>2017-11-27 10:44:06 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-11-27 16:12:56 +0000
commita560c4796f5b83a2e55cf564dc847ad6498164b0 (patch)
tree1e47f0055e1a19621046ecbc601e999e67c34465 /tools
parent56536c42f561f6d8902703a33cf178deb91f9d4d (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.cpp975
-rw-r--r--tools/bookmaker/bookmaker.h194
-rw-r--r--tools/bookmaker/definition.cpp1274
-rw-r--r--tools/bookmaker/includeParser.cpp84
-rw-r--r--tools/bookmaker/includeWriter.cpp11
-rw-r--r--tools/bookmaker/mdOut.cpp50
-rw-r--r--tools/bookmaker/parserCommon.cpp28
-rw-r--r--tools/bookmaker/spellCheck.cpp6
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, &paramName)) {
- 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, &paramName)) {
+ 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;
}