2016-09-26 73 views
2

我试图更新这个glsl-parser,它使用旧的pyparsing版本和python2.x到python3.x &最新的pyparsing版本(2.1.9 atm)。glsl解析器使用pyparsing给AttributeErrors

我不知道使用原来的源代码pyparsing版本是但阅读pyparsing news我已经看到了这个评论Removed keepOriginalText helper method, which was deprecated ages ago. Superceded by originalTextFor.

反正以后一定是很老,因为仍在使用keepOriginalText helper方法,这里的使用python3.5.1 & pyparsing == 2.1.9端口的第一次尝试:

# -*- coding: utf-8 -*- 
# ----------------------------------------------------------------------------- 
# Copyright (c) 2014, Nicolas P. Rougier 
# Distributed under the (new) BSD License. See LICENSE.txt for more info. 
# ----------------------------------------------------------------------------- 
import pyparsing 

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 = pyparsing.Literal("(").suppress() 
RPAREN = pyparsing.Literal(")").suppress() 
LBRACK = pyparsing.Literal("[").suppress() 
RBRACK = pyparsing.Literal("]").suppress() 
LBRACE = pyparsing.Literal("{").suppress() 
RBRACE = pyparsing.Literal("}").suppress() 
IDENTIFIER = pyparsing.Word(pyparsing.alphas + '_', pyparsing.alphanums + '_') 
TYPE = pyparsing.Word(pyparsing.alphas + '_', pyparsing.alphanums + "_") 
END = pyparsing.Literal(";").suppress() 
INT = pyparsing.Word(pyparsing.nums) 
FLOAT = pyparsing.Regex(
    '[+-]?(((\d+\.\d*)|(\d*\.\d+))([eE][-+]?\d+)?)|(\d*[eE][+-]?\d+)') 
STORAGE = pyparsing.Regex('|'.join(storage.split(' '))) 
PRECISION = pyparsing.Regex('|'.join(precision.split(' '))) 
STRUCT = pyparsing.Literal("struct").suppress() 


# ------------------------ 
def get_prototypes(code): 
    """ 
    Get all function declarations 

    Code example 
    ------------ 

    mediump vec3 function_1(vec4); 
    vec3 function_2(float a, float b); 
    """ 

    PARAMETER = pyparsing.Group(pyparsing.Optional(PRECISION).setResultsName("precision") + 
           TYPE.setResultsName("type") + 
           pyparsing.Optional(IDENTIFIER).setResultsName("name")) 
    PARAMETERS = pyparsing.delimitedList(PARAMETER).setResultsName(
     "arg", listAllMatches=True) 
    PROTOTYPE = (pyparsing.Optional(PRECISION).setResultsName("precision") + 
       TYPE.setResultsName("type") + 
       IDENTIFIER.setResultsName("name") + 
       LPAREN + pyparsing.Optional(PARAMETERS).setResultsName("args") + RPAREN + 
       END) 
    PROTOTYPE.ignore(pyparsing.cStyleComment) 

    for (token, start, end) in PROTOTYPE.scanString(code): 
     print(token.precision, token.type, token.name, '(',) 
     for arg in token.args: 
      print(arg.precision, arg.type, arg.name, ',',) 
     print(')') 


# ------------------------ 
def get_functions(code): 
    """ 
    Get all function definitions 

    Code example 
    ------------ 

    mediump vec3 compute_normal(vec4); 
    """ 

    PARAMETER = pyparsing.Group(pyparsing.Optional(PRECISION).setResultsName("precision") + 
           TYPE.setResultsName("type") + 
           pyparsing.Optional(IDENTIFIER).setResultsName("name")) 
    PARAMETERS = pyparsing.delimitedList(PARAMETER).setResultsName(
     "arg", listAllMatches=True) 
    FUNCTION = (pyparsing.Optional(PRECISION).setResultsName("precision") + 
       TYPE.setResultsName("type") + 
       IDENTIFIER.setResultsName("name") + 
       LPAREN + pyparsing.Optional(PARAMETERS).setResultsName("args") + RPAREN + 
       pyparsing.nestedExpr("{", "}").setParseAction(pyparsing.originalTextFor).setResultsName("code")) 
    FUNCTION.ignore(pyparsing.cStyleComment) 

    for (token, start, end) in FUNCTION.scanString(code): 
     print(token.precision, token.type, token.name, '(',) 
     for arg in token.args: 
      print(arg.precision, arg.type, arg.name, ',',) 
     print(') { ... }') 

     # print token.code 
     # print code[start:end] 


