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