Home | History | Annotate | Download | only in builders
      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 
      9 namespace GrGLSLPrettyPrint {
     10 
     11 class GLSLPrettyPrint {
     12 public:
     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     }
     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     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 
    196 SkString 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