# ------------------------ 
def get_version(code): 
    """ 
    Get shader version (if specified) 

    Code example 
    ------------ 

    #version 120 
    """ 

    VERSION = (
     pyparsing.Literal("#") + pyparsing.Keyword("version")).suppress() + INT 
    for (token, start, end) in VERSION.scanString(code): 
     version = token[0] 
     # print code[start:end] 
    return version 

# ------------------------ 


def get_declarations(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 = pyparsing.Forward() 
    ARG = pyparsing.Group(EXPRESSION) | IDENTIFIER | FLOAT | INT 
    ARGS = pyparsing.delimitedList(ARG) 
    EXPRESSION << IDENTIFIER + \ 
     pyparsing.Group(LPAREN + pyparsing.Optional(ARGS) + RPAREN) 

    # Value 
    VALUE = (EXPRESSION | pyparsing.Word(pyparsing.alphanums + "_()+-/*") 
      ).setParseAction(pyparsing.originalTextFor) 

    # Single declaration 
    VARIABLE = (IDENTIFIER.setResultsName("name") + 
       pyparsing.Optional(LBRACK + 
            (INT | IDENTIFIER).setResultsName("size") 
            + RBRACK) + 
       pyparsing.Optional(pyparsing.Literal("=").suppress() + VALUE.setResultsName("value"))) 

    # Several declarations at once 
    DECLARATION = (STORAGE.setResultsName("storage") + 
        pyparsing.Optional(PRECISION).setResultsName("precision") + 
        TYPE.setResultsName("type") + 
        pyparsing.delimitedList(VARIABLE.setResultsName("variable", listAllMatches=True)) + 
        END) 
    DECLARATION.ignore(pyparsing.cStyleComment) 

    for (tokens, start, end) in DECLARATION.scanString(code): 
     for token in tokens.variable: 
      print(tokens.storage, tokens.precision, tokens.type,) 
      print(token.name, token.size) 


# ------------------------ 
def get_definitions(code): 
    """ 
    Get all structure definitions and associated declarations. 

    Code example 
    ------------ 

    uniform struct Light { 
     vec4 position; 
     vec3 color; 
    } light0, light1; 
    """ 

    # Single declaration 
    DECLARATION = pyparsing.Group(IDENTIFIER.setResultsName("name") + 
            pyparsing.Optional(LBRACK + 
                (INT | IDENTIFIER).setResultsName("size") + 
                RBRACK)) 
    # Several declarations at once 
    DECLARATIONS = (pyparsing.Optional(PRECISION) + 
        TYPE + 
        pyparsing.delimitedList(DECLARATION) + 
        END) 

    # Definition + declarations 
    DEFINITION = (STRUCT + 
        IDENTIFIER.setResultsName("name") + 
        LBRACE + pyparsing.OneOrMore(DECLARATIONS).setResultsName('content') + RBRACE + 
        pyparsing.Optional(pyparsing.delimitedList(DECLARATION.setResultsName("declarations", listAllMatches=True))) + 
        END) 
    DEFINITION.ignore(pyparsing.cStyleComment) 

    for (tokens, start, end) in DEFINITION.scanString(code): 
     for token in tokens.declarations: 
      print(tokens.name, token.name) 
      # print tokens.content 


# ---------------- 
def resolve(code): 
    """ 
    Expand const and preprocessor definitions in order to get constant values. 

    Return the transformed code 
    """ 

    constants = {} 

    DEFINITION = (pyparsing.Literal("#") + pyparsing.Literal("define") + 
        IDENTIFIER.setResultsName("name") + 
        pyparsing.restOfLine.setResultsName("value")) 

    VALUE = pyparsing.Word(pyparsing.alphanums + "_()+-/*") 
    DECLARATION = (pyparsing.Literal("const") + 
        TYPE.setResultsName("type") + 
        IDENTIFIER.setResultsName("name") + 
        pyparsing.Literal("=") + 
        VALUE.setResultsName("value") + 
        pyparsing.Literal(";")) 
    REFERENCE = pyparsing.Forward() 

    def process_definition(s, l, t): 
     value = REFERENCE.transformString(t.value) 
     constants[t.name] = value 
     REFERENCE << pyparsing.MatchFirst(
      map(pyparsing.Keyword, constants.keys())) 
     return "#define " + t.name + " " + value 

    def process_declaration(s, l, t): 
     value = REFERENCE.transformString(t.value) 
     constants[t.name] = value 
     REFERENCE << pyparsing.MatchFirst(
      map(pyparsing.Keyword, constants.keys())) 
     return "const " + t.type + " " + t.name + "=" + value + ";" 

    def process_reference(s, l, t): 
     return constants[t[0]] 

    REFERENCE.setParseAction(process_reference) 
    DEFINITION.setParseAction(process_definition) 
    DECLARATION.setParseAction(process_declaration) 
    EXPANDER = REFERENCE | DEFINITION | DECLARATION 

    code = EXPANDER.transformString(code) 
    for key, val in constants.items(): 
     constants[key] = eval(val) 

    return code, constants 


# ----------------------------------------------------------------------------- 
if __name__ == '__main__': 

    code = """ 
#version 120 

#define A (1) 
const int B=(A+2); 
#define C (B+3) 
const int D=C+4; 

uniform float array[D]; 

struct Point { 
    vec4 position; 
    float size; 
}; 

uniform struct Light { 
    vec4 position; 
    vec3 color; 
} light0, light1; 

const float PI = 3.14159265358979323846264; 
const float SQRT_2 = 1.4142135623730951; 

uniform vec4 fg_color = vec4(1), 
      bg_color = vec4(vec3(0),1); 

mediump vec3 compute_normal(vec4 position, vec3 orientation); 
vec3 /* */ compute_light(vec4, vec3, float intensity) 
{ 
    vec3 hello; 
    vec3 hello; 
} 

""" 
code, _ = resolve(code) 
print("GLSL version: %s\n" % get_version(code)) 

get_definitions(code) 
get_declarations(code) 
get_prototypes(code) 
get_functions(code) 

# code = """ 
# #if A 
# #if B 
# #if C 
# #endif 
# #endif 
# #endif 
# """ 

# IF = (pyparsing.Literal('#') + (pyparsing.Keyword('if') | pyparsing.Keyword('ifdef') | pyparsing.Keyword('ifndef'))) 
# ENDIF = (pyparsing.Literal('#') + pyparsing.Keyword('endif')) 
# MACRO = (IF + pyparsing.restOfLine() + 
#   SkipTo(ENDIF, include=True)).setParseAction(pyparsing.originalTextFor) 
# for (tokens, start, end) in MACRO.scanString(code): 
#  print tokens 

当您尝试运行上面的代码MCVE你会得到:

GLSL version: 120 

('Light', 'light0') 
('Light', 'light1') 
d:\virtual_envs\py2711\lib\site-packages\pyparsing.py:3536: SyntaxWarning: Cannot combine element of type <type 'int'> with ParserElement 
    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") 
d:\virtual_envs\py2711\lib\site-packages\pyparsing.py:3536: SyntaxWarning: Cannot combine element of type <type 'NoneType'> with ParserElement 
    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") 
Traceback (most recent call last): 
    File "D:\sources\personal\python\pyqt\pyshaders\gui\glsl-parser.py", line 311, in <module> 
    get_declarations(code) 
    File "D:\sources\personal\python\pyqt\pyshaders\gui\glsl-parser.py", line 173, in get_declarations 
    for (tokens, start, end) in DECLARATION.scanString(code): 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 1258, in scanString 
    nextLoc,tokens = parseFn(instring, preloc, callPreParse=False) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 1084, in _parseNoCache 
    loc,tokens = self.parseImpl(instring, preloc, doActions) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 2576, in parseImpl 
    loc, exprtokens = e._parse(instring, loc, doActions) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 1084, in _parseNoCache 
    loc,tokens = self.parseImpl(instring, preloc, doActions) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 2576, in parseImpl 
    loc, exprtokens = e._parse(instring, loc, doActions) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 1084, in _parseNoCache 
    loc,tokens = self.parseImpl(instring, preloc, doActions) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 3038, in parseImpl 
    loc, tokens = self.expr._parse(instring, loc, doActions, callPreParse=False) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 1084, in _parseNoCache 
    loc,tokens = self.parseImpl(instring, preloc, doActions) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 2576, in parseImpl 
    loc, exprtokens = e._parse(instring, loc, doActions) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 1110, in _parseNoCache 
    tokens = fn(instring, tokensStart, retTokens) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 831, in wrapper 
    ret = func(*args[limit[0]:]) 
    File "d:\virtual_envs\py2711\lib\site-packages\pyparsing.py", line 3542, in originalTextFor 
    matchExpr.setParseAction(extractText) 
AttributeError: 'NoneType' object has no attribute 'setParseAction' 

我还在学习pyparsing,这里有什么问题?

回答

1

originalTextFor不是分析动作,而是将分析动作附加到定义表达式的辅助方法。在您的例子是在两个地方使用:

# Value 
VALUE = (EXPRESSION | pyparsing.Word(pyparsing.alphanums + "_()+-/*") 
     ).setParseAction(pyparsing.originalTextFor) 

FUNCTION = (pyparsing.Optional(PRECISION).setResultsName("precision") + 
      TYPE.setResultsName("type") + 
      IDENTIFIER.setResultsName("name") + 
      LPAREN + pyparsing.Optional(PARAMETERS).setResultsName("args") + RPAREN + 
      pyparsing.nestedExpr("{", "}").setParseAction(pyparsing.originalTextFor).setResultsName("code")) 

改变这些到:

# Value 
VALUE = pyparsing.originalTextFor(EXPRESSION | 
            pyparsing.Word(pyparsing.alphanums + "_()+-/*")) 

FUNCTION = (pyparsing.Optional(PRECISION).setResultsName("precision") + 
      TYPE.setResultsName("type") + 
      IDENTIFIER.setResultsName("name") + 
      LPAREN + pyparsing.Optional(PARAMETERS).setResultsName("args") + RPAREN + 
      pyparsing.originalTextFor(pyparsing.nestedExpr("{", "}")).setResultsName("code")) 

您可能会发现的setResultsName更近pyparsing形式是干净了一点前瞻性,但老形式也可正常工作:

FUNCTION = (pyparsing.Optional(PRECISION)("precision") + 
      TYPE("type") + 
      IDENTIFIER("name") + 
      LPAREN + pyparsing.Optional(PARAMETERS)("args") + RPAREN + 
      pyparsing.originalTextFor(pyparsing.nestedExpr("{", "}"))("code")) 

如果你做出这种改变,那些使用listAllMatches地方都加入了“*”的名称参数处理:

pyparsing.delimitedList(VARIABLE("variable*")) 
+1

我可以看到你pyparsing的主要作者,首先,祝贺你的工作很好!我必须说的是你写在那里的一个非常整洁的模块。我认为使用你的模块来创建一个[little glsl editor widget](http://stackoverflow.com/questions/39700568/tweakable-values-on-qscintilla-widget)可能是一个好主意。如果您有任何关于当前glsl解析器的良好建议/建议,请通知我,我相信目前的版本远没有完美。 – BPL

0

继@保罗麦圭尔建议,这里是它采用python3.5.1一个工作版本和2.1.9 pyparsing:

# -*- coding: utf-8 -*- 
# ----------------------------------------------------------------------------- 
# Copyright (c) 2014, Nicolas P. Rougier 
# Distributed under the (new) BSD License. See LICENSE.txt for more info. 
# ----------------------------------------------------------------------------- 
import pyparsing 

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 = pyparsing.Literal("(").suppress() 
RPAREN = pyparsing.Literal(")").suppress() 
LBRACK = pyparsing.Literal("[").suppress() 
RBRACK = pyparsing.Literal("]").suppress() 
LBRACE = pyparsing.Literal("{").suppress() 
RBRACE = pyparsing.Literal("}").suppress() 
IDENTIFIER = pyparsing.Word(pyparsing.alphas + '_', pyparsing.alphanums + '_') 
TYPE = pyparsing.Word(pyparsing.alphas + '_', pyparsing.alphanums + "_") 
END = pyparsing.Literal(";").suppress() 
INT = pyparsing.Word(pyparsing.nums) 
FLOAT = pyparsing.Regex(
    '[+-]?(((\d+\.\d*)|(\d*\.\d+))([eE][-+]?\d+)?)|(\d*[eE][+-]?\d+)') 
STORAGE = pyparsing.Regex('|'.join(storage.split(' '))) 
PRECISION = pyparsing.Regex('|'.join(precision.split(' '))) 
STRUCT = pyparsing.Literal("struct").suppress() 


# ------------------------ 
def get_prototypes(code): 
    """ 
    Get all function declarations 

    Code example 
    ------------ 

    mediump vec3 function_1(vec4); 
    vec3 function_2(float a, float b); 
    """ 

    PARAMETER = pyparsing.Group(pyparsing.Optional(PRECISION)("precision") + 
           TYPE("type") + 
           pyparsing.Optional(IDENTIFIER)("name")) 
    PARAMETERS = pyparsing.delimitedList(PARAMETER)(
     "arg*") 
    PROTOTYPE = (pyparsing.Optional(PRECISION)("precision") + 
       TYPE("type") + 
       IDENTIFIER("name") + 
       LPAREN + pyparsing.Optional(PARAMETERS)("args") + RPAREN + 
       END) 
    PROTOTYPE.ignore(pyparsing.cStyleComment) 

    for (token, start, end) in PROTOTYPE.scanString(code): 
     print(token.precision, token.type, token.name, '(',) 
     for arg in token.args: 
      print(arg.precision, arg.type, arg.name, ',',) 
     print(')') 


# ------------------------ 
def get_functions(code): 
    """ 
    Get all function definitions 

    Code example 
    ------------ 

    mediump vec3 compute_normal(vec4); 
    """ 

    PARAMETER = pyparsing.Group(pyparsing.Optional(PRECISION)("precision") + 
           TYPE("type") + 
           pyparsing.Optional(IDENTIFIER)("name")) 
    PARAMETERS = pyparsing.delimitedList(PARAMETER)(
     "arg*") 
    FUNCTION = (pyparsing.Optional(PRECISION)("precision") + 
       TYPE("type") + 
       IDENTIFIER("name") + 
       LPAREN + pyparsing.Optional(PARAMETERS)("args") + RPAREN + 
       pyparsing.originalTextFor(pyparsing.nestedExpr("{", "}"))("code")) 
    FUNCTION.ignore(pyparsing.cStyleComment) 

    for (token, start, end) in FUNCTION.scanString(code): 
     print(token.precision, token.type, token.name, '(',) 
     for arg in token.args: 
      print(arg.precision, arg.type, arg.name, ',',) 
     print(') { ... }') 

     # print token.code 
     # print code[start:end] 


# ------------------------ 
def get_version(code): 
    """ 
    Get shader version (if specified) 

    Code example 
    ------------ 

    #version 120 
    """ 

    VERSION = (
     pyparsing.Literal("#") + pyparsing.Keyword("version")).suppress() + INT 
    for (token, start, end) in VERSION.scanString(code): 
     version = token[0] 
     # print code[start:end] 
    return version 

# ------------------------ 


def get_declarations(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 = pyparsing.Forward() 
    ARG = pyparsing.Group(EXPRESSION) | IDENTIFIER | FLOAT | INT 
    ARGS = pyparsing.delimitedList(ARG) 
    EXPRESSION << IDENTIFIER + \ 
     pyparsing.Group(LPAREN + pyparsing.Optional(ARGS) + RPAREN) 

    # Value 
    VALUE = pyparsing.originalTextFor(EXPRESSION | 
             pyparsing.Word(pyparsing.alphanums + "_()+-/*")) 

    # Single declaration 
    VARIABLE = (IDENTIFIER("name") + 
       pyparsing.Optional(LBRACK + 
            (INT | IDENTIFIER)("size") 
            + RBRACK) + 
       pyparsing.Optional(pyparsing.Literal("=").suppress() + VALUE("value"))) 

    # Several declarations at once 
    DECLARATION = (STORAGE("storage") + 
        pyparsing.Optional(PRECISION)("precision") + 
        TYPE("type") + 
        pyparsing.delimitedList(VARIABLE("variable*")) + 
        END) 
    DECLARATION.ignore(pyparsing.cStyleComment) 

    for (tokens, start, end) in DECLARATION.scanString(code): 
     for token in tokens.variable: 
      print(tokens.storage, tokens.precision, tokens.type,) 
      print(token.name, token.size) 


# ------------------------ 
def get_definitions(code): 
    """ 
    Get all structure definitions and associated declarations. 

    Code example 
    ------------ 

    uniform struct Light { 
     vec4 position; 
     vec3 color; 
    } light0, light1; 
    """ 

    # Single declaration 
    DECLARATION = pyparsing.Group(IDENTIFIER("name") + 
            pyparsing.Optional(LBRACK + 
                (INT | IDENTIFIER)("size") + 
                RBRACK)) 
    # Several declarations at once 
    DECLARATIONS = (pyparsing.Optional(PRECISION) + 
        TYPE + 
        pyparsing.delimitedList(DECLARATION) + 
        END) 

    # Definition + declarations 
    DEFINITION = (STRUCT + 
        IDENTIFIER("name") + 
        LBRACE + pyparsing.OneOrMore(DECLARATIONS)('content') + RBRACE + 
        pyparsing.Optional(pyparsing.delimitedList(DECLARATION("declarations*"))) + 
        END) 
    DEFINITION.ignore(pyparsing.cStyleComment) 

    for (tokens, start, end) in DEFINITION.scanString(code): 
     for token in tokens.declarations: 
      print(tokens.name, token.name) 
      # print tokens.content 


# ---------------- 
def resolve(code): 
    """ 
    Expand const and preprocessor definitions in order to get constant values. 

    Return the transformed code 
    """ 

    constants = {} 

    DEFINITION = (pyparsing.Literal("#") + pyparsing.Literal("define") + 
        IDENTIFIER("name") + 
        pyparsing.restOfLine("value")) 

    VALUE = pyparsing.Word(pyparsing.alphanums + "_()+-/*") 
    DECLARATION = (pyparsing.Literal("const") + 
        TYPE("type") + 
        IDENTIFIER("name") + 
        pyparsing.Literal("=") + 
        VALUE("value") + 
        pyparsing.Literal(";")) 
    REFERENCE = pyparsing.Forward() 

    def process_definition(s, l, t): 
     value = REFERENCE.transformString(t.value) 
     constants[t.name] = value 
     REFERENCE << pyparsing.MatchFirst(
      map(pyparsing.Keyword, constants.keys())) 
     return "#define " + t.name + " " + value 

    def process_declaration(s, l, t): 
     value = REFERENCE.transformString(t.value) 
     constants[t.name] = value 
     REFERENCE << pyparsing.MatchFirst(
      map(pyparsing.Keyword, constants.keys())) 
     return "const " + t.type + " " + t.name + "=" + value + ";" 

    def process_reference(s, l, t): 
     return constants[t[0]] 

    REFERENCE.setParseAction(process_reference) 
    DEFINITION.setParseAction(process_definition) 
    DECLARATION.setParseAction(process_declaration) 
    EXPANDER = REFERENCE | DEFINITION | DECLARATION 

    code = EXPANDER.transformString(code) 
    for key, val in constants.items(): 
     constants[key] = eval(val) 

    return code, constants 


# ----------------------------------------------------------------------------- 
if __name__ == '__main__': 

    code = """ 
#version 120 

#define A (1) 
const int B=(A+2); 
#define C (B+3) 
const int D=C+4; 

uniform float array[D]; 

struct Point { 
    vec4 position; 
    float size; 
}; 

uniform struct Light { 
    vec4 position; 
    vec3 color; 
} light0, light1; 

const float PI = 3.14159265358979323846264; 
const float SQRT_2 = 1.4142135623730951; 

uniform vec4 fg_color = vec4(1), 
      bg_color = vec4(vec3(0),1); 

mediump vec3 compute_normal(vec4 position, vec3 orientation); 
vec3 /* */ compute_light(vec4, vec3, float intensity) 
{ 
    vec3 hello; 
    vec3 hello; 
} 

""" 
code, _ = resolve(code) 
print("GLSL version: %s\n" % get_version(code)) 

get_definitions(code) 
get_declarations(code) 
get_prototypes(code) 
get_functions(code) 

# code = """ 
# #if A 
# #if B 
# #if C 
# #endif 
# #endif 
# #endif 
# """ 

# IF = (pyparsing.Literal('#') + (pyparsing.Keyword('if') | pyparsing.Keyword('ifdef') | pyparsing.Keyword('ifndef'))) 
# ENDIF = (pyparsing.Literal('#') + pyparsing.Keyword('endif')) 
# MACRO = (IF + pyparsing.restOfLine() + 
#   SkipTo(ENDIF, include=True)).setParseAction(pyparsing.originalTextFor) 
# for (tokens, start, end) in MACRO.scanString(code): 
#  print tokens