56 releases (22 stable)
7.0.0 | Dec 22, 2023 |
---|---|
6.0.3 |
|
6.0.2 | Mar 23, 2023 |
6.0.1 | Jul 11, 2021 |
0.3.0 | Jul 31, 2017 |
#130 in Parser implementations
2,096 downloads per month
Used in 21 crates
(14 directly)
290KB
8K
SLoC
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 asParse
andParseError
.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
ExternalDeclaration
s. 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);
assert!(expr.is_ok());
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);
assert!(stage.is_ok());
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.
Dependencies
~0.8–5MB
~112K SLoC