Home | History | Annotate | Download | only in gpu
      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 #include "SkSLString.h"
      9 
     10 namespace GrSKSLPrettyPrint {
     11 
     12 class GLSLPrettyPrint {
     13 public:
     14     GLSLPrettyPrint() {}
     15 
     16     SkSL::String prettify(const char** strings, int* lengths, int count, bool countlines) {
     17         fCountlines = countlines;
     18         fTabs = 0;
     19         fLinecount = 1;
     20         fFreshline = true;
     21 
     22         // If a string breaks while in the middle 'parse until' we need to continue parsing on the
     23         // next string
     24         fInParseUntilNewline = false;
     25         fInParseUntil = false;
     26 
     27         int parensDepth = 0;
     28 
     29         // number 1st line
     30         this->lineNumbering();
     31         for (int i = 0; i < count; i++) {
     32             // setup pretty state
     33             fIndex = 0;
     34             fLength = lengths[i];
     35             fInput = strings[i];
     36 
     37             while (fLength > fIndex) {
     38                 /* the heart and soul of our prettification algorithm.  The rules should hopefully
     39                  * be self explanatory.  For '#' and '//' tokens we parse until we reach a newline.
     40                  *
     41                  * For long style comments like this one, we search for the ending token.  We also
     42                  * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines
     43                  * ourselves.  This allows us to remain in control of line numbers, and matching
     44                  * tabs Existing tabs in the input string are copied over too, but this will look
     45                  *  funny
     46                  *
     47                  * '{' and '}' are handled in basically the same way.  We add a newline if we aren't
     48                  * on a fresh line, dirty the line, then add a second newline, ie braces are always
     49                  * on their own lines indented properly.  The one funkiness here is structs print
     50                  * with the semicolon on its own line.  Its not a problem for a glsl compiler though
     51                  *
     52                  * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala
     53                  * in for loops.
     54                  *
     55                  * ';' means add a new line
     56                  *
     57                  * '\t' and '\n' are ignored in general parsing for backwards compatability with
     58                  * existing shader code and we also have a special case for handling whitespace
     59                  * at the beginning of fresh lines.
     60                  *
     61                  * Otherwise just add the new character to the pretty string, indenting if
     62                  * necessary.
     63                  */
     64                 if (fInParseUntilNewline) {
     65                     this->parseUntilNewline();
     66                 } else if (fInParseUntil) {
     67                     this->parseUntil(fInParseUntilToken);
     68                 } else if (this->hasToken("#") || this->hasToken("//")) {
     69                     this->parseUntilNewline();
     70                 } else if (this->hasToken("/*")) {
     71                     this->parseUntil("*/");
     72                 } else if ('{' == fInput[fIndex]) {
     73                     this->newline();
     74                     this->appendChar('{');
     75                     fTabs++;
     76                     this->newline();
     77                 } else if ('}' == fInput[fIndex]) {
     78                     fTabs--;
     79                     this->newline();
     80                     this->appendChar('}');
     81                     this->newline();
     82                 } else if (this->hasToken(")")) {
     83                     parensDepth--;
     84                 } else if (this->hasToken("(")) {
     85                     parensDepth++;
     86                 } else if (!parensDepth && this->hasToken(";")) {
     87                     this->newline();
     88                 } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
     89                            (fFreshline && ' ' == fInput[fIndex])) {
     90                     fIndex++;
     91                 } else {
     92                     this->appendChar(fInput[fIndex]);
     93                 }
     94             }
     95         }
     96         return fPretty;
     97     }
     98 
     99 private:
    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     SkSL::String 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 
    196 SkSL::String PrettyPrint(const char** strings, int* lengths, int count, bool countlines) {
    197     GLSLPrettyPrint pp;
    198     return pp.prettify(strings, lengths, count, countlines);
    199 }
    200 
    201 }  // namespace GrSKSLPrettyPrint
    202