1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2014 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrSKSLPrettyPrint.h"
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSLString.h"
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotnamespace GrSKSLPrettyPrint {
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass GLSLPrettyPrint {
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GLSLPrettyPrint() {}
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSL::String prettify(const char** strings, int* lengths, int count, bool countlines) {
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCountlines = countlines;
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fTabs = 0;
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLinecount = 1;
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fFreshline = true;
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // If a string breaks while in the middle 'parse until' we need to continue parsing on the
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // next string
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fInParseUntilNewline = false;
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fInParseUntil = false;
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int parensDepth = 0;
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // number 1st line
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->lineNumbering();
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int i = 0; i < count; i++) {
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // setup pretty state
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fIndex = 0;
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fLength = lengths[i];
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInput = strings[i];
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            while (fLength > fIndex) {
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                /* the heart and soul of our prettification algorithm.  The rules should hopefully
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * be self explanatory.  For '#' and '//' tokens we parse until we reach a newline.
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 *
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * For long style comments like this one, we search for the ending token.  We also
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * ourselves.  This allows us to remain in control of line numbers, and matching
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * tabs Existing tabs in the input string are copied over too, but this will look
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 *  funny
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 *
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * '{' and '}' are handled in basically the same way.  We add a newline if we aren't
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * on a fresh line, dirty the line, then add a second newline, ie braces are always
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * on their own lines indented properly.  The one funkiness here is structs print
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * with the semicolon on its own line.  Its not a problem for a glsl compiler though
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 *
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * in for loops.
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 *
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * ';' means add a new line
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 *
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * '\t' and '\n' are ignored in general parsing for backwards compatability with
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * existing shader code and we also have a special case for handling whitespace
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * at the beginning of fresh lines.
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 *
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * Otherwise just add the new character to the pretty string, indenting if
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 * necessary.
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 */
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (fInParseUntilNewline) {
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->parseUntilNewline();
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if (fInParseUntil) {
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->parseUntil(fInParseUntilToken);
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if (this->hasToken("#") || this->hasToken("//")) {
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->parseUntilNewline();
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if (this->hasToken("/*")) {
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->parseUntil("*/");
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if ('{' == fInput[fIndex]) {
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->newline();
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->appendChar('{');
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fTabs++;
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->newline();
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if ('}' == fInput[fIndex]) {
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fTabs--;
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->newline();
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->appendChar('}');
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->newline();
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if (this->hasToken(")")) {
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    parensDepth--;
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if (this->hasToken("(")) {
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    parensDepth++;
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if (!parensDepth && this->hasToken(";")) {
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->newline();
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           (fFreshline && ' ' == fInput[fIndex])) {
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fIndex++;
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else {
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->appendChar(fInput[fIndex]);
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return fPretty;
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void appendChar(char c) {
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->tabString();
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fPretty.appendf("%c", fInput[fIndex++]);
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fFreshline = false;
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // hasToken automatically consumes the next token, if it is a match, and then tabs
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // if necessary, before inserting the token into the pretty string
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool hasToken(const char* token) {
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t i = fIndex;
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (size_t j = 0; token[j] && fLength > i; i++, j++) {
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (token[j] != fInput[i]) {
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return false;
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->tabString();
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fIndex = i;
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fPretty.append(token);
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fFreshline = false;
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return true;
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void parseUntilNewline() {
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        while (fLength > fIndex) {
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if ('\n' == fInput[fIndex]) {
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fIndex++;
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->newline();
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fInParseUntilNewline = false;
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fPretty.appendf("%c", fInput[fIndex++]);
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInParseUntilNewline = true;
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // this code assumes it is not actually searching for a newline.  If you need to search for a
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // newline, then use the function above.  If you do search for a newline with this function
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // it will consume the entire string and the output will certainly not be prettified
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void parseUntil(const char* token) {
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        while (fLength > fIndex) {
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // For embedded newlines,  this code will make sure to embed the newline in the
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // pretty string, increase the linecount, and tab out the next line to the appropriate
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // place
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if ('\n' == fInput[fIndex]) {
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->newline();
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->tabString();
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fIndex++;
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (this->hasToken(token)) {
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fInParseUntil = false;
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                break;
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fFreshline = false;
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fPretty.appendf("%c", fInput[fIndex++]);
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInParseUntil = true;
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInParseUntilToken = token;
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // We only tab if on a newline, otherwise consider the line tabbed
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void tabString() {
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fFreshline) {
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int t = 0; t < fTabs; t++) {
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fPretty.append("\t");
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // newline is really a request to add a newline, if we are on a fresh line there is no reason
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // to add another newline
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void newline() {
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fFreshline) {
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fFreshline = true;
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fPretty.append("\n");
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->lineNumbering();
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void lineNumbering() {
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fCountlines) {
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fPretty.appendf("%4d\t", fLinecount++);
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool fCountlines, fFreshline;
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int fTabs, fLinecount;
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    size_t fIndex, fLength;
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* fInput;
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkSL::String fPretty;
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Some helpers for parseUntil when we go over a string length
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool fInParseUntilNewline;
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool fInParseUntil;
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* fInParseUntilToken;
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkSL::String PrettyPrint(const char** strings, int* lengths, int count, bool countlines) {
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GLSLPrettyPrint pp;
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return pp.prettify(strings, lengths, count, countlines);
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}  // namespace GrSKSLPrettyPrint
202