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 
      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