Skip to main content

wgsl_parse/
parser.rs

1use std::str::FromStr;
2
3use crate::{
4    error::Error,
5    lexer::{Lexer, Token, TokenIterator},
6    syntax::*,
7};
8
9#[allow(clippy::all, reason = "generated code")]
10mod grammar {
11    include!("grammar.rs");
12}
13
14pub use crate::parser_support::ParseEntryPoint;
15use grammar::EntryPointParser;
16
17macro_rules! parse {
18    ($source:expr, $token:ident, $entrypoint:ident) => {{
19        match parse_tokens(Lexer::new($source), Token::$token) {
20            Ok(ParseEntryPoint::$entrypoint(res)) => Ok(res),
21            Ok(_) => unreachable!("parser parsed the wrong entrypoint"),
22            Err(e) => Err(e),
23        }
24    }};
25}
26
27/// Parse a token stream into a syntax tree.
28///
29/// Low-level implementation, you probably don't want to use this. See [`parse_str`].
30///
31/// The `entrypoint` parameter must be one of the `EntryPointXXX` tokens, which tells the
32/// parser which syntax node to expect. It returns the [`ParseEntryPoint`] union type.
33pub fn parse_tokens(
34    lexer: impl TokenIterator,
35    entrypoint: Token,
36) -> Result<ParseEntryPoint, Error> {
37    let lexer = std::iter::once(Ok((0, entrypoint, 0))).chain(lexer);
38    let parser = EntryPointParser::new();
39    parser.parse(lexer).map_err(Into::into)
40}
41
42/// Parse a string into a syntax tree ([`TranslationUnit`]).
43///
44/// Identical to [`TranslationUnit::from_str`].
45pub fn parse_str(source: &str) -> Result<TranslationUnit, Error> {
46    parse!(source, EntryPointTranslationUnit, TranslationUnit)
47}
48
49pub fn recognize_template_list(lexer: impl TokenIterator) -> Result<(), Error> {
50    match parse_tokens(lexer, Token::EntryPointTryTemplateList) {
51        Ok(ParseEntryPoint::TryTemplateList(_)) => Ok(()),
52        Ok(_) => unreachable!("parser parsed the wrong entrypoint"),
53        Err(e) => Err(e),
54    }
55}
56
57impl FromStr for TranslationUnit {
58    type Err = Error;
59
60    fn from_str(source: &str) -> Result<Self, Self::Err> {
61        parse!(source, EntryPointTranslationUnit, TranslationUnit)
62    }
63}
64impl FromStr for GlobalDirective {
65    type Err = Error;
66
67    fn from_str(source: &str) -> Result<Self, Self::Err> {
68        parse!(source, EntryPointGlobalDirective, GlobalDirective)
69    }
70}
71impl FromStr for GlobalDeclaration {
72    type Err = Error;
73
74    fn from_str(source: &str) -> Result<Self, Self::Err> {
75        parse!(source, EntryPointGlobalDecl, GlobalDecl)
76    }
77}
78impl FromStr for Statement {
79    type Err = Error;
80
81    fn from_str(source: &str) -> Result<Self, Self::Err> {
82        parse!(source, EntryPointStatement, Statement)
83    }
84}
85impl FromStr for Expression {
86    type Err = Error;
87
88    fn from_str(source: &str) -> Result<Self, Self::Err> {
89        parse!(source, EntryPointExpression, Expression)
90    }
91}
92impl FromStr for LiteralExpression {
93    type Err = Error;
94
95    fn from_str(source: &str) -> Result<Self, Self::Err> {
96        parse!(source, EntryPointLiteral, Literal)
97    }
98}
99impl FromStr for crate::syntax::ImportStatement {
100    type Err = Error;
101
102    fn from_str(source: &str) -> Result<Self, Self::Err> {
103        parse!(source, EntryPointImportStatement, ImportStatement)
104    }
105}
106
107#[cfg(test)]
108mod test {
109    use super::*;
110
111    fn expect_err<T: FromStr + std::fmt::Display>(expr: &str)
112    where
113        <T as FromStr>::Err: std::fmt::Debug,
114    {
115        let parsed = T::from_str(expr);
116        assert!(
117            parsed.is_err(),
118            "case `{expr}`: expected Err, got Ok({})",
119            parsed.unwrap()
120        );
121    }
122
123    fn expect_ok<T: FromStr + std::fmt::Debug>(expr: &str)
124    where
125        <T as FromStr>::Err: std::fmt::Display,
126    {
127        let parsed = T::from_str(expr);
128        assert!(
129            parsed.is_ok(),
130            "case `{expr}`: expected Ok, got Err({})",
131            parsed.unwrap_err()
132        );
133    }
134
135    #[test]
136    fn operator_prec_assoc() {
137        // failure cases from the WGSL spec
138        expect_ok::<Expression>("x & (y ^ (z | w))"); // Invalid: x & y ^ z | w
139        expect_ok::<Expression>("(x + y) << (z >= w)"); // Invalid: x + y << z >= w
140        expect_ok::<Expression>("x < (y > z)"); // Invalid: x < y > z
141        expect_ok::<Expression>("x && (y || z)"); // Invalid: x && y || z
142        expect_err::<Expression>("x & y ^ z | w");
143        expect_err::<Expression>("x + y << z >= w");
144        expect_err::<Expression>("x < y > z");
145        expect_err::<Expression>("x && y || z");
146        // more cases
147        expect_ok::<Expression>("x && y && z");
148        expect_ok::<Expression>("x || y || z");
149        expect_ok::<Expression>("x & y & z");
150        expect_ok::<Expression>("x ^ y ^ z");
151        expect_ok::<Expression>("x | y | z");
152        expect_err::<Expression>("x >> y >> z");
153        expect_err::<Expression>("x << y << z");
154        expect_ok::<Expression>("x && y < z && &w");
155        expect_ok::<Expression>("x & -y & &z");
156    }
157
158    #[test]
159    fn lhs_expression() {
160        expect_ok::<Statement>("(x) = 1;");
161        expect_ok::<Statement>("(*x) += 1;");
162        expect_ok::<Statement>("&(*x) += 1;");
163        expect_ok::<Statement>("x[0] = 1;");
164        expect_ok::<Statement>("x.y[0] = 1;");
165        expect_ok::<Statement>("x[0].y = 1;");
166
167        // WESL extensions
168        expect_ok::<Statement>("@if(true) x = 1;");
169        expect_ok::<Statement>("@if(true) &(*x) += 1;");
170        expect_err::<Statement>("@if(true) (*x) += 1;");
171        expect_ok::<Statement>("x::y = 1;");
172    }
173}