Source code for fsleyes.gl.shaders.glsl.parse

#
# parse.py - Simple parser for extracting information about a GLSL program.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
# Based on work by Nicolas P. Rougier
# <https://github.com/rougier/glsl-parser>, which is released under the New
# BSD license.
#
# Copyright (c) 2014, Nicolas P. Rougier
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
#    contributors may be used to endorse or promote products derived from this
#    software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
"""This module implements a simple GLSL parser, for extracting information
about a GLSL program.

 .. note:: The code in this module is based on work by Nicolas P. Rougier
           (https://github.com/rougier/glsl-parser), which is released under
           the New BSD license.

The main entry point to this module is the :func:`parseGLSL` function which,
given the source code of a GLSL program, parses it and returns information
about the program.
"""


from __future__ import print_function

import sys
import logging

import pyparsing as pp

import fsl.utils.memoize as memoize


log = logging.getLogger(__name__)


KEYWORDS  = ('attribute const uniform varying break continue do for while '
             'if else '
             'in out inout '
             'float int void bool true false '
             'lowp mediump highp precision invariant  '
             'discard return '
             'mat2 mat3 mat4 '
             'vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 sampler2D '
             'samplerCube '
             'struct')
RESERVED  = ('asm '
             'class union enum typedef template this packed '
             'goto switch default '
             'inline noinline volatile public static extern external '
             'interface flat long short double half fixed unsigned superp '
             'input output '
             'hvec2 hvec3 hvec4 dvec2 dvec3 dvec4 fvec2 fvec3 fvec4 sampler1D '
             'sampler3D '
             'sampler1DShadow sampler2DShadow '
             'sampler2DRect sampler3DRect sampler2DRectShadow '
             'sizeof cast '
             'namespace using ')
PRECISION = 'lowp mediump high'
STORAGE   = 'const uniform attribute varying'


# Tokens
# ----------------------------------
LPAREN     = pp.Literal("(").suppress()
RPAREN     = pp.Literal(")").suppress()
LBRACK     = pp.Literal("[").suppress()
RBRACK     = pp.Literal("]").suppress()
LBRACE     = pp.Literal("{").suppress()
RBRACE     = pp.Literal("}").suppress()
IDENTIFIER = pp.Word(pp.alphas + '_', pp.alphanums + '_')
TYPE       = pp.Word(pp.alphas + '_', pp.alphanums + "_")
END        = pp.Literal(";").suppress()
INT        = pp.Word(pp.nums)
FLOAT      = pp.Regex(r'[+-]?(((\d+\.\d*)|(\d*\.\d+))'
                      r'([eE][-+]?\d+)?)|(\d*[eE][+-]?\d+)')
STORAGE    = pp.Regex('|'.join(STORAGE.split(' ')))
PRECISION  = pp.Regex('|'.join(PRECISION.split(' ')))
STRUCT     = pp.Literal("struct").suppress()


[docs]def getDeclarations(code): """Get all declarations prefixed with a storage qualifier. *Code example* :: uniform lowp vec4 fg_color = vec4(1), bg_color = vec4(vec3(0),1); """ # Callable expression EXPRESSION = pp.Forward() ARG = pp.Group(EXPRESSION) | IDENTIFIER | FLOAT | INT ARGS = pp.delimitedList(ARG) EXPRESSION << IDENTIFIER + pp.Group(LPAREN + pp.Optional(ARGS) + RPAREN) # Value VALUE = (EXPRESSION | pp.Word(pp.alphanums + '_()+-/*')).setParseAction( pp.originalTextFor) # Single declaration VARIABLE = (IDENTIFIER.setResultsName('name') + pp.Optional(LBRACK + (INT | IDENTIFIER).setResultsName('size') + RBRACK) + pp.Optional(pp.Literal("=").suppress() + VALUE.setResultsName('value'))) # Several declarations at once DECLARATION = (STORAGE.setResultsName('storage') + pp.Optional(PRECISION).setResultsName('precision') + TYPE.setResultsName('type') + pp.delimitedList( VARIABLE.setResultsName('variable', listAllMatches=True)) + END) DECLARATION.ignore(pp.cStyleComment) decs = {'uniform' : [], 'varying' : [], 'attribute' : []} for (tokens, start, end) in DECLARATION.scanString(code): for token in tokens.variable: if tokens.storage not in decs: log.debug('Skipping declaration with unknown storage ' 'qualifier: {}'.format(tokens.storage)) continue size = tokens.size.strip() if size == '': size = 1 else: size = int(size) decs[tokens.storage].append((tokens.name, tokens.type, size)) return decs
@memoize.memoizeMD5 def parseGLSL(source): """Parses the given GLSL source, and returns: - The attribute declarations. """ decs = getDeclarations(source) return decs
[docs]def main(): """If this module is executed as a script, this function is called. It expects a path to a ``glsl`` file as a single parameter. This file is parsed, and information about it printed to standard output. """ if len(sys.argv) != 2: print('Usage: {}.py file.glsl'.format(__name__)) sys.exit(0) infile = sys.argv[1] print('File: {}'.format(infile)) with open(infile, 'rt') as f: code = f.read() decs = parseGLSL(code) for d, v in decs.items(): print('\n--{}--\n'.format(d.upper())) for t, n, s in v: print('{}: {} [{}]'.format(t, n, s))
if __name__ == '__main__': main()