glsl_lang_quote/
lib.rs

1//! `glsl-lang-quote` offers proc-macros to quote GLSL syntax in Rust, using the [glsl_lang] crate.
2//!
3//! # Usage
4//!
5//! ```
6//! use glsl_lang_quote::glsl;
7//!
8//! // Parse a translation unit at compile time
9//! let ast = glsl! {
10//!     void main() {
11//!         gl_FragColor = vec4(1., 0., 0., 1.);
12//!     }
13//! };
14//!
15//! // There is exactly one external declaration
16//! assert_eq!(ast.0.len(), 1);
17//! ```
18//!
19//! # Parsing expressions
20//!
21//! This crate offers a set of features `quote-expr`, and `quote-statement`. Enabling those
22//! features enable the respective `parser-expr`, and `parser-statement` in [glsl_lang], which
23//! creates dedicated parsers for those types of GLSL fragments of the language grammar.
24//!
25//! This is the most efficient option for parsing lots of expressions and statements at
26//! compile-time, however this will slow down the initial compilation of `glsl-lang-quote` since
27//! the generated parser file in [glsl_lang] will be much larger.
28//!
29//! This is why by default, this crate enables the `quote-parsable` feature, which uses
30//! [glsl_lang]'s [glsl_lang::parse::Parsable] trait, whose limitations apply. Whichever method you
31//! chose, the following code will work:
32//!
33//! ```
34//! use glsl_lang_quote::glsl_expr;
35//!
36//! // Parse an expression
37//! let ast = glsl_expr! {
38//!     a = vec4(1., 0., 0., 1.)
39//! };
40//! ```
41//!
42//! # Quoting and stateful lexers
43//!
44//! Since [glsl_lang]'s parser has a stateful lexer (to handle the fact that GLSL's grammar is not
45//! context-free), declaring a type (a `struct` for example) and using it must happen in the same
46//! macro invocation, otherwise the parser will *forget* about the previously declared types. The
47//! best is to only parse whole translation units ([glsl!] macro), or parse unambiguous fragments
48//! (such as expressions, but not statements).
49//!
50//! ```ignore
51//! use glsl_lang_quote::{glsl, glsl_statement};
52//!
53//! // This is ok:
54//! let ast = glsl! {
55//!     struct PointLight {
56//!         vec3 pos;
57//!         vec3 color;
58//!     }
59//!
60//!     PointLight p;
61//! };
62//!
63//! // This will not compile, PointLight can't be parsed as a type name without extra state
64//! let statement = glsl_expr! {
65//!     PointLight p;
66//! };
67
68#![deny(missing_docs)]
69
70use glsl_lang::{
71    ast,
72    parse::{Parsable, ParseOptions},
73};
74use proc_macro2::TokenStream;
75
76use crate::tokenize::Tokenize;
77
78mod quoted;
79mod tokenize;
80
81fn glsl_internal<F>(input: proc_macro::TokenStream) -> proc_macro::TokenStream
82where
83    F: Parsable + Tokenize + std::fmt::Debug,
84{
85    let s = format!("{}", &input);
86    let opts = ParseOptions {
87        allow_rs_ident: true,
88        ..Default::default()
89    };
90    let parsed: Result<(F, _), _> = Parsable::parse_with_options(&s, &opts);
91
92    if let Ok((tu, _)) = parsed {
93        // create the stream and return it
94        let mut stream = TokenStream::new();
95        tu.tokenize(&mut stream);
96
97        stream.into()
98    } else {
99        panic!("GLSL error: {:?}", parsed);
100    }
101}
102
103/// Parse a translation unit at compile time
104#[proc_macro]
105pub fn glsl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
106    glsl_internal::<ast::TranslationUnit>(input)
107}
108
109/// Parse a statement at compile time
110#[cfg(any(feature = "quote-statement", feature = "quote-parsable"))]
111#[proc_macro]
112pub fn glsl_statement(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
113    glsl_internal::<ast::Statement>(input)
114}
115
116/// Parse an expression at compile time
117#[cfg(any(feature = "quote-expr", feature = "quote-parsable"))]
118#[proc_macro]
119pub fn glsl_expr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
120    glsl_internal::<ast::Expr>(input)
121}