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