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 "GrSKSLPrettyPrint.h"
8 #include "SkSLString.h"
9
10 namespace GrSKSLPrettyPrint {
11
12 class GLSLPrettyPrint {
13 public:
GLSLPrettyPrint()14 GLSLPrettyPrint() {}
15
prettify(const char ** strings,int * lengths,int count,bool countlines)16 SkSL::String prettify(const char** strings, int* lengths, int count, bool countlines) {
17 fCountlines = countlines;
18 fTabs = 0;
19 fLinecount = 1;
20 fFreshline = true;
21
22 // If a string breaks while in the middle 'parse until' we need to continue parsing on the
23 // next string
24 fInParseUntilNewline = false;
25 fInParseUntil = false;
26
27 int parensDepth = 0;
28
29 // number 1st line
30 this->lineNumbering();
31 for (int i = 0; i < count; i++) {
32 // setup pretty state
33 fIndex = 0;
34 fLength = lengths[i];
35 fInput = strings[i];
36
37 while (fLength > fIndex) {
38 /* the heart and soul of our prettification algorithm. The rules should hopefully
39 * be self explanatory. For '#' and '//' tokens we parse until we reach a newline.
40 *
41 * For long style comments like this one, we search for the ending token. We also
42 * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines
43 * ourselves. This allows us to remain in control of line numbers, and matching
44 * tabs Existing tabs in the input string are copied over too, but this will look
45 * funny
46 *
47 * '{' and '}' are handled in basically the same way. We add a newline if we aren't
48 * on a fresh line, dirty the line, then add a second newline, ie braces are always
49 * on their own lines indented properly. The one funkiness here is structs print
50 * with the semicolon on its own line. Its not a problem for a glsl compiler though
51 *
52 * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala
53 * in for loops.
54 *
55 * ';' means add a new line
56 *
57 * '\t' and '\n' are ignored in general parsing for backwards compatability with
58 * existing shader code and we also have a special case for handling whitespace
59 * at the beginning of fresh lines.
60 *
61 * Otherwise just add the new character to the pretty string, indenting if
62 * necessary.
63 */
64 if (fInParseUntilNewline) {
65 this->parseUntilNewline();
66 } else if (fInParseUntil) {
67 this->parseUntil(fInParseUntilToken);
68 } else if (this->hasToken("#") || this->hasToken("//")) {
69 this->parseUntilNewline();
70 } else if (this->hasToken("/*")) {
71 this->parseUntil("*/");
72 } else if ('{' == fInput[fIndex]) {
73 this->newline();
74 this->appendChar('{');
75 fTabs++;
76 this->newline();
77 } else if ('}' == fInput[fIndex]) {
78 fTabs--;
79 this->newline();
80 this->appendChar('}');
81 this->newline();
82 } else if (this->hasToken(")")) {
83 parensDepth--;
84 } else if (this->hasToken("(")) {
85 parensDepth++;
86 } else if (!parensDepth && this->hasToken(";")) {
87 this->newline();
88 } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
89 (fFreshline && ' ' == fInput[fIndex])) {
90 fIndex++;
91 } else {
92 this->appendChar(fInput[fIndex]);
93 }
94 }
95 }
96 return fPretty;
97 }
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 SkSL::String 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
PrettyPrint(const char ** strings,int * lengths,int count,bool countlines)196 SkSL::String PrettyPrint(const char** strings, int* lengths, int count, bool countlines) {
197 GLSLPrettyPrint pp;
198 return pp.prettify(strings, lengths, count, countlines);
199 }
200
201 } // namespace GrSKSLPrettyPrint
202