aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/bookmaker/bookmaker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/bookmaker/bookmaker.cpp')
-rw-r--r--tools/bookmaker/bookmaker.cpp391
1 files changed, 370 insertions, 21 deletions
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp
index 02dbbc1961..91e4cc2b23 100644
--- a/tools/bookmaker/bookmaker.cpp
+++ b/tools/bookmaker/bookmaker.cpp
@@ -6,6 +6,7 @@
*/
#include "bookmaker.h"
+#include "SkOSPath.h"
#ifdef SK_BUILD_FOR_WIN
#include <Windows.h>
@@ -189,7 +190,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
if (!typeNameBuilder.size()) {
return this->reportError<bool>("unnamed topic");
}
- fTopics.emplace_front(markType, defStart, fLineCount, fParent);
+ fTopics.emplace_front(markType, defStart, fLineCount, fParent, fMC);
RootDefinition* rootDefinition = &fTopics.front();
definition = rootDefinition;
definition->fFileName = fFileName;
@@ -277,6 +278,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
// may be one-liner
case MarkType::kNoExample:
case MarkType::kParam:
+ case MarkType::kPhraseDef:
case MarkType::kReturn:
case MarkType::kToDo:
if (hasEnd) {
@@ -289,22 +291,37 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
if (!this->popParentStack(fParent)) { // if not one liner, pop
return false;
}
- if (MarkType::kParam == markType || MarkType::kReturn == markType) {
+ if (MarkType::kParam == markType || MarkType::kReturn == markType
+ || MarkType::kPhraseDef == markType) {
if (!this->checkParamReturn(definition)) {
return false;
}
}
+ if (MarkType::kPhraseDef == markType) {
+ string key = definition->fName;
+ if (fPhraseMap.end() != fPhraseMap.find(key)) {
+ this->reportError<bool>("duplicate phrase key");
+ }
+ fPhraseMap[key] = definition;
+ }
} else {
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent);
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
definition = &fMarkup.front();
definition->fName = typeNameBuilder[0];
definition->fFiddle = fParent->fFiddle;
definition->fContentStart = fChar;
- definition->fContentEnd = this->trimmedBracketEnd(fMC);
- this->skipToEndBracket(fMC);
+ string endBracket;
+ endBracket += fMC;
+ endBracket += fMC;
+ definition->fContentEnd = this->trimmedBracketEnd(endBracket);
+ this->skipToEndBracket(endBracket.c_str());
SkAssertResult(fMC == this->next());
SkAssertResult(fMC == this->next());
definition->fTerminator = fChar;
+ TextParser checkForChildren(definition);
+ if (checkForChildren.strnchr(fMC, definition->fContentEnd)) {
+ this->reportError<bool>("put ## on separate line");
+ }
fParent->fChildren.push_back(definition);
}
break;
@@ -334,10 +351,11 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
return this->reportError<bool>("missing example body");
}
}
- definition->setWrapper();
+// can't do this here; phrase refs may not have been defined yet
+// this->setWrapper(definition);
}
} else {
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent);
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
definition = &fMarkup.front();
definition->fContentStart = fChar;
definition->fName = typeNameBuilder[0];
@@ -395,7 +413,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
} else if (!hasEnd && MarkType::kAnchor == markType) {
return this->reportError<bool>("anchor line must have end element last");
}
- fMarkup.emplace_front(markType, defStart, fLineCount, fParent);
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
definition = &fMarkup.front();
definition->fName = typeNameBuilder[0];
definition->fFiddle = Definition::NormalizedName(typeNameBuilder[0]);
@@ -405,7 +423,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
fParent->fChildren.push_back(definition);
if (MarkType::kAnchor == markType) {
this->skipToEndBracket(fMC);
- fMarkup.emplace_front(MarkType::kLink, fChar, fLineCount, definition);
+ fMarkup.emplace_front(MarkType::kLink, fChar, fLineCount, definition, fMC);
SkAssertResult(fMC == this->next());
this->skipWhiteSpace();
Definition* link = &fMarkup.front();
@@ -437,7 +455,7 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
if (fMC != this->next() || fMC != this->next()) {
return this->reportError<bool>("expected ## to delineate line");
}
- fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition);
+ fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition, fMC);
Definition* text = &fMarkup.front();
text->fContentStart = start;
text->fContentEnd = end;
@@ -690,7 +708,8 @@ bool BmhParser::collectExternals() {
const char* wordStart = fChar;
this->skipToNonAlphaNum();
if (fChar - wordStart > 0) {
- fExternals.emplace_front(MarkType::kExternal, wordStart, fChar, fLineCount, fParent);
+ fExternals.emplace_front(MarkType::kExternal, wordStart, fChar, fLineCount, fParent,
+ fMC);
RootDefinition* definition = &fExternals.front();
definition->fFileName = fFileName;
definition->fName = string(wordStart ,fChar - wordStart);
@@ -700,10 +719,10 @@ bool BmhParser::collectExternals() {
return true;
}
-static bool dump_examples(FILE* fiddleOut, const Definition& def, bool* continuation) {
+bool BmhParser::dumpExamples(FILE* fiddleOut, Definition& def, bool* continuation) const {
if (MarkType::kExample == def.fMarkType) {
string result;
- if (!def.exampleToScript(&result, Definition::ExampleOptions::kAll)) {
+ if (!this->exampleToScript(&def, BmhParser::ExampleOptions::kAll, &result)) {
return false;
}
if (result.length() > 0) {
@@ -719,7 +738,7 @@ static bool dump_examples(FILE* fiddleOut, const Definition& def, bool* continua
return true;
}
for (auto& child : def.fChildren ) {
- if (!dump_examples(fiddleOut, *child, continuation)) {
+ if (!this->dumpExamples(fiddleOut, *child, continuation)) {
return false;
}
}
@@ -738,7 +757,7 @@ bool BmhParser::dumpExamples(const char* fiddleJsonFileName) const {
if (topic.second->fParent) {
continue;
}
- dump_examples(fiddleOut, *topic.second, &continuation);
+ this->dumpExamples(fiddleOut, *topic.second, &continuation);
}
fprintf(fiddleOut, "\n}\n");
fclose(fiddleOut);
@@ -767,6 +786,262 @@ bool BmhParser::endTableColumn(const char* end, const char* terminator) {
return true;
}
+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);
+}
+
+bool BmhParser::exampleToScript(Definition* def, ExampleOptions exampleOptions,
+ string* result) const {
+ bool hasFiddle = true;
+ const Definition* platform = def->hasChild(MarkType::kPlatform);
+ if (platform) {
+ TextParser platParse(platform);
+ hasFiddle = !platParse.strnstr("!fiddle", platParse.fEnd);
+ }
+ if (!hasFiddle) {
+ *result = "";
+ return true;
+ }
+ string text = this->extractText(def, 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(def->fFiddle);
+ string code;
+ string imageStr = "0";
+ string srgbStr = "false";
+ string durationStr = "0";
+ for (auto iter : def->fChildren) {
+ switch (iter->fMarkType) {
+ case MarkType::kDuration:
+ durationStr = string(iter->fContentStart, iter->fContentEnd - iter->fContentStart);
+ break;
+ 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 = this->extractText(&*iter, TrimExtract::kNo);
+ 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::kBug:
+ case MarkType::kMarkChar:
+ case MarkType::kPlatform:
+ case MarkType::kPhraseRef:
+ // ignore for now
+ break;
+ case MarkType::kSet:
+ if ("sRGB" == string(iter->fContentStart,
+ iter->fContentEnd - iter->fContentStart)) {
+ srgbStr = "true";
+ } else {
+ SkASSERT(0); // more work to do
+ return false;
+ }
+ break;
+ case MarkType::kStdOut:
+ textOut = true;
+ break;
+ default:
+ SkASSERT(0); // more coding to do
+ }
+ }
+ string animatedStr = "0" != durationStr ? "true" : "false";
+ 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 ("" == def->fWrapper) {
+ this->setWrapper(def);
+ }
+ if (def->fWrapper.length() > 0) {
+ code += def->fWrapper;
+ code += "\\n";
+ outIndent = 4;
+ }
+ add_code(text, pos, end, outIndent, textIndent, code);
+ if (def->fWrapper.length() > 0) {
+ code += "}";
+ }
+ string example = "\"" + normalizedName + "\": {\n";
+ size_t nameStart = def->fFileName.find(SkOSPath::SEPARATOR, 0);
+ SkASSERT(string::npos != nameStart);
+ string baseFile = def->fFileName.substr(nameStart + 1, def->fFileName.length() - nameStart - 5);
+ if (ExampleOptions::kText == exampleOptions) {
+ example += " \"code\": \"" + code + "\",\n";
+ example += " \"hash\": \"" + def->fHash + "\",\n";
+ example += " \"file\": \"" + baseFile + "\",\n";
+ example += " \"name\": \"" + def->fName + "\",";
+ } else {
+ example += " \"code\": \"" + code + "\",\n";
+ if (ExampleOptions::kPng == exampleOptions) {
+ example += " \"width\": " + widthStr + ",\n";
+ example += " \"height\": " + heightStr + ",\n";
+ example += " \"hash\": \"" + def->fHash + "\",\n";
+ example += " \"file\": \"" + baseFile + "\",\n";
+ example += " \"name\": \"" + def->fName + "\"\n";
+ example += "}";
+ } else {
+ example += " \"options\": {\n";
+ example += " \"width\": " + widthStr + ",\n";
+ example += " \"height\": " + heightStr + ",\n";
+ example += " \"source\": " + imageStr + ",\n";
+ example += " \"srgb\": " + srgbStr + ",\n";
+ example += " \"f16\": false,\n";
+ example += " \"textOnly\": " + textOutStr + ",\n";
+ example += " \"animated\": " + animatedStr + ",\n";
+ example += " \"duration\": " + durationStr + "\n";
+ example += " },\n";
+ example += " \"fast\": true";
+ }
+ }
+ *result = example;
+ return true;
+}
+
+string BmhParser::extractText(const Definition* def, TrimExtract trimExtract) const {
+ string result;
+ TextParser parser(def);
+ auto childIter = def->fChildren.begin();
+ while (!parser.eof()) {
+ const char* end = def->fChildren.end() == childIter ? parser.fEnd : (*childIter)->fStart;
+ 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);
+ if (def->fChildren.end() != childIter) {
+ Definition* child = *childIter;
+ if (MarkType::kPhraseRef == child->fMarkType) {
+ auto phraseIter = fPhraseMap.find(child->fName);
+ if (fPhraseMap.end() == phraseIter) {
+ return def->reportError<string>("missing phrase definition");
+ }
+ Definition* phrase = phraseIter->second;
+ // count indent of last line in result
+ size_t lastLF = result.rfind('\n');
+ size_t startPos = string::npos == lastLF ? 0 : lastLF;
+ size_t lastLen = result.length() - startPos;
+ size_t indent = count_indent(result, startPos, result.length()) + 4;
+ string phraseStr = this->extractText(phrase, TrimExtract::kNo);
+ startPos = 0;
+ bool firstTime = true;
+ size_t endPos;
+ do {
+ endPos = phraseStr.find('\n', startPos);
+ size_t len = (string::npos != endPos ? endPos : phraseStr.length()) - startPos;
+ if (firstTime && lastLen + len + 1 < 100) { // FIXME: make 100 global const or something
+ result += ' ';
+ } else {
+ result += '\n';
+ result += string(indent, ' ');
+ }
+ firstTime = false;
+ string tmp = phraseStr.substr(startPos, len);
+ result += tmp;
+ startPos = endPos + 1;
+ } while (string::npos != endPos);
+ result += '\n';
+ }
+ parser.skipTo(child->fTerminator);
+ std::advance(childIter, 1);
+ }
+ }
+ return result;
+}
+
+void BmhParser::setWrapper(Definition* def) const {
+ const char drawWrapper[] = "void draw(SkCanvas* canvas) {";
+ const char drawNoCanvas[] = "void draw(SkCanvas* ) {";
+ string text = this->extractText(def, 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) {
+ def->fWrapper = hasCanvas ? string(drawNoCanvas) : string(drawWrapper);
+ }
+}
+
// FIXME: some examples may produce different output on different platforms
// if the text output can be different, think of how to author that
@@ -812,7 +1087,7 @@ bool BmhParser::findDefinitions() {
if (' ' >= fMC) {
return this->reportError<bool>("illegal markup character");
}
- fMarkup.emplace_front(MarkType::kMarkChar, fChar - 1, fLineCount, fParent);
+ fMarkup.emplace_front(MarkType::kMarkChar, fChar - 4, fLineCount, fParent, fMC);
Definition* markChar = &fMarkup.front();
markChar->fContentStart = fChar - 1;
this->skipToEndBracket('\n');
@@ -865,7 +1140,7 @@ bool BmhParser::findDefinitions() {
}
} else { // one line comment
fMarkup.emplace_front(MarkType::kComment, fChar - 1, fLineCount,
- fParent);
+ fParent, fMC);
Definition* comment = &fMarkup.front();
comment->fContentStart = fChar - 1;
this->skipToEndBracket('\n');
@@ -890,7 +1165,7 @@ bool BmhParser::findDefinitions() {
} else if (TableState::kNone == fTableState) {
// fixme? no nested tables for now
fColStart = fChar - 1;
- fMarkup.emplace_front(MarkType::kRow, fColStart, fLineCount, fParent);
+ fMarkup.emplace_front(MarkType::kRow, fColStart, fLineCount, fParent, fMC);
fRow = &fMarkup.front();
fRow->fName = fParent->fName;
this->skipWhiteSpace();
@@ -899,7 +1174,7 @@ bool BmhParser::findDefinitions() {
fTableState = TableState::kColumnStart;
}
if (TableState::kColumnStart == fTableState) {
- fMarkup.emplace_front(MarkType::kColumn, fColStart, fLineCount, fParent);
+ fMarkup.emplace_front(MarkType::kColumn, fColStart, fLineCount, fParent, fMC);
fWorkingColumn = &fMarkup.front();
fWorkingColumn->fName = fParent->fName;
fWorkingColumn->fContentStart = fChar;
@@ -907,6 +1182,28 @@ bool BmhParser::findDefinitions() {
fTableState = TableState::kColumnEnd;
continue;
}
+ } else if (this->peek() >= 'a' && this->peek() <= 'z') {
+ // expect zero or more letters and underscores (no spaces) then hash
+ const char* phraseNameStart = fChar;
+ this->skipPhraseName();
+ string phraseKey = string(phraseNameStart, fChar - phraseNameStart);
+ if (fMC != this->next()) {
+ return this->reportError<bool>("expect # after phrase-name");
+ }
+ const char* start = phraseNameStart;
+ SkASSERT('#' == start[-1]);
+ --start;
+ if (start > fStart && ' ' >= start[-1]) {
+ --start; // preserve whether to add whitespace before substitution
+ }
+ fMarkup.emplace_front(MarkType::kPhraseRef, start, fLineCount, fParent, fMC);
+ Definition* markChar = &fMarkup.front();
+ markChar->fContentStart = fChar;
+ this->skipToEndBracket('\n');
+ markChar->fContentEnd = fChar;
+ markChar->fTerminator = fChar;
+ markChar->fName = phraseKey;
+ fParent->fChildren.push_back(markChar);
}
}
char nextChar = this->next();
@@ -1316,6 +1613,45 @@ const Definition* BmhParser::parentSpace() const {
return parent;
}
+const char* BmhParser::checkForFullTerminal(const char* end, const Definition* definition) const {
+ const char* start = end;
+ while ('\n' != start[0] && start > fStart) {
+ --start;
+ }
+ SkASSERT (start < end);
+ // if end is preceeeded by \n#MarkType ## backup to there
+ TextParser parser(fFileName, start, fChar, fLineCount);
+ parser.skipWhiteSpace();
+ if (parser.eof() || fMC != parser.next()) {
+ return end;
+ }
+ const char* markName = fMaps[(int) definition->fMarkType].fName;
+ if (!parser.skipExact(markName)) {
+ return end;
+ }
+ parser.skipWhiteSpace();
+ const char* nameStart = parser.fChar;
+ if (isupper(nameStart[0])) {
+ parser.skipToWhiteSpace();
+ if (parser.eof()) {
+ return end;
+ }
+ string defName = string(nameStart, parser.fChar - nameStart);
+ size_t defNamePos = definition->fName.rfind(defName);
+ if (definition->fName.length() != defNamePos + defName.length()) {
+ return end;
+ }
+ }
+ parser.skipWhiteSpace();
+ if (fMC != parser.next()) {
+ return end;
+ }
+ if (!parser.eof() && fMC != parser.next()) {
+ return end;
+ }
+ return start;
+}
+
bool BmhParser::popParentStack(Definition* definition) {
if (!fParent) {
return this->reportError<bool>("missing parent");
@@ -1329,7 +1665,19 @@ bool BmhParser::popParentStack(Definition* definition) {
if (definition->fContentEnd) {
return this->reportError<bool>("definition already ended");
}
- definition->fContentEnd = fLine - 1;
+ // more to figure out to handle table columns, at minimum
+ const char* end = fChar;
+ if (fMC != end[0]) {
+ while (end > definition->fContentStart && ' ' >= end[-1]) {
+ --end;
+ }
+ SkASSERT(&end[-1] >= definition->fContentStart && fMC == end[-1]
+ && (MarkType::kColumn == definition->fMarkType
+ || (&end[-2] >= definition->fContentStart && fMC == end[-2])));
+ end -= 2;
+ }
+ end = checkForFullTerminal(end, definition);
+ definition->fContentEnd = end;
definition->fTerminator = fChar;
fParent = definition->fParent;
if (!fParent || (MarkType::kTopic == fParent->fMarkType && !fParent->fParent)) {
@@ -1629,7 +1977,8 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
builder = this->typedefName();
break;
case MarkType::kParam:
- // fixme: expect camelCase
+ case MarkType::kPhraseDef:
+ // fixme: expect camelCase for param
builder = this->word("", "");
this->skipSpace();
*checkEnd = false;