extract glsl function body with regexp

Viewed 100

I am having trouble to extract the function in a glsl script using javascript.

Here is the glsl content :

 precision highp float;                           
  varying vec4 v_color;                                 
  #if USE_TEXTURE                                       
      varying vec2 v_uv0;                               
      uniform sampler2D texture;                        
  #endif                                                
vec2 sfx_func_uv_to_01(vec4 ucrect, vec2 uv) {
    return vec2(
        sfx_func_uv_to_01(ucrect[0], ucrect[0] + ucrect[2], uv.x),
        1.0 - sfx_func_uv_to_01(ucrect[1], ucrect[1] - ucrect[3], uv.y)
    );
}
vec2 sfx_func_uv_to_01(vec4 ucrect, vec2 uv) {
    return vec2(1.0, 2.0);
}
vec4 sfx_fs_output_uv_quarter(vec2 uv, vec4 color) {
    vec4 result = color;
    return result;
}
 void main () {                                    
    vec2 uv = v_uv0;                                  
    vec4 color = texture2D(texture, uv);              
    gl_FragColor = color;
}                                             

And I want to extract the 4 functions in the script with the body. So I tried the reg:

/^(vec4|vec3|vec2|float|int).*\{.*\}/gms

And the js code is :

for (const match of glsl1.matchAll(/^(vec4|vec3|vec2|float|int).*\{.*\}/gms)) {
    console.log(match[0]);
}

Instead of 4 functions, I only get one result :

vec2 sfx_func_uv_to_01(vec4 ucrect, vec2 uv) {
    return vec2(
        sfx_func_uv_to_01(ucrect[0], ucrect[0] + ucrect[2], uv.x),
        1.0 - sfx_func_uv_to_01(ucrect[1], ucrect[1] - ucrect[3], uv.y)
    );
}
vec2 sfx_func_uv_to_01(vec4 ucrect, vec2 uv) {
    return vec2(1.0, 2.0);
}
vec4 sfx_fs_output_uv_quarter(vec2 uv, vec4 color) {
    vec4 result = color;
    return result;
}
 void main () {                                    
    vec2 uv = v_uv0;                                  
    vec4 color = texture2D(texture, uv);              
    gl_FragColor = color;
}    

I searched for solution of non-greedy mode for multiline text but came in vain.

Any advice will be appreciated, thanks :)

2 Answers

Use

/^(\s*)(vec4|vec3|vec2|float|int|void).*?{[\s\S]*?^\1}\s*$/gm

See proof.

Explanation

--------------------------------------------------------------------------------
  ^                        the beginning of the string
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    \s*                      whitespace (\n, \r, \t, \f, and " ") (0
                             or more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  (                        group and capture to \2:
--------------------------------------------------------------------------------
    vec4                     'vec4'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    vec3                     'vec3'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    vec2                     'vec2'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    float                    'float'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    int                      'int'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    void                     'void'
--------------------------------------------------------------------------------
  )                        end of \2
--------------------------------------------------------------------------------
  .*?                      any character except \n (0 or more times
                           (matching the least amount possible))
--------------------------------------------------------------------------------
  {                        '{'
--------------------------------------------------------------------------------
  [\s\S]*?                 any character of: whitespace (\n, \r, \t,
                           \f, and " "), non-whitespace (all but \n,
                           \r, \t, \f, and " ") (0 or more times
                           (matching the least amount possible))
--------------------------------------------------------------------------------
  ^                        the beginning of the string
--------------------------------------------------------------------------------
  \1                       what was matched by capture \1
--------------------------------------------------------------------------------
  }                        '}'
--------------------------------------------------------------------------------
  \s*                      whitespace (\n, \r, \t, \f, and " ") (0 or
                           more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of the
                           string

I wouldn't dare trying to have a regexp to track unlimited nesting of curly braces, but instead attempt to split() along function headers with a g switch (so separators are kept), and then concatenate the header-body pairs. The code assumes that there is some prologue prior to the first function definition, which is probably true for most GLSL sources, and doesn't expect anything to be between the functions (though a code=code.substr(0,code.lastIndexOf("}")+1); could probably address such case).

const source=`precision highp float;                           
  varying vec4 v_color;                                 
  #if USE_TEXTURE                                       
      varying vec2 v_uv0;                               
      uniform sampler2D texture;                        
  #endif                                                
vec2 sfx_func_uv_to_01(vec4 ucrect, vec2 uv) {
    return vec2(
        sfx_func_uv_to_01(ucrect[0], ucrect[0] + ucrect[2], uv.x),
        1.0 - sfx_func_uv_to_01(ucrect[1], ucrect[1] - ucrect[3], uv.y)
    );
}
vec2 sfx_func_uv_to_01(vec4 ucrect, vec2 uv) {
    return vec2(1.0, 2.0);
}
vec4 sfx_fs_output_uv_quarter(vec2 uv, vec4 color) {
    vec4 result = color;
    return result;
}
int trap(){if(true){whatever}}
 void main () {                                    
    vec2 uv = v_uv0;                                  
    vec4 color = texture2D(texture, uv);              
    gl_FragColor = color;
}               `;

const blocks=source.split(/((?:vec4|vec3|vec2|float|int|void)\s+[\w]+\s*\(.*?\))/gs);
console.log("Header");
console.log(blocks.shift());
for(let i=1;blocks.length;i++){
  console.log("Function #"+i);
  console.log(blocks.shift()+blocks.shift());
}

The int trap(){if(true){whatever}} fake function was added to show that this approach doesn't have to worry about nested curly braces.

Related