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 SkString& input, bool countlines) { 16 // setup pretty state 17 fIndex = 0; 18 fLength = input.size(); 19 fInput = input; 20 fCountlines = countlines; 21 fTabs = 0; 22 fLinecount = 1; 23 fFreshline = true; 24 25 int parensDepth = 0; 26 // number 1st line 27 this->lineNumbering(); 28 while (fLength > fIndex) { 29 /* the heart and soul of our prettification algorithm. The rules should hopefully be 30 * self explanatory. For '#' and '//' tokens we parse until we reach a newline. 31 * 32 * For long style comments like this one, we search for the ending token. We also 33 * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines 34 * ourselves. This allows us to remain in control of line numbers, and matching tabs 35 * Existing tabs in the input string are copied over too, but this will look funny 36 * 37 * '{' and '}' are handled in basically the same way. We add a newline if we aren't 38 * on a fresh line, dirty the line, then add a second newline, ie braces are always 39 * on their own lines indented properly. The one funkiness here is structs print with 40 * the semicolon on its own line. Its not a problem for a glsl compiler though 41 * 42 * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala 43 * in for loops. 44 * 45 * ';' means add a new line 46 * 47 * '\t' and '\n' are ignored in general parsing for backwards compatability with 48 * existing shader code and we also have a special case for handling whitespace 49 * at the beginning of fresh lines. 50 * 51 * Otherwise just add the new character to the pretty string, indenting if necessary. 52 */ 53 if (this->hasToken("#") || this->hasToken("//")) { 54 this->parseUntilNewline(); 55 } else if (this->hasToken("/*")) { 56 this->parseUntil("*/"); 57 } else if ('{' == fInput[fIndex]) { 58 this->newline(); 59 this->appendChar('{'); 60 fTabs++; 61 this->newline(); 62 } else if ('}' == fInput[fIndex]) { 63 fTabs--; 64 this->newline(); 65 this->appendChar('}'); 66 this->newline(); 67 } else if (this->hasToken(")")) { 68 parensDepth--; 69 } else if (this->hasToken("(")) { 70 parensDepth++; 71 } else if (!parensDepth && this->hasToken(";")) { 72 this->newline(); 73 } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] || 74 (fFreshline && ' ' == fInput[fIndex])) { 75 fIndex++; 76 } else { 77 this->appendChar(input[fIndex]); 78 } 79 } 80 return fPretty; 81 } 82private: 83 void appendChar(char c) { 84 this->tabString(); 85 fPretty.appendf("%c", fInput[fIndex++]); 86 fFreshline = false; 87 } 88 89 // hasToken automatically consumes the next token, if it is a match, and then tabs 90 // if necessary, before inserting the token into the pretty string 91 bool hasToken(const char* token) { 92 size_t i = fIndex; 93 for (size_t j = 0; token[j] && fLength > i; i++, j++) { 94 if (token[j] != fInput[i]) { 95 return false; 96 } 97 } 98 this->tabString(); 99 fIndex = i; 100 fPretty.append(token); 101 fFreshline = false; 102 return true; 103 } 104 105 void parseUntilNewline() { 106 while (fLength > fIndex) { 107 if ('\n' == fInput[fIndex]) { 108 fIndex++; 109 this->newline(); 110 break; 111 } 112 fPretty.appendf("%c", fInput[fIndex++]); 113 } 114 } 115 116 // this code assumes it is not actually searching for a newline. If you need to search for a 117 // newline, then use the function above. If you do search for a newline with this function 118 // it will consume the entire string and the output will certainly not be prettified 119 void parseUntil(const char* token) { 120 while (fLength > fIndex) { 121 // For embedded newlines, this code will make sure to embed the newline in the 122 // pretty string, increase the linecount, and tab out the next line to the appropriate 123 // place 124 if ('\n' == fInput[fIndex]) { 125 this->newline(); 126 this->tabString(); 127 fIndex++; 128 } 129 if (this->hasToken(token)) { 130 break; 131 } 132 fFreshline = false; 133 fPretty.appendf("%c", fInput[fIndex++]); 134 } 135 } 136 137 // We only tab if on a newline, otherwise consider the line tabbed 138 void tabString() { 139 if (fFreshline) { 140 for (int t = 0; t < fTabs; t++) { 141 fPretty.append("\t"); 142 } 143 } 144 } 145 146 // newline is really a request to add a newline, if we are on a fresh line there is no reason 147 // to add another newline 148 void newline() { 149 if (!fFreshline) { 150 fFreshline = true; 151 fPretty.append("\n"); 152 this->lineNumbering(); 153 } 154 } 155 156 void lineNumbering() { 157 if (fCountlines) { 158 fPretty.appendf("%4d\t", fLinecount++); 159 } 160 } 161 162 bool fCountlines, fFreshline; 163 int fTabs, fLinecount; 164 size_t fIndex, fLength; 165 SkString fInput, fPretty; 166}; 167 168SkString PrettyPrintGLSL(const SkString& input, bool countlines) { 169 GLSLPrettyPrint pp; 170 return pp.prettify(input, countlines); 171} 172 173} // end namespace 174