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 9 namespace GrSKSLPrettyPrint { 10 11 class GLSLPrettyPrint { 12 public: 13 GLSLPrettyPrint() {} 14 15 SkString prettify(const char** strings, int* lengths, int count, bool countlines) { 16 fCountlines = countlines; 17 fTabs = 0; 18 fLinecount = 1; 19 fFreshline = true; 20 21 // 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 26 int parensDepth = 0; 27 28 // number 1st line 29 this->lineNumbering(); 30 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 * 60 * Otherwise just add the new character to the pretty string, indenting if 61 * necessary. 62 */ 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] || 88 (fFreshline && ' ' == fInput[fIndex])) { 89 fIndex++; 90 } else { 91 this->appendChar(fInput[fIndex]); 92 } 93 } 94 } 95 return fPretty; 96 } 97 98 private: 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(); 126 fInParseUntilNewline = false; 127 break; 128 } 129 fPretty.appendf("%c", fInput[fIndex++]); 130 fInParseUntilNewline = true; 131 } 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)) { 148 fInParseUntil = false; 149 break; 150 } 151 fFreshline = false; 152 fPretty.appendf("%c", fInput[fIndex++]); 153 fInParseUntil = true; 154 fInParseUntilToken = token; 155 } 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; 186 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; 193 }; 194 195 SkString PrettyPrint(const char** strings, int* lengths, int count, bool countlines) { 196 GLSLPrettyPrint pp; 197 return pp.prettify(strings, lengths, count, countlines); 198 } 199 200 } // namespace GrSKSLPrettyPrint 201