naga/front/glsl/
parser.rs

1use alloc::{string::String, vec};
2use core::iter::Peekable;
3
4use pp_rs::token::{PreprocessorError, Token as PPToken, TokenValue as PPTokenValue};
5
6use super::{
7    ast::{FunctionKind, Profile, TypeQualifiers},
8    context::{Context, ExprPos},
9    error::ExpectedToken,
10    error::{Error, ErrorKind},
11    lex::{Lexer, LexerResultKind},
12    token::{Directive, DirectiveKind},
13    token::{Token, TokenValue},
14    variables::{GlobalOrConstant, VarDeclaration},
15    Frontend, Result,
16};
17use crate::{arena::Handle, proc::U32EvalError, Expression, Module, Span, Type};
18
19mod declarations;
20mod expressions;
21mod functions;
22mod types;
23
24pub struct ParsingContext<'source> {
25    lexer: Peekable<Lexer<'source>>,
26    /// Used to store tokens already consumed by the parser but that need to be backtracked
27    backtracked_token: Option<Token>,
28    last_meta: Span,
29}
30
31impl<'source> ParsingContext<'source> {
32    pub fn new(lexer: Lexer<'source>) -> Self {
33        ParsingContext {
34            lexer: lexer.peekable(),
35            backtracked_token: None,
36            last_meta: Span::default(),
37        }
38    }
39
40    /// Helper method for backtracking from a consumed token
41    ///
42    /// This method should always be used instead of assigning to `backtracked_token` since
43    /// it validates that backtracking hasn't occurred more than one time in a row
44    ///
45    /// # Panics
46    /// - If the parser already backtracked without bumping in between
47    pub fn backtrack(&mut self, token: Token) -> Result<()> {
48        // This should never happen
49        if let Some(ref prev_token) = self.backtracked_token {
50            return Err(Error {
51                kind: ErrorKind::InternalError("The parser tried to backtrack twice in a row"),
52                meta: prev_token.meta,
53            });
54        }
55
56        self.backtracked_token = Some(token);
57
58        Ok(())
59    }
60
61    pub fn expect_ident(&mut self, frontend: &mut Frontend) -> Result<(String, Span)> {
62        let token = self.bump(frontend)?;
63
64        match token.value {
65            TokenValue::Identifier(name) => Ok((name, token.meta)),
66            _ => Err(Error {
67                kind: ErrorKind::InvalidToken(token.value, vec![ExpectedToken::Identifier]),
68                meta: token.meta,
69            }),
70        }
71    }
72
73    pub fn expect(&mut self, frontend: &mut Frontend, value: TokenValue) -> Result<Token> {
74        let token = self.bump(frontend)?;
75
76        if token.value != value {
77            Err(Error {
78                kind: ErrorKind::InvalidToken(token.value, vec![value.into()]),
79                meta: token.meta,
80            })
81        } else {
82            Ok(token)
83        }
84    }
85
86    pub fn next(&mut self, frontend: &mut Frontend) -> Option<Token> {
87        loop {
88            if let Some(token) = self.backtracked_token.take() {
89                self.last_meta = token.meta;
90                break Some(token);
91            }
92
93            let res = self.lexer.next()?;
94
95            match res.kind {
96                LexerResultKind::Token(token) => {
97                    self.last_meta = token.meta;
98                    break Some(token);
99                }
100                LexerResultKind::Directive(directive) => {
101                    frontend.handle_directive(directive, res.meta)
102                }
103                LexerResultKind::Error(error) => frontend.errors.push(Error {
104                    kind: ErrorKind::PreprocessorError(error),
105                    meta: res.meta,
106                }),
107            }
108        }
109    }
110
111    pub fn bump(&mut self, frontend: &mut Frontend) -> Result<Token> {
112        self.next(frontend).ok_or(Error {
113            kind: ErrorKind::EndOfFile,
114            meta: self.last_meta,
115        })
116    }
117
118    /// Returns None on the end of the file rather than an error like other methods
119    pub fn bump_if(&mut self, frontend: &mut Frontend, value: TokenValue) -> Option<Token> {
120        if self.peek(frontend).filter(|t| t.value == value).is_some() {
121            self.bump(frontend).ok()
122        } else {
123            None
124        }
125    }
126
127    pub fn peek(&mut self, frontend: &mut Frontend) -> Option<&Token> {
128        loop {
129            if let Some(ref token) = self.backtracked_token {
130                break Some(token);
131            }
132
133            match self.lexer.peek()?.kind {
134                LexerResultKind::Token(_) => {
135                    let res = self.lexer.peek()?;
136
137                    match res.kind {
138                        LexerResultKind::Token(ref token) => break Some(token),
139                        _ => unreachable!(),
140                    }
141                }
142                LexerResultKind::Error(_) | LexerResultKind::Directive(_) => {
143                    let res = self.lexer.next()?;
144
145                    match res.kind {
146                        LexerResultKind::Directive(directive) => {
147                            frontend.handle_directive(directive, res.meta)
148                        }
149                        LexerResultKind::Error(error) => frontend.errors.push(Error {
150                            kind: ErrorKind::PreprocessorError(error),
151                            meta: res.meta,
152                        }),
153                        LexerResultKind::Token(_) => unreachable!(),
154                    }
155                }
156            }
157        }
158    }
159
160    pub fn expect_peek(&mut self, frontend: &mut Frontend) -> Result<&Token> {
161        let meta = self.last_meta;
162        self.peek(frontend).ok_or(Error {
163            kind: ErrorKind::EndOfFile,
164            meta,
165        })
166    }
167
168    pub fn parse(&mut self, frontend: &mut Frontend) -> Result<Module> {
169        let mut module = Module::default();
170        let mut global_expression_kind_tracker = crate::proc::ExpressionKindTracker::new();
171
172        // Body and expression arena for global initialization
173        let mut ctx = Context::new(
174            frontend,
175            &mut module,
176            false,
177            &mut global_expression_kind_tracker,
178        )?;
179
180        while self.peek(frontend).is_some() {
181            self.parse_external_declaration(frontend, &mut ctx)?;
182        }
183
184        // Add an `EntryPoint` to `parser.module` for `main`, if a
185        // suitable overload exists. Error out if we can't find one.
186        if let Some(declaration) = frontend.lookup_function.get("main") {
187            for decl in declaration.overloads.iter() {
188                if let FunctionKind::Call(handle) = decl.kind {
189                    if decl.defined && decl.parameters.is_empty() {
190                        frontend.add_entry_point(handle, ctx)?;
191                        return Ok(module);
192                    }
193                }
194            }
195        }
196
197        Err(Error {
198            kind: ErrorKind::SemanticError("Missing entry point".into()),
199            meta: Span::default(),
200        })
201    }
202
203    fn parse_uint_constant(
204        &mut self,
205        frontend: &mut Frontend,
206        ctx: &mut Context,
207    ) -> Result<(u32, Span)> {
208        let (const_expr, meta) = self.parse_constant_expression(
209            frontend,
210            ctx.module,
211            ctx.global_expression_kind_tracker,
212        )?;
213
214        let res = ctx.module.to_ctx().eval_expr_to_u32(const_expr);
215
216        let int = match res {
217            Ok(value) => Ok(value),
218            Err(U32EvalError::Negative) => Err(Error {
219                kind: ErrorKind::SemanticError("int constant overflows".into()),
220                meta,
221            }),
222            Err(U32EvalError::NonConst) => Err(Error {
223                kind: ErrorKind::SemanticError("Expected a uint constant".into()),
224                meta,
225            }),
226        }?;
227
228        Ok((int, meta))
229    }
230
231    fn parse_constant_expression(
232        &mut self,
233        frontend: &mut Frontend,
234        module: &mut Module,
235        global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker,
236    ) -> Result<(Handle<Expression>, Span)> {
237        let mut ctx = Context::new(frontend, module, true, global_expression_kind_tracker)?;
238
239        let mut stmt_ctx = ctx.stmt_ctx();
240        let expr = self.parse_conditional(frontend, &mut ctx, &mut stmt_ctx, None)?;
241        let (root, meta) = ctx.lower_expect(stmt_ctx, frontend, expr, ExprPos::Rhs)?;
242
243        Ok((root, meta))
244    }
245}
246
247impl Frontend {
248    fn handle_directive(&mut self, directive: Directive, meta: Span) {
249        let mut tokens = directive.tokens.into_iter();
250
251        match directive.kind {
252            DirectiveKind::Version { is_first_directive } => {
253                if !is_first_directive {
254                    self.errors.push(Error {
255                        kind: ErrorKind::SemanticError(
256                            "#version must occur first in shader".into(),
257                        ),
258                        meta,
259                    })
260                }
261
262                match tokens.next() {
263                    Some(PPToken {
264                        value: PPTokenValue::Integer(int),
265                        location,
266                    }) => match int.value {
267                        440 | 450 | 460 => self.meta.version = int.value as u16,
268                        _ => self.errors.push(Error {
269                            kind: ErrorKind::InvalidVersion(int.value),
270                            meta: location.into(),
271                        }),
272                    },
273                    Some(PPToken { value, location }) => self.errors.push(Error {
274                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
275                            value,
276                        )),
277                        meta: location.into(),
278                    }),
279                    None => self.errors.push(Error {
280                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine),
281                        meta,
282                    }),
283                };
284
285                match tokens.next() {
286                    Some(PPToken {
287                        value: PPTokenValue::Ident(name),
288                        location,
289                    }) => match name.as_str() {
290                        "core" => self.meta.profile = Profile::Core,
291                        _ => self.errors.push(Error {
292                            kind: ErrorKind::InvalidProfile(name),
293                            meta: location.into(),
294                        }),
295                    },
296                    Some(PPToken { value, location }) => self.errors.push(Error {
297                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
298                            value,
299                        )),
300                        meta: location.into(),
301                    }),
302                    None => {}
303                };
304
305                if let Some(PPToken { value, location }) = tokens.next() {
306                    self.errors.push(Error {
307                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
308                            value,
309                        )),
310                        meta: location.into(),
311                    })
312                }
313            }
314            DirectiveKind::Extension => {
315                // TODO: Proper extension handling
316                // - Checking for extension support in the compiler
317                // - Handle behaviors such as warn
318                // - Handle the all extension
319                let name = match tokens.next() {
320                    Some(PPToken {
321                        value: PPTokenValue::Ident(name),
322                        ..
323                    }) => Some(name),
324                    Some(PPToken { value, location }) => {
325                        self.errors.push(Error {
326                            kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
327                                value,
328                            )),
329                            meta: location.into(),
330                        });
331
332                        None
333                    }
334                    None => {
335                        self.errors.push(Error {
336                            kind: ErrorKind::PreprocessorError(
337                                PreprocessorError::UnexpectedNewLine,
338                            ),
339                            meta,
340                        });
341
342                        None
343                    }
344                };
345
346                match tokens.next() {
347                    Some(PPToken {
348                        value: PPTokenValue::Punct(pp_rs::token::Punct::Colon),
349                        ..
350                    }) => {}
351                    Some(PPToken { value, location }) => self.errors.push(Error {
352                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
353                            value,
354                        )),
355                        meta: location.into(),
356                    }),
357                    None => self.errors.push(Error {
358                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine),
359                        meta,
360                    }),
361                };
362
363                match tokens.next() {
364                    Some(PPToken {
365                        value: PPTokenValue::Ident(behavior),
366                        location,
367                    }) => match behavior.as_str() {
368                        "require" | "enable" | "warn" | "disable" => {
369                            if let Some(name) = name {
370                                self.meta.extensions.insert(name);
371                            }
372                        }
373                        _ => self.errors.push(Error {
374                            kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
375                                PPTokenValue::Ident(behavior),
376                            )),
377                            meta: location.into(),
378                        }),
379                    },
380                    Some(PPToken { value, location }) => self.errors.push(Error {
381                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
382                            value,
383                        )),
384                        meta: location.into(),
385                    }),
386                    None => self.errors.push(Error {
387                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine),
388                        meta,
389                    }),
390                }
391
392                if let Some(PPToken { value, location }) = tokens.next() {
393                    self.errors.push(Error {
394                        kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken(
395                            value,
396                        )),
397                        meta: location.into(),
398                    })
399                }
400            }
401            DirectiveKind::Pragma => {
402                // TODO: handle some common pragmas?
403            }
404        }
405    }
406}
407
408pub struct DeclarationContext<'ctx, 'qualifiers, 'a> {
409    qualifiers: TypeQualifiers<'qualifiers>,
410    /// Indicates a global declaration
411    external: bool,
412    is_inside_loop: bool,
413    ctx: &'ctx mut Context<'a>,
414}
415
416impl DeclarationContext<'_, '_, '_> {
417    fn add_var(
418        &mut self,
419        frontend: &mut Frontend,
420        ty: Handle<Type>,
421        name: String,
422        init: Option<Handle<Expression>>,
423        meta: Span,
424    ) -> Result<Handle<Expression>> {
425        let decl = VarDeclaration {
426            qualifiers: &mut self.qualifiers,
427            ty,
428            name: Some(name),
429            init,
430            meta,
431        };
432
433        match self.external {
434            true => {
435                let global = frontend.add_global_var(self.ctx, decl)?;
436                let expr = match global {
437                    GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle),
438                    GlobalOrConstant::Constant(handle) => Expression::Constant(handle),
439                };
440                Ok(self.ctx.add_expression(expr, meta)?)
441            }
442            false => frontend.add_local_var(self.ctx, decl),
443        }
444    }
445}