1/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "bookmaker.h"
9
10static Definition* find_fiddle(Definition* def, const string& name) {
11    if (MarkType::kExample == def->fMarkType && name == def->fFiddle) {
12        return def;
13    }
14    for (auto& child : def->fChildren) {
15        Definition* result = find_fiddle(child, name);
16        if (result) {
17            return result;
18        }
19    }
20    return nullptr;
21}
22
23Definition* FiddleBase::findExample(const string& name) const {
24    for (const auto& topic : fBmhParser->fTopicMap) {
25        if (topic.second->fParent) {
26            continue;
27        }
28        Definition* def = find_fiddle(topic.second, name);
29        if (def) {
30            return def;
31        }
32    }
33    return nullptr;
34}
35
36bool FiddleBase::parseFiddles() {
37    if (!this->skipExact("{\n")) {
38        return false;
39    }
40    while (!this->eof()) {
41        if (!this->skipExact("  \"")) {
42            return false;
43        }
44        const char* nameLoc = fChar;
45        if (!this->skipToEndBracket("\"")) {
46            return false;
47        }
48        string name(nameLoc, fChar - nameLoc);
49        if (!this->skipExact("\": {\n")) {
50            return false;
51        }
52        if (!this->skipExact("    \"compile_errors\": [")) {
53            return false;
54        }
55        if (']' != this->peek()) {
56            // report compiler errors
57            int brackets = 1;
58            do {
59                if ('[' == this->peek()) {
60                    ++brackets;
61                } else if (']' == this->peek()) {
62                    --brackets;
63                }
64            } while (!this->eof() && this->next() && brackets > 0);
65            this->reportError("fiddle compile error");
66        }
67        if (!this->skipExact("],\n")) {
68            return false;
69        }
70        if (!this->skipExact("    \"runtime_error\": \"")) {
71            return false;
72        }
73        if ('"' != this->peek()) {
74            if (!this->skipToEndBracket('"')) {
75                return false;
76            }
77            this->reportError("fiddle runtime error");
78        }
79        if (!this->skipExact("\",\n")) {
80            return false;
81        }
82        if (!this->skipExact("    \"fiddleHash\": \"")) {
83            return false;
84        }
85        const char* hashStart = fChar;
86        if (!this->skipToEndBracket('"')) {
87            return false;
88        }
89        Definition* example = this->findExample(name);
90        if (!example) {
91            this->reportError("missing example");
92        }
93        string hash(hashStart, fChar - hashStart);
94        if (example) {
95            example->fHash = hash;
96        }
97        if (!this->skipExact("\",\n")) {
98            return false;
99        }
100        if (!this->skipExact("    \"text\": \"")) {
101            return false;
102        }
103        if ('"' != this->peek()) {
104            const char* stdOutStart = fChar;
105            do {
106                if ('\\' == this->peek()) {
107                    this->next();
108                } else if ('"' == this->peek()) {
109                    break;
110                }
111            } while (!this->eof() && this->next());
112            const char* stdOutEnd = fChar;
113            if (example && fTextOut) {
114                if (!this->textOut(example, stdOutStart, stdOutEnd)) {
115                    return false;
116                }
117            }
118        } else {
119            if (example && fPngOut) {
120                if (!this->pngOut(example)) {
121                    return false;
122                }
123            }
124        }
125        if (!this->skipExact("\"\n")) {
126            return false;
127        }
128        if (!this->skipExact("  }")) {
129            return false;
130        }
131        if ('\n' == this->peek()) {
132            break;
133        }
134        if (!this->skipExact(",\n")) {
135            return false;
136        }
137    }
138    return true;
139}
140
141bool FiddleParser::textOut(Definition* example, const char* stdOutStart,
142        const char* stdOutEnd) {
143    bool foundStdOut = false;
144    for (auto& textOut : example->fChildren) {
145        if (MarkType::kStdOut != textOut->fMarkType) {
146            continue;
147        }
148        foundStdOut = true;
149        bool foundVolatile = false;
150        for (auto& stdOutChild : textOut->fChildren) {
151                if (MarkType::kVolatile == stdOutChild->fMarkType) {
152                    foundVolatile = true;
153                    break;
154                }
155        }
156        TextParser bmh(textOut);
157        EscapeParser fiddle(stdOutStart, stdOutEnd);
158        do {
159            bmh.skipWhiteSpace();
160            fiddle.skipWhiteSpace();
161            const char* bmhEnd = bmh.trimmedLineEnd();
162            const char* fiddleEnd = fiddle.trimmedLineEnd();
163            ptrdiff_t bmhLen = bmhEnd - bmh.fChar;
164            SkASSERT(bmhLen > 0);
165            ptrdiff_t fiddleLen = fiddleEnd - fiddle.fChar;
166            SkASSERT(fiddleLen > 0);
167            if (bmhLen != fiddleLen) {
168                if (!foundVolatile) {
169                    bmh.reportError("mismatched stdout len\n");
170                }
171            } else  if (strncmp(bmh.fChar, fiddle.fChar, fiddleLen)) {
172                if (!foundVolatile) {
173                    bmh.reportError("mismatched stdout text\n");
174                }
175            }
176            bmh.skipToLineStart();
177            fiddle.skipToLineStart();
178        } while (!bmh.eof() && !fiddle.eof());
179        if (!foundStdOut) {
180            bmh.reportError("bmh %s missing stdout\n");
181        } else if (!bmh.eof() || !fiddle.eof()) {
182            if (!foundVolatile) {
183                bmh.reportError("%s mismatched stdout eof\n");
184            }
185        }
186    }
187    return true;
188}
189