1/*
2* Copyright 2011 The Android Open Source Project
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17#include "ShaderParser.h"
18#include <string.h>
19
20ShaderParser::ShaderParser():ObjectData(SHADER_DATA),
21                             m_type(0),
22                             m_originalSrc(NULL),
23                             m_parsedLines(NULL) {
24    m_infoLog = new GLchar[1];
25    m_infoLog[0] = '\0';
26};
27
28ShaderParser::ShaderParser(GLenum type):ObjectData(SHADER_DATA),
29                                        m_type(type),
30                                        m_originalSrc(NULL),
31                                        m_parsedLines(NULL) {
32
33    m_infoLog = new GLchar[1];
34    m_infoLog[0] = '\0';
35};
36
37void ShaderParser::setSrc(const Version& ver,GLsizei count,const GLchar** strings,const GLint* length){
38    for(int i = 0;i<count;i++){
39        m_src.append(strings[i]);
40    }
41    //store original source
42    if (m_originalSrc)
43        free(m_originalSrc);
44    m_originalSrc = strdup(m_src.c_str());
45
46    clearParsedSrc();
47
48    // parseGLSLversion must be called first since #version should be the
49    // first token in the shader source.
50    parseGLSLversion();
51    parseBuiltinConstants();
52    /*
53      version 1.30.10 is the first version of GLSL Language containing precision qualifiers
54      if the glsl version is less than 1.30.10 than we will use a shader parser which omits
55      all precision qualifiers from the shader source , otherwise we will use a shader parser
56      which set the default precisions to be the same as the default precisions of GLSL ES
57    */
58#if 0
59    if(ver < Version(1,30,10)){
60        parseOmitPrecision();
61     } else {
62        parseExtendDefaultPrecision();
63     }
64#else
65    //XXX: Until proved otherwise, glsl doesn't know/use those precision macros, so we omit then
66    parseOmitPrecision();
67#endif
68    parseLineNumbers();
69    parseOriginalSrc();
70}
71const GLchar** ShaderParser::parsedLines() {
72      m_parsedLines = (GLchar*)m_parsedSrc.c_str();
73      return const_cast<const GLchar**> (&m_parsedLines);
74};
75
76const char* ShaderParser::getOriginalSrc(){
77    return m_originalSrc;
78}
79
80void ShaderParser::parseLineNumbers()
81{
82    m_parsedSrc += "#line 1\n";
83}
84
85void ShaderParser::parseOriginalSrc() {
86    m_parsedSrc+=m_src;
87}
88
89void ShaderParser::parseGLSLversion() {
90
91    //
92    // find in shader the #version token if exist.
93    // That token should be the first non-comment or blank token
94    //
95    const char *src = m_src.c_str();
96    const int minGLSLVersion = 120;
97    int glslVersion = minGLSLVersion;
98    enum {
99        PARSE_NONE,
100        PARSE_IN_C_COMMENT,
101        PARSE_IN_LINE_COMMENT
102    } parseState = PARSE_NONE;
103    const char *c = src;
104
105    while( c && *c != '\0') {
106        if (parseState == PARSE_IN_C_COMMENT) {
107            if (*c == '*' && *(c+1) == '/') {
108                parseState = PARSE_NONE;
109                c += 2;
110            }
111            else c++;
112        }
113        else if (parseState == PARSE_IN_LINE_COMMENT) {
114            if (*c == '\n') {
115                parseState = PARSE_NONE;
116            }
117            c++;
118        }
119        else if (*c == '/' && *(c+1) == '/') {
120            parseState = PARSE_IN_LINE_COMMENT;
121            c += 2;
122        }
123        else if (*c == '/' && *(c+1) == '*') {
124            parseState = PARSE_IN_C_COMMENT;
125            c += 2;
126        }
127        else if (*c == ' ' || *c == '\t' || *c == '\r' || *c == '\n') {
128            c++;
129        }
130        else {
131            //
132            // We have reached the first non-blank character outside
133            // a comment, this must be a #version token or else #version
134            // token does not exist in this shader source.
135            //
136            if (!strncmp(c,"#version",8)) {
137                int ver;
138                if (sscanf(c+8,"%d",&ver) == 1) {
139                    //
140                    // parsed version string correctly, blank out the
141                    // version token from the source, we will add it later at
142                    // the begining of the shader.
143                    //
144                    char *cc = (char *)c;
145                    for (int i=0; i<8; i++,cc++) *cc = ' ';
146                    while (*cc < '0' || *cc > '9') { *cc = ' '; cc++; }
147                    while (*cc >= '0' && *cc <= '9') { *cc = ' '; cc++; }
148
149                    // Use the version from the source but only if
150                    // it is larger than our minGLSLVersion
151                    if (ver > minGLSLVersion) glslVersion = ver;
152                }
153            }
154
155            //
156            // break the loop, no need to go further on the source.
157            break;
158        }
159    }
160
161    //
162    // allow to force GLSL version through environment variable
163    //
164    const char *forceVersion = getenv("GOOGLE_GLES_FORCE_GLSL_VERSION");
165    if (forceVersion) {
166        int ver;
167        if (sscanf(forceVersion,"%d",&ver) == 1) {
168            glslVersion = ver;
169        }
170    }
171
172    //
173    // if glslVersion is defined, add it to the parsed source
174    //
175    if (glslVersion > 0) {
176        char vstr[16];
177        sprintf(vstr,"%d",glslVersion);
178        m_parsedSrc += std::string("#version ") +
179                       std::string(vstr) +
180                       std::string("\n");
181    }
182}
183
184void ShaderParser::parseBuiltinConstants()
185{
186    m_parsedSrc +=
187                   "const int _translator_gl_MaxVertexUniformVectors = 256;\n"
188                   "const int _translator_gl_MaxFragmentUniformVectors = 256;\n"
189                   "const int _translator_gl_MaxVaryingVectors = 15;\n"
190                   "#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n"
191                   "#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n"
192                   "#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n";
193
194}
195
196void ShaderParser::parseOmitPrecision(){
197
198    //defines we need to add in order to Omit precisions qualifiers
199    static const GLchar defines[] = {
200                                         "#define GLES 1\n"
201                                         "#define lowp \n"
202                                         "#define mediump \n"
203                                         "#define highp \n"
204                                     };
205    m_parsedSrc+=defines;
206
207    //
208    // parse the source and blank out precision statements
209    // which has the following syntax:
210    //   precision {qualifier} {type};
211    // where {qualifier} is one of lowp,mediump or hightp
212    // type is any valid GLES defined type (we do not check that here!)
213    // NOTE: This is needed in order to workaround driver bug in
214    //       Intel/Linux where the compiler does not get statement like
215    //       "float;", otherwise we could just define a macro named
216    //       precision to be empty.
217    //
218    const char *src = m_src.c_str();
219
220    enum {
221        PRECISION,
222        QUALIFIER,
223        SEMICOLON
224    } statementState = PRECISION;
225    const char *precision = NULL;
226    const char *delimiter = NULL;
227
228    enum {
229        PARSE_NONE,
230        PARSE_IN_C_COMMENT,
231        PARSE_IN_LINE_COMMENT
232    } parseState = PARSE_NONE;
233    const char *c = src;
234    const char *t = NULL;
235
236    #define IS_DELIMITER(c) ( (c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' )
237    #define IS_TOKEN_START(c) ( ((c) >= 'a' && (c) <='z') || ((c) >= 'A' && (c) <= 'Z') )
238    #define IS_TOKEN_DELIMITER(c) ( IS_DELIMITER(c) || (c) == ';' )
239
240    while( c && *c != '\0') {
241        if (parseState == PARSE_IN_C_COMMENT) {
242            if (*c == '*' && *(c+1) == '/') {
243                parseState = PARSE_NONE;
244                c += 2;
245            }
246            else c++;
247        }
248        else if (parseState == PARSE_IN_LINE_COMMENT) {
249            if (*c == '\n') {
250                parseState = PARSE_NONE;
251            }
252            c++;
253        }
254        else if (*c == '/' && *(c+1) == '/') {
255            parseState = PARSE_IN_LINE_COMMENT;
256            c += 2;
257        }
258        else if (*c == '/' && *(c+1) == '*') {
259            parseState = PARSE_IN_C_COMMENT;
260            c += 2;
261        }
262        else if (t && IS_TOKEN_DELIMITER(*c)) {
263            int tokenLen = c - t;
264            switch (statementState) {
265            case PRECISION:
266                if (tokenLen == 9 && !strncmp(t,"precision",9)) {
267                    statementState = QUALIFIER;
268                    precision = t;
269                }
270                break;
271            case QUALIFIER:
272                if ((tokenLen == 4 && !strncmp(t,"lowp",4)) ||
273                    (tokenLen == 7 && !strncmp(t,"mediump",7)) ||
274                    (tokenLen == 5 && !strncmp(t,"highp",5))) {
275                    statementState = SEMICOLON;
276                }
277                else {
278                    statementState = PRECISION;
279                }
280                break;
281            case SEMICOLON:
282                if (*c == ';') {
283                    for (char *r = (char *)precision; r<=c ; ++r) {
284                        *r = ' '; //blank the character
285                    }
286                }
287                statementState = PRECISION; //search for the next precision line
288                break;
289            default:
290                break;
291            }
292            c++;
293            t = NULL;
294        }
295        else if (IS_DELIMITER(*c)) {
296            c++;
297        }
298        else {
299            if (!t && IS_TOKEN_START(*c)) {
300                t = c;
301            }
302            c++;
303        }
304    }
305}
306
307void ShaderParser::parseExtendDefaultPrecision(){
308
309    //the precision lines which we need to add to the shader
310    static const GLchar extend[] = {
311                                      "#define GLES 1\n"
312                                      "precision lowp sampler2D;\n"
313                                      "precision lowp samplerCube;\n"
314                                   };
315
316    m_parsedSrc+=extend;
317}
318
319void ShaderParser::clearParsedSrc(){
320    m_parsedSrc.clear();
321}
322
323GLenum ShaderParser::getType() {
324    return m_type;
325}
326
327void ShaderParser::setInfoLog(GLchar* infoLog)
328{
329    delete[] m_infoLog;
330    m_infoLog = infoLog;
331}
332
333GLchar* ShaderParser::getInfoLog()
334{
335    return m_infoLog;
336}
337
338ShaderParser::~ShaderParser(){
339    clearParsedSrc();
340    if (m_originalSrc)
341        free(m_originalSrc);
342    delete[] m_infoLog;
343}
344