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