created_at2017-07-03 13:28:19.211793
updated_at2023-12-22 23:35:01.935748
descriptionA GLSL450/GLSL460 parser.
Dimitri Sabadie (hadronized)



Build Status License

This crate is a GLSL450/GLSL460 compiler. It’s able to parse valid GLSL formatted source into an abstract syntax tree (AST). That AST can then be transformed into SPIR-V, your own format or even folded back to a raw GLSL [String] (think of a minifier, for instance).

You’ll find several modules:

  • [parser], which exports the parsing interface. This is the place you will get most interesting types and traits, such as Parse and ParseError.
  • [syntax], which exports the AST and language definitions. If you look into destructuring, transpiling or getting information on the GLSL code that got parsed, you will likely manipulate objects which types are defined in this module.
  • [transpiler], which provides you with GLSL transpilers. For instance, you will find GLSL to GLSL transpiler, GLSL to SPIR-V transpiler, etc.
  • visitor, which gives you a way to visit AST nodes and mutate them, both with inner and outer mutation.

Feel free to inspect those modules for further information.

GLSL parsing and transpiling

Parsing is the most common operation you will do. It is not required per-se (you can still create your AST by hand or use glsl-quasiquote to create it at compile-time by using the GLSL syntax directly in Rust). However, in this section, we are going to see how we can parse from a string to several GLSL types.

Parsing architecture

Basically, the Parse trait gives you all you need to start parsing. This crate is designed around the concept of type-driven parsing: parsers are hidden and you just have to state what result type you expect.

The most common type you want to parse to is TranslationUnit, which represents a set of ExternalDeclarations. An ExternalDeclaration is just a declaration at the top-most level of a shader. It can be a global, uniform declarations, vertex attributes, a function, a structure, etc. In that sense, a TranslationUnit is akin to a shader stage (vertex shader, fragment shader, etc.).

You can parse any type that implements Parse. Parsers are mostly sensible to external blanks, which means that parsing an Expr starting with a blank will not work (this is not true for a TranslationUnit as it’s exceptionnally more permissive).

Parsing an expression

Let’s try to parse an expression.

use glsl::parser::Parse as _;
use glsl::syntax::Expr;

let glsl = "(vec3(r, g, b) * cos(t * PI * .5)).xxz";
let expr = Expr::parse(glsl);

Here, expr is an AST which type is Result<Expr, ParseError> that represents the GLSL expression (vec3(r, g, b) * cos(t * PI * .5)).xxz, which is an outer (scalar) multiplication of an RGB color by a cosine of a time, the whole thing being swizzled with XXZ. It is your responsibility to check if the parsing process has succeeded.

In the previous example, the GLSL string is a constant and hardcoded. It could come from a file, network or built on the fly, but in the case of constant GLSL code, it would be preferable not to parse the string at runtime, right? Well, glsl-quasiquote is there exactly for that. You can ask rustc to parse that string and, if the parsing has succeeded, inject the AST directly into your code. No [Result], just the pure AST. Have a look at glsl-quasiquote for further details.

Parsing a whole shader

Vertex shaders, geometry shaders, fragment shaders and control and evaluation tessellation shaders can be parsed the same way by using one of the TranslationUnit or ShaderStage types.

Here, a simple vertex shader being parsed.

use glsl::parser::Parse as _;
use glsl::syntax::ShaderStage;

let glsl = "
  layout (location = 0) in vec3 pos;
  layout (location = 1) in vec4 col;

  out vec4 v_col;

  uniform mat4 projview;

  void main() {
    v_col = col; // pass color to the next stage
    gl_Position = projview * vec4(pos, 1.);
let stage = ShaderStage::parse(glsl);

Visiting AST nodes

The crate is also getting more and more combinators and functions to transform the AST or create nodes with regular Rust. The Visitor trait will be a great friend of yours when you will want to cope with deep mutation, filtering and validation. Have a look at the visitor module for a tutorial on how to use visitors.

About the GLSL versions…

This crate can parse both GLSL450 and GLSL460 formatted input sources. At the language level, the difference between GLSL450 and GLSL460 is pretty much nothing, so both cases are covered.

If you’re wondering, the only difference between both versions is that in GLSL460, it’s authorized to have semicolons (;) on empty lines at top-level in a shader.

Commit count: 449

cargo fmt