blob: cb2da7589c5c2169c4edf0f153b2505ab163c7b4 [file] [log] [blame]
joshualittbab82ed2014-08-08 09:41:42 -07001/*
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 */
Brian Salomone334c592017-05-15 11:00:58 -04007#include "GrSKSLPrettyPrint.h"
Brian Osman93ba0a42017-08-14 14:48:10 -04008#include "SkSLString.h"
joshualittbab82ed2014-08-08 09:41:42 -07009
Brian Salomone334c592017-05-15 11:00:58 -040010namespace GrSKSLPrettyPrint {
joshualittbab82ed2014-08-08 09:41:42 -070011
12class GLSLPrettyPrint {
13public:
14 GLSLPrettyPrint() {}
15
Brian Osman93ba0a42017-08-14 14:48:10 -040016 SkSL::String prettify(const char** strings, int* lengths, int count, bool countlines) {
joshualittbab82ed2014-08-08 09:41:42 -070017 fCountlines = countlines;
18 fTabs = 0;
19 fLinecount = 1;
20 fFreshline = true;
21
joshualitt43466a12015-02-13 17:18:27 -080022 // 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
joshualittbab82ed2014-08-08 09:41:42 -070027 int parensDepth = 0;
joshualitt43466a12015-02-13 17:18:27 -080028
joshualittbab82ed2014-08-08 09:41:42 -070029 // number 1st line
30 this->lineNumbering();
joshualitt43466a12015-02-13 17:18:27 -080031 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 *
Brian Salomone334c592017-05-15 11:00:58 -040061 * Otherwise just add the new character to the pretty string, indenting if
62 * necessary.
joshualitt43466a12015-02-13 17:18:27 -080063 */
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] ||
Brian Salomone334c592017-05-15 11:00:58 -040089 (fFreshline && ' ' == fInput[fIndex])) {
joshualitt43466a12015-02-13 17:18:27 -080090 fIndex++;
91 } else {
92 this->appendChar(fInput[fIndex]);
93 }
joshualittbab82ed2014-08-08 09:41:42 -070094 }
95 }
96 return fPretty;
97 }
Brian Salomone334c592017-05-15 11:00:58 -040098
joshualittbab82ed2014-08-08 09:41:42 -070099private:
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
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
122 void parseUntilNewline() {
123 while (fLength > fIndex) {
124 if ('\n' == fInput[fIndex]) {
125 fIndex++;
126 this->newline();
joshualitt43466a12015-02-13 17:18:27 -0800127 fInParseUntilNewline = false;
joshualittbab82ed2014-08-08 09:41:42 -0700128 break;
129 }
130 fPretty.appendf("%c", fInput[fIndex++]);
joshualitt43466a12015-02-13 17:18:27 -0800131 fInParseUntilNewline = true;
joshualittbab82ed2014-08-08 09:41:42 -0700132 }
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
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)) {
joshualitt43466a12015-02-13 17:18:27 -0800149 fInParseUntil = false;
joshualittbab82ed2014-08-08 09:41:42 -0700150 break;
151 }
152 fFreshline = false;
153 fPretty.appendf("%c", fInput[fIndex++]);
joshualitt43466a12015-02-13 17:18:27 -0800154 fInParseUntil = true;
155 fInParseUntilToken = token;
joshualittbab82ed2014-08-08 09:41:42 -0700156 }
157 }
158
159 // We only tab if on a newline, otherwise consider the line tabbed
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
170 void newline() {
171 if (!fFreshline) {
172 fFreshline = true;
173 fPretty.append("\n");
174 this->lineNumbering();
175 }
176 }
177
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;
joshualitt43466a12015-02-13 17:18:27 -0800187 const char* fInput;
Brian Osman93ba0a42017-08-14 14:48:10 -0400188 SkSL::String fPretty;
joshualitt43466a12015-02-13 17:18:27 -0800189
190 // Some helpers for parseUntil when we go over a string length
191 bool fInParseUntilNewline;
192 bool fInParseUntil;
193 const char* fInParseUntilToken;
joshualittbab82ed2014-08-08 09:41:42 -0700194};
195
Brian Osman93ba0a42017-08-14 14:48:10 -0400196SkSL::String PrettyPrint(const char** strings, int* lengths, int count, bool countlines) {
joshualittbab82ed2014-08-08 09:41:42 -0700197 GLSLPrettyPrint pp;
joshualitt43466a12015-02-13 17:18:27 -0800198 return pp.prettify(strings, lengths, count, countlines);
joshualittbab82ed2014-08-08 09:41:42 -0700199}
200
Brian Salomone334c592017-05-15 11:00:58 -0400201} // namespace GrSKSLPrettyPrint