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 9namespace GrGLSLPrettyPrint { 10 11class GLSLPrettyPrint { 12public: 13 GLSLPrettyPrint() {} 14 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 } 99private: 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(); 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 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 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; 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 196SkString 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