tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

glsShaderLibrary.js (43573B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program OpenGL ES Utilities
      3 * ------------------------------------------------
      4 *
      5 * Copyright 2014 The Android Open Source Project
      6 *
      7 * Licensed under the Apache License, Version 2.0 (the "License");
      8 * you may not use this file except in compliance with the License.
      9 * You may obtain a copy of the License at
     10 *
     11 *      http://www.apache.org/licenses/LICENSE-2.0
     12 *
     13 * Unless required by applicable law or agreed to in writing, software
     14 * distributed under the License is distributed on an "AS IS" BASIS,
     15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 * See the License for the specific language governing permissions and
     17 * limitations under the License.
     18 *
     19 */
     20 
     21 'use strict';
     22 goog.provide('modules.shared.glsShaderLibrary');
     23 goog.require('framework.common.tcuTestCase');
     24 goog.require('framework.opengl.gluShaderUtil');
     25 goog.require('modules.shared.glsShaderLibraryCase');
     26 
     27 goog.scope(function() {
     28 
     29 var glsShaderLibrary = modules.shared.glsShaderLibrary;
     30 var tcuTestCase = framework.common.tcuTestCase;
     31 var glsShaderLibraryCase = modules.shared.glsShaderLibraryCase;
     32 var gluShaderUtil = framework.opengl.gluShaderUtil;
     33 
     34    glsShaderLibrary.generateTestCases = function() {
     35    /** @type {glsShaderLibrary.Parser} */ var parser = new glsShaderLibrary.Parser();
     36        try {
     37        /** @type {Object} */ var state = tcuTestCase.runner;
     38            var tree = parser.parse(state.testFile);
     39            var rootTest = tcuTestCase.newTest(state.testName, 'Top level');
     40            rootTest.setChildren(tree);
     41            state.setRoot(rootTest);
     42        }
     43        catch (err) {
     44            bufferedLogToConsole(err);
     45            testFailed('Failed to parse shader test case file');
     46            return false;
     47        }
     48        return true;
     49    };
     50 
     51    glsShaderLibrary.processTestFile = function() {
     52        if (glsShaderLibrary.generateTestCases()) {
     53            tcuTestCase.runner.runCallback(glsShaderLibraryCase.runTestCases);
     54        } else {
     55            tcuTestCase.runner.terminate();
     56        }
     57    };
     58 
     59    glsShaderLibrary.isWhitespace = function(value) {
     60        return /^[ \t\r\n]+$/.test(value);
     61    };
     62    glsShaderLibrary.isEOL = function(value) {
     63        return /^[\r\n]+$/.test(value);
     64    };
     65    glsShaderLibrary.isAlpha = function(value) {
     66        return /^[a-zA-Z]$/.test(value);
     67    };
     68    glsShaderLibrary.isNumeric = function(value) {
     69        return /^[0-9]$/.test(value);
     70    };
     71    glsShaderLibrary.isCaseNameChar = function(value) {
     72        return /^[a-zA-Z0-9_\-\.]$/.test(value);
     73    };
     74 
     75    /**
     76     * Removes however many indents there are on the first line from all lines.
     77     * @param {string} str
     78     * @return {string} output
     79     */
     80    glsShaderLibrary.removeExtraIndentation = function(str) {
     81        return glsShaderLibrary.removeExtraIndentationArray(
     82            str.split(/\r\n|\r|\n/)
     83        ).join('\n');
     84    };
     85 
     86    /**
     87     * Returns an array of strings without indentation.
     88     * @param {Array<string>} arr
     89     * @return {Array<string>} output
     90     */
     91    glsShaderLibrary.removeExtraIndentationArray = function(arr) {
     92    /** @type {Array<string>} */ var output = [];
     93 
     94        if (arr.length) {
     95 
     96        /** @type {number} */ var numIndentChars = 0;
     97            for (var i = 0; i < arr[0].length && glsShaderLibrary.isWhitespace(arr[0].charAt(i)); ++i) {
     98                numIndentChars += arr[0].charAt(i) === '\t' ? 4 : 1;
     99            }
    100 
    101            for (var i = 0; i < arr.length; ++i) {
    102            /** @type {number} */ var removed = 0;
    103            /** @type {number} */ var j;
    104                // Some tests are indented inconsistently, so we have to check for non-whitespace characters here.
    105                for (j = 0; removed < numIndentChars && j < arr[i].length && glsShaderLibrary.isWhitespace(arr[i].charAt(j)); ++j) {
    106                    removed += (arr[i].charAt(j) === '\t' ? 4 : 1);
    107                }
    108 
    109                output.push(arr[i].substr(j, arr[i].length - j));
    110            }
    111 
    112        }
    113 
    114        return output;
    115    };
    116 
    117    glsShaderLibrary.de_assert = function(condition) {
    118        if (!condition) {
    119            throw Error();
    120        }
    121    };
    122 
    123    /**
    124     * @param {string} str
    125     * @param {string} endstr end of string character
    126     * @param {boolean=} trimFront trim leading whitespace
    127     * @return {string} str
    128     * @private
    129     */
    130    glsShaderLibrary.parseStringLiteralHelper = function(str, endstr, trimFront) {
    131        trimFront = trimFront || false;
    132 
    133    /** @type {number} */ var index_end = 0;
    134        // isolate the string
    135        do {
    136            index_end = str.indexOf(endstr, index_end + 1);
    137        } while (index_end >= 0 && str.charAt(index_end - 1) === '\\');
    138 
    139        if (index_end <= 0) {
    140            index_end = str.length;
    141        }
    142 
    143        // strip quotes, replace \n and \t with nl and tabs respectively
    144        str = str.substr(endstr.length, index_end - endstr.length);
    145        if (trimFront)
    146            str = str.replace(/^\s*\n/, '');
    147        var result = '';
    148        var i = 0;
    149        while (str[i] != undefined) {
    150            if (str[i]  == '\\') {
    151                switch (str[i + 1]) {
    152                    case undefined:
    153                        break;
    154                    case 'n':
    155                        result += '\n';
    156                        break;
    157                    case 't':
    158                        result += '\t';
    159                        break;
    160                    default:
    161                        result += str[i + 1];
    162                        break;
    163                }
    164                i += 2;
    165            } else {
    166                result += str[i];
    167                i++;
    168            }
    169        }
    170        return result;
    171 
    172    };
    173 
    174    /**
    175     * glsShaderLibrary.Parser class
    176     * @constructor
    177     */
    178    glsShaderLibrary.Parser = function() {
    179 
    180    /* data members */
    181 
    182        /**
    183         * The Token constants
    184         * @enum {number}
    185         */
    186        var Token = {
    187            TOKEN_INVALID: 0,
    188            TOKEN_EOF: 1,
    189            TOKEN_STRING: 2,
    190            TOKEN_SHADER_SOURCE: 3,
    191 
    192            TOKEN_INT_LITERAL: 4,
    193            TOKEN_FLOAT_LITERAL: 5,
    194 
    195            // identifiers
    196            TOKEN_IDENTIFIER: 6,
    197            TOKEN_TRUE: 7,
    198            TOKEN_FALSE: 8,
    199            TOKEN_DESC: 9,
    200            TOKEN_EXPECT: 10,
    201            TOKEN_GROUP: 11,
    202            TOKEN_CASE: 12,
    203            TOKEN_END: 13,
    204            TOKEN_VALUES: 14,
    205            TOKEN_BOTH: 15,
    206            TOKEN_VERTEX: 26,
    207            TOKEN_FRAGMENT: 17,
    208            TOKEN_UNIFORM: 18,
    209            TOKEN_INPUT: 19,
    210            TOKEN_OUTPUT: 20,
    211            TOKEN_FLOAT: 21,
    212            TOKEN_FLOAT_VEC2: 22,
    213            TOKEN_FLOAT_VEC3: 23,
    214            TOKEN_FLOAT_VEC4: 24,
    215            TOKEN_FLOAT_MAT2: 25,
    216            TOKEN_FLOAT_MAT2X3: 26,
    217            TOKEN_FLOAT_MAT2X4: 27,
    218            TOKEN_FLOAT_MAT3X2: 28,
    219            TOKEN_FLOAT_MAT3: 29,
    220            TOKEN_FLOAT_MAT3X4: 30,
    221            TOKEN_FLOAT_MAT4X2: 31,
    222            TOKEN_FLOAT_MAT4X3: 32,
    223            TOKEN_FLOAT_MAT4: 33,
    224            TOKEN_INT: 34,
    225            TOKEN_INT_VEC2: 35,
    226            TOKEN_INT_VEC3: 36,
    227            TOKEN_INT_VEC4: 37,
    228            TOKEN_UINT: 38,
    229            TOKEN_UINT_VEC2: 39,
    230            TOKEN_UINT_VEC3: 40,
    231            TOKEN_UINT_VEC4: 41,
    232            TOKEN_BOOL: 42,
    233            TOKEN_BOOL_VEC2: 43,
    234            TOKEN_BOOL_VEC3: 44,
    235            TOKEN_BOOL_VEC4: 45,
    236            TOKEN_VERSION: 46,
    237 
    238            // symbols
    239            TOKEN_ASSIGN: 47,
    240            TOKEN_PLUS: 48,
    241            TOKEN_MINUS: 49,
    242            TOKEN_COMMA: 50,
    243            TOKEN_VERTICAL_BAR: 51,
    244            TOKEN_SEMI_COLON: 52,
    245            TOKEN_LEFT_PAREN: 53,
    246            TOKEN_RIGHT_PAREN: 54,
    247            TOKEN_LEFT_BRACKET: 55,
    248            TOKEN_RIGHT_BRACKET: 56,
    249            TOKEN_LEFT_BRACE: 57,
    250            TOKEN_RIGHT_BRACE: 58,
    251 
    252            TOKEN_LAST: 59
    253        };
    254 
    255        /** @type {string} */ var m_input = '';
    256        /** @type {number} */ var m_curPtr = 0;
    257        /** @type {number} */ var m_curToken;// = Token.TOKEN_INVALID;
    258        /** @type {string} */ var m_curTokenStr = '';
    259 
    260        /* function members */
    261        this.parse = function(input) {
    262 
    263            // initialise parser
    264            m_input = input;
    265            m_curPtr = 0;
    266            m_curToken = Token.TOKEN_INVALID;
    267            m_curTokenStr = '';
    268            advanceToken();
    269 
    270            /** @type {Array<tcuTestCase.DeqpTest>} */ var nodeList = [];
    271 
    272            for (;;) {
    273 
    274                if (m_curToken === Token.TOKEN_CASE) {
    275                    parseShaderCase(nodeList);
    276                } else if (m_curToken === Token.TOKEN_GROUP) {
    277                    parseShaderGroup(nodeList);
    278                } else if (m_curToken === Token.TOKEN_EOF) {
    279                    break;
    280                } else {
    281                //    throw Error("invalid token encountered at main level: '" + m_curTokenStr + "'");
    282                    testFailed("invalid token encountered at main level: '" + m_curTokenStr + "'");
    283                    tcuTestCase.runner.terminate();
    284                }
    285 
    286            }
    287 
    288            return nodeList;
    289 
    290        };
    291 
    292        /**
    293         * ensures that the token exists
    294         * otherwise it returns the corresponding token's name depending on enum number value
    295         * @param {number} id
    296         * @return {string} name
    297         */
    298        var resolveTokenName = function(id) {
    299            for (var name in Token) {
    300                if (Token[name] === id) return name;
    301            }
    302            return 'TOKEN_UNKNOWN';
    303        };
    304 
    305        /**
    306         * Throws an error which contains the passed string
    307         * @param {string} errorStr that contains an error to notify
    308         * @return {string} error
    309         */
    310        var parseError = function(errorStr) {
    311            // abort
    312            throw 'glsShaderLibrary.Parser error: ' + errorStr + ' near ' + m_input.substr(m_curPtr, m_curPtr + 80);
    313        };
    314 
    315        /**
    316         * Converts string into float
    317         * @param {string} str
    318         * @return {number}
    319         */
    320        var parseFloatLiteral = function(str) {
    321            return parseFloat(str);
    322        };
    323 
    324        /**
    325         * Converts string into integer
    326         * @param {string} str
    327         * @return {number}
    328         */
    329        var parseIntLiteral = function(str) {
    330            return parseInt(str, 10);
    331        };
    332        var parseStringLiteral = function(str) {
    333        /**
    334        * @type {string}
    335        * find delimitor
    336        */ var endchar = str.substr(0, 1);
    337            return glsShaderLibrary.parseStringLiteralHelper(str, endchar);
    338        };
    339        var parseShaderSource = function(str) {
    340            // similar to parse literal, delimitors are two double quotes ("")
    341            return glsShaderLibrary.removeExtraIndentation(
    342                glsShaderLibrary.parseStringLiteralHelper(str, '""', true)
    343            );
    344        };
    345 
    346        var advanceTokenWorker = function() {
    347 
    348            // Skip old token
    349            m_curPtr += m_curTokenStr.length;
    350 
    351            // Reset token (for safety).
    352            m_curToken = Token.TOKEN_INVALID;
    353            m_curTokenStr = '';
    354 
    355            // Eat whitespace & comments while they last.
    356            for (;;) {
    357 
    358                while (glsShaderLibrary.isWhitespace(m_input.charAt(m_curPtr))) ++m_curPtr;
    359 
    360                // check for EOL comment
    361                if (m_input.charAt(m_curPtr) === '#') {
    362                    // if m_input is to be an array of lines then this probably wont work very well
    363                    while (
    364                        m_curPtr < m_input.length &&
    365                        !glsShaderLibrary.isEOL(m_input.charAt(m_curPtr))
    366                    ) ++m_curPtr;
    367                } else {
    368                    break;
    369                }
    370 
    371            }
    372 
    373            if (m_curPtr >= m_input.length) {
    374 
    375                m_curToken = Token.TOKEN_EOF;
    376                m_curTokenStr = '<EOF>';
    377 
    378            } else if (glsShaderLibrary.isAlpha(m_input.charAt(m_curPtr))) {
    379 
    380            /** @type {number} */ var end = m_curPtr + 1;
    381                while (glsShaderLibrary.isCaseNameChar(m_input.charAt(end))) ++end;
    382 
    383                m_curTokenStr = m_input.substr(m_curPtr, end - m_curPtr);
    384 
    385                m_curToken = (function() {
    386                    // consider reimplementing with a binary search
    387                    switch (m_curTokenStr) {
    388                        case 'true': return Token.TOKEN_TRUE;
    389                        case 'false': return Token.TOKEN_FALSE;
    390                        case 'desc': return Token.TOKEN_DESC;
    391                        case 'expect': return Token.TOKEN_EXPECT;
    392                        case 'group': return Token.TOKEN_GROUP;
    393                        case 'case': return Token.TOKEN_CASE;
    394                        case 'end': return Token.TOKEN_END;
    395                        case 'values': return Token.TOKEN_VALUES;
    396                        case 'both': return Token.TOKEN_BOTH;
    397                        case 'vertex': return Token.TOKEN_VERTEX;
    398                        case 'fragment': return Token.TOKEN_FRAGMENT;
    399                        case 'uniform': return Token.TOKEN_UNIFORM;
    400                        case 'input': return Token.TOKEN_INPUT;
    401                        case 'output': return Token.TOKEN_OUTPUT;
    402                        case 'float': return Token.TOKEN_FLOAT;
    403                        case 'vec2': return Token.TOKEN_FLOAT_VEC2;
    404                        case 'vec3': return Token.TOKEN_FLOAT_VEC3;
    405                        case 'vec4': return Token.TOKEN_FLOAT_VEC4;
    406                        case 'mat2': return Token.TOKEN_FLOAT_MAT2;
    407                        case 'mat2x3': return Token.TOKEN_FLOAT_MAT2X3;
    408                        case 'mat2x4': return Token.TOKEN_FLOAT_MAT2X4;
    409                        case 'mat3x2': return Token.TOKEN_FLOAT_MAT3X2;
    410                        case 'mat3': return Token.TOKEN_FLOAT_MAT3;
    411                        case 'mat3x4': return Token.TOKEN_FLOAT_MAT3X4;
    412                        case 'mat4x2': return Token.TOKEN_FLOAT_MAT4X2;
    413                        case 'mat4x3': return Token.TOKEN_FLOAT_MAT4X3;
    414                        case 'mat4': return Token.TOKEN_FLOAT_MAT4;
    415                        case 'int': return Token.TOKEN_INT;
    416                        case 'ivec2': return Token.TOKEN_INT_VEC2;
    417                        case 'ivec3': return Token.TOKEN_INT_VEC3;
    418                        case 'ivec4': return Token.TOKEN_INT_VEC4;
    419                        case 'uint': return Token.TOKEN_UINT;
    420                        case 'uvec2': return Token.TOKEN_UINT_VEC2;
    421                        case 'uvec3': return Token.TOKEN_UINT_VEC3;
    422                        case 'uvec4': return Token.TOKEN_UINT_VEC4;
    423                        case 'bool': return Token.TOKEN_BOOL;
    424                        case 'bvec2': return Token.TOKEN_BOOL_VEC2;
    425                        case 'bvec3': return Token.TOKEN_BOOL_VEC3;
    426                        case 'bvec4': return Token.TOKEN_BOOL_VEC4;
    427                        case 'version': return Token.TOKEN_VERSION;
    428                        default: return Token.TOKEN_IDENTIFIER;
    429                    }
    430                }());
    431 
    432            } else if (glsShaderLibrary.isNumeric(m_input.charAt(m_curPtr))) {
    433 
    434            /** @type {number} */ var p = m_curPtr;
    435                while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p;
    436 
    437                if (m_input.charAt(p) === '.') { // float
    438 
    439                    ++p;
    440                    while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p;
    441 
    442                    if (m_input.charAt(p) === 'e' || m_input.charAt(p) === 'E') {
    443 
    444                        ++p;
    445                        if (m_input.charAt(p) === '+' || m_input.charAt(p) === '-') ++p;
    446 
    447                        glsShaderLibrary.de_assert(p < m_input.length && glsShaderLibrary.isNumeric(m_input.charAt(p)));
    448                        while (glsShaderLibrary.isNumeric(m_input.charAt(p))) ++p;
    449 
    450                    }
    451 
    452                    m_curToken = Token.TOKEN_FLOAT_LITERAL;
    453                    m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr);
    454 
    455                } else {
    456 
    457                    m_curToken = Token.TOKEN_INT_LITERAL;
    458                    m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr);
    459 
    460                }
    461 
    462            } else if (m_input.charAt(m_curPtr) === '"' && m_input.charAt(m_curPtr + 1) === '"') { // shader source
    463 
    464                var p = m_curPtr + 2;
    465 
    466                while (m_input.charAt(p) != '"' || m_input.charAt(p + 1) != '"') {
    467                    glsShaderLibrary.de_assert(p < m_input.length);
    468                    if (m_input.charAt(p) === '\\') {
    469                        glsShaderLibrary.de_assert(p + 1 < m_input.length);
    470                        p += 2;
    471                    } else {
    472                        ++p;
    473                    }
    474                }
    475                p += 2;
    476 
    477                m_curToken = Token.TOKEN_SHADER_SOURCE;
    478                m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr);
    479 
    480            } else if (m_input.charAt(m_curPtr) === '"' || m_input.charAt(m_curPtr) === "'") {
    481 
    482                /** @type {string} */ var delimitor = m_input.charAt(m_curPtr);
    483                var p = m_curPtr + 1;
    484 
    485                while (m_input.charAt(p) != delimitor) {
    486 
    487                    glsShaderLibrary.de_assert(p < m_input.length);
    488                    if (m_input.charAt(p) === '\\') {
    489                        glsShaderLibrary.de_assert(p + 1 < m_input.length);
    490                        p += 2;
    491                    } else {
    492                        ++p;
    493                    }
    494 
    495                }
    496                ++p;
    497 
    498                m_curToken = Token.TOKEN_STRING;
    499                m_curTokenStr = m_input.substr(m_curPtr, p - m_curPtr);
    500 
    501            } else {
    502 
    503                m_curTokenStr = m_input.charAt(m_curPtr);
    504                m_curToken = (function() {
    505                    // consider reimplementing with a binary search
    506                    switch (m_curTokenStr) {
    507                        case '=': return Token.TOKEN_ASSIGN;
    508                        case '+': return Token.TOKEN_PLUS;
    509                        case '-': return Token.TOKEN_MINUS;
    510                        case ',': return Token.TOKEN_COMMA;
    511                        case '|': return Token.TOKEN_VERTICAL_BAR;
    512                        case ';': return Token.TOKEN_SEMI_COLON;
    513                        case '(': return Token.TOKEN_LEFT_PAREN;
    514                        case ')': return Token.TOKEN_RIGHT_PAREN;
    515                        case '[': return Token.TOKEN_LEFT_BRACKET;
    516                        case ']': return Token.TOKEN_RIGHT_BRACKET;
    517                        case '{': return Token.TOKEN_LEFT_BRACE;
    518                        case '}': return Token.TOKEN_RIGHT_BRACE;
    519 
    520                        default: return Token.TOKEN_INVALID;
    521                    }
    522                }());
    523 
    524            }
    525 
    526        };
    527 
    528        /**
    529         * @return {Object.<number, string, string>}
    530         */
    531        var advanceTokenTester = function(input, current_index) {
    532            m_input = input;
    533            m_curPtr = current_index;
    534            m_curTokenStr = '';
    535            advanceTokenWorker();
    536            return {
    537            /** @type {number} */ idType: m_curToken,
    538            /** @type {string} */ name: resolveTokenName(m_curToken),
    539            /** @type {string} */ value: m_curTokenStr
    540            };
    541        };
    542 
    543        /**
    544          * @param {Token=} tokenAssumed
    545          */
    546        var advanceToken = function(tokenAssumed) {
    547            if (typeof(tokenAssumed) !== 'undefined') {
    548                assumeToken(tokenAssumed);
    549            }
    550            advanceTokenWorker();
    551        };
    552        var assumeToken = function(token) {
    553 
    554            if (m_curToken != token) {
    555                // parse error
    556            /** @type {string} */ var msg = "unexpected token '" + m_curTokenStr + "', expecting '" + getTokenName(token) + "'";
    557                throw Error('Parse Error. ' + msg + ' near ' + m_curPtr + ' ...');
    558            }
    559        };
    560        var mapDataTypeToken = function(token) {
    561            switch (token) {
    562                case Token.TOKEN_FLOAT: return gluShaderUtil.DataType.FLOAT;
    563                case Token.TOKEN_FLOAT_VEC2: return gluShaderUtil.DataType.FLOAT_VEC2;
    564                case Token.TOKEN_FLOAT_VEC3: return gluShaderUtil.DataType.FLOAT_VEC3;
    565                case Token.TOKEN_FLOAT_VEC4: return gluShaderUtil.DataType.FLOAT_VEC4;
    566                case Token.TOKEN_FLOAT_MAT2: return gluShaderUtil.DataType.FLOAT_MAT2;
    567                case Token.TOKEN_FLOAT_MAT2X3: return gluShaderUtil.DataType.FLOAT_MAT2X3;
    568                case Token.TOKEN_FLOAT_MAT2X4: return gluShaderUtil.DataType.FLOAT_MAT2X4;
    569                case Token.TOKEN_FLOAT_MAT3X2: return gluShaderUtil.DataType.FLOAT_MAT3X2;
    570                case Token.TOKEN_FLOAT_MAT3: return gluShaderUtil.DataType.FLOAT_MAT3;
    571                case Token.TOKEN_FLOAT_MAT3X4: return gluShaderUtil.DataType.FLOAT_MAT3X4;
    572                case Token.TOKEN_FLOAT_MAT4X2: return gluShaderUtil.DataType.FLOAT_MAT4X2;
    573                case Token.TOKEN_FLOAT_MAT4X3: return gluShaderUtil.DataType.FLOAT_MAT4X3;
    574                case Token.TOKEN_FLOAT_MAT4: return gluShaderUtil.DataType.FLOAT_MAT4;
    575                case Token.TOKEN_INT: return gluShaderUtil.DataType.INT;
    576                case Token.TOKEN_INT_VEC2: return gluShaderUtil.DataType.INT_VEC2;
    577                case Token.TOKEN_INT_VEC3: return gluShaderUtil.DataType.INT_VEC3;
    578                case Token.TOKEN_INT_VEC4: return gluShaderUtil.DataType.INT_VEC4;
    579                case Token.TOKEN_UINT: return gluShaderUtil.DataType.UINT;
    580                case Token.TOKEN_UINT_VEC2: return gluShaderUtil.DataType.UINT_VEC2;
    581                case Token.TOKEN_UINT_VEC3: return gluShaderUtil.DataType.UINT_VEC3;
    582                case Token.TOKEN_UINT_VEC4: return gluShaderUtil.DataType.UINT_VEC4;
    583                case Token.TOKEN_BOOL: return gluShaderUtil.DataType.BOOL;
    584                case Token.TOKEN_BOOL_VEC2: return gluShaderUtil.DataType.BOOL_VEC2;
    585                case Token.TOKEN_BOOL_VEC3: return gluShaderUtil.DataType.BOOL_VEC3;
    586                case Token.TOKEN_BOOL_VEC4: return gluShaderUtil.DataType.BOOL_VEC4;
    587                default: return gluShaderUtil.DataType.INVALID;
    588            }
    589        };
    590 
    591        /**
    592         * Returns the corresponding token's name depending on enum number value
    593         * @param {number} token
    594         * @return {string}
    595         */
    596        var getTokenName = function(token) {
    597            switch (token) {
    598                case Token.TOKEN_INVALID: return '<invalid>';
    599                case Token.TOKEN_EOF: return '<eof>';
    600                case Token.TOKEN_STRING: return '<string>';
    601                case Token.TOKEN_SHADER_SOURCE: return 'source';
    602 
    603                case Token.TOKEN_INT_LITERAL: return '<int>';
    604                case Token.TOKEN_FLOAT_LITERAL: return '<float>';
    605 
    606                // identifiers
    607                case Token.TOKEN_IDENTIFIER: return '<identifier>';
    608                case Token.TOKEN_TRUE: return 'true';
    609                case Token.TOKEN_FALSE: return 'false';
    610                case Token.TOKEN_DESC: return 'desc';
    611                case Token.TOKEN_EXPECT: return 'expect';
    612                case Token.TOKEN_GROUP: return 'group';
    613                case Token.TOKEN_CASE: return 'case';
    614                case Token.TOKEN_END: return 'end';
    615                case Token.TOKEN_VALUES: return 'values';
    616                case Token.TOKEN_BOTH: return 'both';
    617                case Token.TOKEN_VERTEX: return 'vertex';
    618                case Token.TOKEN_FRAGMENT: return 'fragment';
    619                case Token.TOKEN_UNIFORM: return 'uniform';
    620                case Token.TOKEN_INPUT: return 'input';
    621                case Token.TOKEN_OUTPUT: return 'output';
    622                case Token.TOKEN_FLOAT: return 'float';
    623                case Token.TOKEN_FLOAT_VEC2: return 'vec2';
    624                case Token.TOKEN_FLOAT_VEC3: return 'vec3';
    625                case Token.TOKEN_FLOAT_VEC4: return 'vec4';
    626                case Token.TOKEN_FLOAT_MAT2: return 'mat2';
    627                case Token.TOKEN_FLOAT_MAT2X3: return 'mat2x3';
    628                case Token.TOKEN_FLOAT_MAT2X4: return 'mat2x4';
    629                case Token.TOKEN_FLOAT_MAT3X2: return 'mat3x2';
    630                case Token.TOKEN_FLOAT_MAT3: return 'mat3';
    631                case Token.TOKEN_FLOAT_MAT3X4: return 'mat3x4';
    632                case Token.TOKEN_FLOAT_MAT4X2: return 'mat4x2';
    633                case Token.TOKEN_FLOAT_MAT4X3: return 'mat4x3';
    634                case Token.TOKEN_FLOAT_MAT4: return 'mat4';
    635                case Token.TOKEN_INT: return 'int';
    636                case Token.TOKEN_INT_VEC2: return 'ivec2';
    637                case Token.TOKEN_INT_VEC3: return 'ivec3';
    638                case Token.TOKEN_INT_VEC4: return 'ivec4';
    639                case Token.TOKEN_UINT: return 'uint';
    640                case Token.TOKEN_UINT_VEC2: return 'uvec2';
    641                case Token.TOKEN_UINT_VEC3: return 'uvec3';
    642                case Token.TOKEN_UINT_VEC4: return 'uvec4';
    643                case Token.TOKEN_BOOL: return 'bool';
    644                case Token.TOKEN_BOOL_VEC2: return 'bvec2';
    645                case Token.TOKEN_BOOL_VEC3: return 'bvec3';
    646                case Token.TOKEN_BOOL_VEC4: return 'bvec4';
    647 
    648                case Token.TOKEN_ASSIGN: return '=';
    649                case Token.TOKEN_PLUS: return '+';
    650                case Token.TOKEN_MINUS: return '-';
    651                case Token.TOKEN_COMMA: return ',';
    652                case Token.TOKEN_VERTICAL_BAR: return '|';
    653                case Token.TOKEN_SEMI_COLON: return ';';
    654                case Token.TOKEN_LEFT_PAREN: return '(';
    655                case Token.TOKEN_RIGHT_PAREN: return ')';
    656                case Token.TOKEN_LEFT_BRACKET: return '[';
    657                case Token.TOKEN_RIGHT_BRACKET: return ']';
    658                case Token.TOKEN_LEFT_BRACE: return ' {';
    659                case Token.TOKEN_RIGHT_BRACE: return '}';
    660 
    661                default: return '<unknown>';
    662            }
    663        };
    664 
    665        /**
    666         * @param {?gluShaderUtil.DataType} expectedDataType
    667         * @param {Object} result
    668         */
    669        var parseValueElement = function(expectedDataType, result) {
    670            /** @type {?string} */ var scalarType = null;
    671            /** @type {number} */ var scalarSize = 0;
    672            if (expectedDataType) {
    673                scalarType = gluShaderUtil.getDataTypeScalarType(expectedDataType);
    674                scalarSize = gluShaderUtil.getDataTypeScalarSize(expectedDataType);
    675            }
    676 
    677            /** @type {Array<number>} */ var elems = [];
    678 
    679            if (scalarSize > 1) {
    680                glsShaderLibrary.de_assert(mapDataTypeToken(m_curToken) === expectedDataType);
    681                advanceToken(); // data type(float, vec2, etc.)
    682                advanceToken(Token.TOKEN_LEFT_PAREN);
    683            }
    684 
    685            for (var i = 0; i < scalarSize; ++i) {
    686                if (scalarType === 'float') {
    687 
    688                /** @type {number} */ var signMult = 1.0;
    689                    if (m_curToken === Token.TOKEN_MINUS) {
    690                        signMult = -1.0;
    691                        advanceToken();
    692                    }
    693 
    694                    assumeToken(Token.TOKEN_FLOAT_LITERAL);
    695                    elems.push(signMult * parseFloatLiteral(m_curTokenStr));
    696                    advanceToken(Token.TOKEN_FLOAT_LITERAL);
    697 
    698                } else if (scalarType === 'int' || scalarType === 'uint') {
    699 
    700                    var signMult = 1;
    701                    if (m_curToken === Token.TOKEN_MINUS) {
    702                        signMult = -1;
    703                        advanceToken();
    704                    }
    705 
    706                    assumeToken(Token.TOKEN_INT_LITERAL);
    707                    elems.push(signMult * parseIntLiteral(m_curTokenStr));
    708                    advanceToken(Token.TOKEN_INT_LITERAL);
    709 
    710                } else {
    711 
    712                    glsShaderLibrary.de_assert(scalarType === 'bool');
    713                    elems.push(m_curToken === Token.TOKEN_TRUE);
    714                    if (m_curToken != Token.TOKEN_TRUE && m_curToken != Token.TOKEN_FALSE) {
    715                        throw Error('unexpected token, expecting bool: ' + m_curTokenStr);
    716                    }
    717                    advanceToken(); // true/false
    718 
    719                }
    720 
    721                if (i != (scalarSize - 1)) {
    722                    advanceToken(Token.TOKEN_COMMA);
    723                }
    724            }
    725 
    726            if (scalarSize > 1) {
    727                advanceToken(Token.TOKEN_RIGHT_PAREN);
    728            }
    729 
    730            for (var i = 0; i < elems.length; i++)
    731                result.elements.push(elems[i]);
    732 
    733        };
    734 
    735        /**
    736         * @param {Object.<Array, number>} valueBlock
    737         */
    738        var parseValue = function(valueBlock) {
    739 
    740        /**
    741        * @type {Object}
    742        */
    743            var result = {
    744            /** @type {?gluShaderUtil.DataType} */ dataType: null,
    745            /** @type {?glsShaderLibraryCase.shaderCase} */ storageType: null,
    746            /** @type {?string} */ valueName: null,
    747            /** @type {Array} */ elements: []
    748            };
    749 
    750            // parse storage
    751            switch (m_curToken) {
    752             case Token.TOKEN_UNIFORM:
    753                result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_UNIFORM;
    754                break;
    755             case Token.TOKEN_INPUT:
    756                result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_INPUT;
    757                break;
    758             case Token.TOKEN_OUTPUT:
    759                result.storageType = glsShaderLibraryCase.shaderCase.STORAGE_OUTPUT;
    760                break;
    761             default:
    762                throw Error('unexpected token encountered when parsing value classifier');
    763                break;
    764            }
    765            advanceToken();
    766 
    767            // parse data type
    768            result.dataType = mapDataTypeToken(m_curToken);
    769            if (result.dataType === gluShaderUtil.DataType.INVALID) {
    770                throw Error('unexpected token when parsing value data type: ' + m_curTokenStr);
    771            }
    772            advanceToken();
    773 
    774            // parse value name
    775            if (m_curToken === Token.TOKEN_IDENTIFIER) {
    776                result.valueName = m_curTokenStr;
    777            } else if (m_curToken === Token.TOKEN_STRING) {
    778                result.valueName = parseStringLiteral(m_curTokenStr);
    779            } else {
    780                throw Error('unexpected token when parsing value name: ' + m_curTokenStr);
    781            }
    782            advanceToken();
    783 
    784            // parse assignment operator.
    785            advanceToken(Token.TOKEN_ASSIGN);
    786 
    787            // parse actual value
    788            if (m_curToken === Token.TOKEN_LEFT_BRACKET) { // value list
    789                advanceToken(Token.TOKEN_LEFT_BRACKET);
    790                result.arrayLength = 0;
    791 
    792                for (;;) {
    793                    parseValueElement(result.dataType, result);
    794                    result.arrayLength += 1;
    795 
    796                    if (m_curToken === Token.TOKEN_RIGHT_BRACKET) {
    797                        break;
    798                    } else if (m_curToken === Token.TOKEN_VERTICAL_BAR) { // pipe?
    799                        advanceToken();
    800                        continue;
    801                    } else {
    802                        throw Error('unexpected token in value element array: ' + m_curTokenStr);
    803                    }
    804                }
    805 
    806                advanceToken(Token.TOKEN_RIGHT_BRACKET);
    807 
    808            } else { // arrays, single elements
    809                parseValueElement(result.dataType, result);
    810                result.arrayLength = 1;
    811            }
    812 
    813            advanceToken(Token.TOKEN_SEMI_COLON);
    814 
    815            valueBlock.values.push(result);
    816 
    817        };
    818 
    819        /**
    820         * @param {Object.<Array, number>} valueBlock
    821         */
    822        var parseValueBlock = function(valueBlock) {
    823 
    824            advanceToken(Token.TOKEN_VALUES);
    825            advanceToken(Token.TOKEN_LEFT_BRACE);
    826 
    827            for (;;) {
    828                if (
    829                    m_curToken === Token.TOKEN_UNIFORM ||
    830                    m_curToken === Token.TOKEN_INPUT ||
    831                    m_curToken === Token.TOKEN_OUTPUT
    832                ) {
    833                    parseValue(valueBlock);
    834                } else if (m_curToken === Token.TOKEN_RIGHT_BRACE) {
    835                    break;
    836                } else {
    837                    throw Error('unexpected( token when parsing a value block: ' + m_curTokenStr);
    838                }
    839            }
    840 
    841            advanceToken(Token.TOKEN_RIGHT_BRACE);
    842 
    843            /** @type {number} */ var arrayLength = 1;
    844            // compute combined array length of value block.
    845            for (var i = 0; i < valueBlock.values.length; ++i) {
    846                if (valueBlock.values[i].arrayLength > 1) {
    847                    glsShaderLibrary.de_assert(arrayLength === 1 || arrayLength === valueBlock.values[i].arrayLength);
    848                    arrayLength = valueBlock.values[i].arrayLength;
    849                }
    850            }
    851 
    852            valueBlock.arrayLength = arrayLength;
    853 
    854        };
    855 
    856        /**
    857         * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList
    858         */
    859        var parseShaderCase = function(shaderNodeList) {
    860 
    861            // parse case
    862            advanceToken(Token.TOKEN_CASE);
    863 
    864            /**
    865             * @type {string}
    866             * parse case name
    867             */
    868            var caseName = m_curTokenStr;
    869            advanceToken(); // \note [pyry] All token types are allowed here.
    870 
    871            /**
    872             * @type {Array<Object>}
    873             * setup case
    874             */
    875            var valueBlockList = [];
    876 
    877            /** TODO: Should the default version be defined elsewhere? */
    878            /** @type {string} */ var version = '100';
    879            /** @type {number} */ var expectResult = glsShaderLibraryCase.expectResult.EXPECT_PASS;
    880            /** @type {string} */ var description;
    881            /** @type {string} */ var bothSource = '';
    882            /** @type {string} */ var vertexSource = '';
    883            /** @type {string} */ var fragmentSource = '';
    884 
    885            for (;;) {
    886 
    887                if (m_curToken === Token.TOKEN_END) {
    888 
    889                    break;
    890 
    891                } else if (m_curToken === Token.TOKEN_DESC) {
    892 
    893                    advanceToken();
    894                    assumeToken(Token.TOKEN_STRING);
    895 
    896                    description = parseStringLiteral(m_curTokenStr);
    897                    advanceToken();
    898 
    899                } else if (m_curToken === Token.TOKEN_EXPECT) {
    900 
    901                    advanceToken();
    902                    assumeToken(Token.TOKEN_IDENTIFIER);
    903 
    904                    expectResult = (function(token) {
    905                        switch (token) {
    906                            case 'pass': return glsShaderLibraryCase.expectResult.EXPECT_PASS;
    907                            case 'compile_fail': return glsShaderLibraryCase.expectResult.EXPECT_COMPILE_FAIL;
    908                            case 'link_fail': return glsShaderLibraryCase.expectResult.EXPECT_LINK_FAIL;
    909                            case 'compile_or_link_fail': return glsShaderLibraryCase.expectResult.EXPECT_COMPILE_LINK_FAIL;
    910                            case 'build_successful': return glsShaderLibraryCase.expectResult.EXPECT_BUILD_SUCCESSFUL;
    911                            default:
    912                                throw Error('invalid expected result value: ' + m_curTokenStr);
    913                        }
    914                    }(m_curTokenStr));
    915 
    916                    advanceToken();
    917 
    918                } else if (m_curToken === Token.TOKEN_VALUES) {
    919 
    920                /** @type {Object.<Array, number>} */ var block = glsShaderLibraryCase.genValueBlock();
    921                    parseValueBlock(block);
    922                    valueBlockList.push(block);
    923 
    924                } else if (
    925                    m_curToken === Token.TOKEN_BOTH ||
    926                    m_curToken === Token.TOKEN_VERTEX ||
    927                    m_curToken === Token.TOKEN_FRAGMENT
    928                ) {
    929 
    930                /** @type {number} */ var token = m_curToken;
    931                    advanceToken();
    932                    assumeToken(Token.TOKEN_SHADER_SOURCE);
    933                /** @type {string} */ var source = parseShaderSource(m_curTokenStr);
    934 
    935                    advanceToken();
    936                    switch (token) {
    937                        case Token.TOKEN_BOTH: bothSource = source; break;
    938                        case Token.TOKEN_VERTEX: vertexSource = source; break;
    939                        case Token.TOKEN_FRAGMENT: fragmentSource = source; break;
    940                        default: glsShaderLibrary.de_assert(false); break;
    941                    }
    942 
    943                } else if (m_curToken === Token.TOKEN_VERSION) {
    944 
    945                    advanceToken();
    946 
    947                    /** @type {number} */ var versionNum = 0;
    948                    /** @type {string} */ var postfix = '';
    949 
    950                    assumeToken(Token.TOKEN_INT_LITERAL);
    951                    versionNum = parseIntLiteral(m_curTokenStr);
    952                    advanceToken();
    953 
    954                    if (m_curToken === Token.TOKEN_IDENTIFIER) {
    955                        postfix = m_curTokenStr;
    956                        advanceToken();
    957                    }
    958 
    959                    // TODO: need to fix these constants, we dont have glu
    960                    if (versionNum === 100 && postfix === 'es') version = '100';
    961                    else if (versionNum === 300 && postfix === 'es') version = '300 es';
    962                    else if (versionNum === 310 && postfix === 'es') version = '310 es';
    963                    else if (versionNum === 130) version = '130';
    964                    else if (versionNum === 140) version = '140';
    965                    else if (versionNum === 150) version = '150';
    966                    else if (versionNum === 330) version = '330';
    967                    else if (versionNum === 400) version = '400';
    968                    else if (versionNum === 410) version = '410';
    969                    else if (versionNum === 420) version = '420';
    970                    else if (versionNum === 430) version = '430';
    971                    else if (versionNum === 440) version = '440';
    972                    else if (versionNum === 450) version = '450';
    973                    else {
    974                        throw Error('Unknown GLSL version');
    975                    }
    976 
    977                } else {
    978                    throw Error('unexpected token while parsing shader case: ' + m_curTokenStr);
    979                }
    980 
    981            }
    982 
    983            advanceToken(Token.TOKEN_END); // case end
    984 
    985            /**
    986             * no ShaderCase yet?
    987             * @param {?string} vert
    988             * @param {?string} frag
    989             * @param {glsShaderLibraryCase.caseType} type
    990             * @return {Object}
    991             */
    992            var getShaderSpec = function(vert, frag, type) {
    993                return {
    994                /** @type {glsShaderLibraryCase.expectResult} */ expectResult: expectResult,
    995                /** @type {glsShaderLibraryCase.caseType} */ caseType: type,
    996                /** @type {Array<Object>} */ valueBlockList: valueBlockList,
    997                /** @type {string} */ targetVersion: version,
    998                /** @type {?string} */ vertexSource: vert,
    999                /** @type {?string} */ fragmentSource: frag
   1000                };
   1001            };
   1002            getShaderSpec.bind(this);
   1003 
   1004            if (bothSource.length) {
   1005 
   1006                glsShaderLibrary.de_assert(!vertexSource);
   1007                glsShaderLibrary.de_assert(!fragmentSource);
   1008 
   1009                shaderNodeList.push(tcuTestCase.newTest(caseName + '_vertex', description, getShaderSpec(bothSource, null,
   1010                    glsShaderLibraryCase.caseType.CASETYPE_VERTEX_ONLY)));
   1011                shaderNodeList.push(tcuTestCase.newTest(caseName + '_fragment', description, getShaderSpec(null, bothSource,
   1012                    glsShaderLibraryCase.caseType.CASETYPE_FRAGMENT_ONLY)));
   1013 
   1014            } else {
   1015                glsShaderLibrary.de_assert(vertexSource);
   1016                glsShaderLibrary.de_assert(fragmentSource);
   1017 
   1018                shaderNodeList.push(tcuTestCase.newTest(caseName, description, getShaderSpec(vertexSource, fragmentSource,
   1019                    glsShaderLibraryCase.caseType.CASETYPE_COMPLETE)));
   1020                }
   1021        };
   1022 
   1023        /**
   1024         * @param {Array<tcuTestCase.DeqpTest>} shaderNodeList
   1025         */
   1026        var parseShaderGroup = function(shaderNodeList) {
   1027 
   1028            // parse 'case'
   1029            advanceToken(Token.TOKEN_GROUP);
   1030 
   1031            /** @type {string}
   1032             * parse case name
   1033             */ var name = m_curTokenStr;
   1034            advanceToken(); // \note [pyry] We don't want to check token type here (for instance to allow "uniform") group.
   1035 
   1036            // Parse description.
   1037            assumeToken(Token.TOKEN_STRING);
   1038            /** @type {string} */ var description = parseStringLiteral(m_curTokenStr);
   1039            advanceToken(Token.TOKEN_STRING);
   1040 
   1041            /** @type {Array<tcuTestCase.DeqpTest>} */ var children = [];
   1042 
   1043            for (;;) {
   1044 
   1045                if (m_curToken === Token.TOKEN_END) {
   1046                    break;
   1047                } else if (m_curToken === Token.TOKEN_GROUP) {
   1048                    parseShaderGroup(children);
   1049                } else if (m_curToken === Token.TOKEN_CASE) {
   1050                    parseShaderCase(children);
   1051                } else {
   1052                    testFailed('unexpected token while parsing shader group: ' + m_curTokenStr);
   1053                    tcuTestCase.runner.terminate();
   1054                }
   1055 
   1056            }
   1057 
   1058            advanceToken(Token.TOKEN_END); // group end
   1059 
   1060            /** @type {tcuTestCase.DeqpTest} */ var groupNode = tcuTestCase.newTest(name, description, null);
   1061            groupNode.setChildren(children);
   1062 
   1063            shaderNodeList.push(groupNode);
   1064 
   1065        };
   1066 
   1067        // uncomment to expose private functions
   1068        (function(obj) {
   1069            obj.priv = {
   1070                m_curPtr: m_curPtr,
   1071 
   1072                parseError: parseError,
   1073                parseFloatLiteral: parseFloatLiteral,
   1074                parseIntLiteral: parseIntLiteral,
   1075                parseStringLiteral: parseStringLiteral,
   1076                parseShaderSource: parseShaderSource,
   1077                advanceTokenTester: advanceTokenTester,
   1078                assumeToken: assumeToken,
   1079                mapDataTypeToken: mapDataTypeToken,
   1080                getTokenName: getTokenName,
   1081 
   1082                Token: Token,
   1083 
   1084                parseValueElement: parseValueElement,
   1085                parseValue: parseValue,
   1086                parseValueBlock: parseValueBlock,
   1087                parseShaderCase: parseShaderCase,
   1088                parseShaderGroup: parseShaderGroup,
   1089 
   1090                none: false
   1091            };
   1092        }(this));
   1093        //*/
   1094    };
   1095 
   1096 /**
   1097 * Parse the test file and execute the test cases
   1098 * @param {string} testName Name of the test file (without extension)
   1099 * @param {string} filter Optional filter. Common substring of the names of the tests that should be glsShaderLibrary.run.
   1100 */
   1101 glsShaderLibrary.run = function(testName, filter) {
   1102    WebGLTestUtils.loadTextFileAsync(testName + '.test', function(success, content) {
   1103        if (success) {
   1104            tcuTestCase.runner.testFile = content;
   1105            tcuTestCase.runner.testName = testName;
   1106            tcuTestCase.runner.runCallback(glsShaderLibrary.processTestFile);
   1107        } else {
   1108            testFailed('Failed to load test file: ' + testName);
   1109            tcuTestCase.runner.terminate();
   1110        }
   1111    });
   1112 };
   1113 
   1114 });