/* * Copyright 2014 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrSKSLPrettyPrint.h" #include "SkSLString.h" namespace GrSKSLPrettyPrint { class GLSLPrettyPrint { public: GLSLPrettyPrint() {} SkSL::String prettify(const char** strings, int* lengths, int count, bool countlines) { fCountlines = countlines; fTabs = 0; fLinecount = 1; fFreshline = true; // If a string breaks while in the middle 'parse until' we need to continue parsing on the // next string fInParseUntilNewline = false; fInParseUntil = false; int parensDepth = 0; // number 1st line this->lineNumbering(); for (int i = 0; i < count; i++) { // setup pretty state fIndex = 0; fLength = lengths[i]; fInput = strings[i]; while (fLength > fIndex) { /* the heart and soul of our prettification algorithm. The rules should hopefully * be self explanatory. For '#' and '//' tokens we parse until we reach a newline. * * For long style comments like this one, we search for the ending token. We also * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines * ourselves. This allows us to remain in control of line numbers, and matching * tabs Existing tabs in the input string are copied over too, but this will look * funny * * '{' and '}' are handled in basically the same way. We add a newline if we aren't * on a fresh line, dirty the line, then add a second newline, ie braces are always * on their own lines indented properly. The one funkiness here is structs print * with the semicolon on its own line. Its not a problem for a glsl compiler though * * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala * in for loops. * * ';' means add a new line * * '\t' and '\n' are ignored in general parsing for backwards compatability with * existing shader code and we also have a special case for handling whitespace * at the beginning of fresh lines. * * Otherwise just add the new character to the pretty string, indenting if * necessary. */ if (fInParseUntilNewline) { this->parseUntilNewline(); } else if (fInParseUntil) { this->parseUntil(fInParseUntilToken); } else if (this->hasToken("#") || this->hasToken("//")) { this->parseUntilNewline(); } else if (this->hasToken("/*")) { this->parseUntil("*/"); } else if ('{' == fInput[fIndex]) { this->newline(); this->appendChar('{'); fTabs++; this->newline(); } else if ('}' == fInput[fIndex]) { fTabs--; this->newline(); this->appendChar('}'); this->newline(); } else if (this->hasToken(")")) { parensDepth--; } else if (this->hasToken("(")) { parensDepth++; } else if (!parensDepth && this->hasToken(";")) { this->newline(); } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] || (fFreshline && ' ' == fInput[fIndex])) { fIndex++; } else { this->appendChar(fInput[fIndex]); } } } return fPretty; } private: void appendChar(char c) { this->tabString(); fPretty.appendf("%c", fInput[fIndex++]); fFreshline = false; } // hasToken automatically consumes the next token, if it is a match, and then tabs // if necessary, before inserting the token into the pretty string bool hasToken(const char* token) { size_t i = fIndex; for (size_t j = 0; token[j] && fLength > i; i++, j++) { if (token[j] != fInput[i]) { return false; } } this->tabString(); fIndex = i; fPretty.append(token); fFreshline = false; return true; } void parseUntilNewline() { while (fLength > fIndex) { if ('\n' == fInput[fIndex]) { fIndex++; this->newline(); fInParseUntilNewline = false; break; } fPretty.appendf("%c", fInput[fIndex++]); fInParseUntilNewline = true; } } // this code assumes it is not actually searching for a newline. If you need to search for a // newline, then use the function above. If you do search for a newline with this function // it will consume the entire string and the output will certainly not be prettified void parseUntil(const char* token) { while (fLength > fIndex) { // For embedded newlines, this code will make sure to embed the newline in the // pretty string, increase the linecount, and tab out the next line to the appropriate // place if ('\n' == fInput[fIndex]) { this->newline(); this->tabString(); fIndex++; } if (this->hasToken(token)) { fInParseUntil = false; break; } fFreshline = false; fPretty.appendf("%c", fInput[fIndex++]); fInParseUntil = true; fInParseUntilToken = token; } } // We only tab if on a newline, otherwise consider the line tabbed void tabString() { if (fFreshline) { for (int t = 0; t < fTabs; t++) { fPretty.append("\t"); } } } // newline is really a request to add a newline, if we are on a fresh line there is no reason // to add another newline void newline() { if (!fFreshline) { fFreshline = true; fPretty.append("\n"); this->lineNumbering(); } } void lineNumbering() { if (fCountlines) { fPretty.appendf("%4d\t", fLinecount++); } } bool fCountlines, fFreshline; int fTabs, fLinecount; size_t fIndex, fLength; const char* fInput; SkSL::String fPretty; // Some helpers for parseUntil when we go over a string length bool fInParseUntilNewline; bool fInParseUntil; const char* fInParseUntilToken; }; SkSL::String PrettyPrint(const char** strings, int* lengths, int count, bool countlines) { GLSLPrettyPrint pp; return pp.prettify(strings, lengths, count, countlines); } } // namespace GrSKSLPrettyPrint