use super::{ ast::{FunctionKind, Profile, TypeQualifiers}, context::{Context, ExprPos}, error::ExpectedToken, error::{Error, ErrorKind}, lex::{Lexer, LexerResultKind}, token::{Directive, DirectiveKind}, token::{Token, TokenValue}, variables::{GlobalOrConstant, VarDeclaration}, Frontend, Result, }; use crate::{arena::Handle, proc::U32EvalError, Expression, Module, Span, Type}; use pp_rs::token::{PreprocessorError, Token as PPToken, TokenValue as PPTokenValue}; use std::iter::Peekable; mod declarations; mod expressions; mod functions; mod types; pub struct ParsingContext<'source> { lexer: Peekable>, /// Used to store tokens already consumed by the parser but that need to be backtracked backtracked_token: Option, last_meta: Span, } impl<'source> ParsingContext<'source> { pub fn new(lexer: Lexer<'source>) -> Self { ParsingContext { lexer: lexer.peekable(), backtracked_token: None, last_meta: Span::default(), } } /// Helper method for backtracking from a consumed token /// /// This method should always be used instead of assigning to `backtracked_token` since /// it validates that backtracking hasn't occurred more than one time in a row /// /// # Panics /// - If the parser already backtracked without bumping in between pub fn backtrack(&mut self, token: Token) -> Result<()> { // This should never happen if let Some(ref prev_token) = self.backtracked_token { return Err(Error { kind: ErrorKind::InternalError("The parser tried to backtrack twice in a row"), meta: prev_token.meta, }); } self.backtracked_token = Some(token); Ok(()) } pub fn expect_ident(&mut self, frontend: &mut Frontend) -> Result<(String, Span)> { let token = self.bump(frontend)?; match token.value { TokenValue::Identifier(name) => Ok((name, token.meta)), _ => Err(Error { kind: ErrorKind::InvalidToken(token.value, vec![ExpectedToken::Identifier]), meta: token.meta, }), } } pub fn expect(&mut self, frontend: &mut Frontend, value: TokenValue) -> Result { let token = self.bump(frontend)?; if token.value != value { Err(Error { kind: ErrorKind::InvalidToken(token.value, vec![value.into()]), meta: token.meta, }) } else { Ok(token) } } pub fn next(&mut self, frontend: &mut Frontend) -> Option { loop { if let Some(token) = self.backtracked_token.take() { self.last_meta = token.meta; break Some(token); } let res = self.lexer.next()?; match res.kind { LexerResultKind::Token(token) => { self.last_meta = token.meta; break Some(token); } LexerResultKind::Directive(directive) => { frontend.handle_directive(directive, res.meta) } LexerResultKind::Error(error) => frontend.errors.push(Error { kind: ErrorKind::PreprocessorError(error), meta: res.meta, }), } } } pub fn bump(&mut self, frontend: &mut Frontend) -> Result { self.next(frontend).ok_or(Error { kind: ErrorKind::EndOfFile, meta: self.last_meta, }) } /// Returns None on the end of the file rather than an error like other methods pub fn bump_if(&mut self, frontend: &mut Frontend, value: TokenValue) -> Option { if self.peek(frontend).filter(|t| t.value == value).is_some() { self.bump(frontend).ok() } else { None } } pub fn peek(&mut self, frontend: &mut Frontend) -> Option<&Token> { loop { if let Some(ref token) = self.backtracked_token { break Some(token); } match self.lexer.peek()?.kind { LexerResultKind::Token(_) => { let res = self.lexer.peek()?; match res.kind { LexerResultKind::Token(ref token) => break Some(token), _ => unreachable!(), } } LexerResultKind::Error(_) | LexerResultKind::Directive(_) => { let res = self.lexer.next()?; match res.kind { LexerResultKind::Directive(directive) => { frontend.handle_directive(directive, res.meta) } LexerResultKind::Error(error) => frontend.errors.push(Error { kind: ErrorKind::PreprocessorError(error), meta: res.meta, }), LexerResultKind::Token(_) => unreachable!(), } } } } } pub fn expect_peek(&mut self, frontend: &mut Frontend) -> Result<&Token> { let meta = self.last_meta; self.peek(frontend).ok_or(Error { kind: ErrorKind::EndOfFile, meta, }) } pub fn parse(&mut self, frontend: &mut Frontend) -> Result { let mut module = Module::default(); // Body and expression arena for global initialization let mut ctx = Context::new(frontend, &mut module, false)?; while self.peek(frontend).is_some() { self.parse_external_declaration(frontend, &mut ctx)?; } // Add an `EntryPoint` to `parser.module` for `main`, if a // suitable overload exists. Error out if we can't find one. if let Some(declaration) = frontend.lookup_function.get("main") { for decl in declaration.overloads.iter() { if let FunctionKind::Call(handle) = decl.kind { if decl.defined && decl.parameters.is_empty() { frontend.add_entry_point(handle, ctx)?; return Ok(module); } } } } Err(Error { kind: ErrorKind::SemanticError("Missing entry point".into()), meta: Span::default(), }) } fn parse_uint_constant( &mut self, frontend: &mut Frontend, ctx: &mut Context, ) -> Result<(u32, Span)> { let (const_expr, meta) = self.parse_constant_expression(frontend, ctx.module)?; let res = ctx.module.to_ctx().eval_expr_to_u32(const_expr); let int = match res { Ok(value) => Ok(value), Err(U32EvalError::Negative) => Err(Error { kind: ErrorKind::SemanticError("int constant overflows".into()), meta, }), Err(U32EvalError::NonConst) => Err(Error { kind: ErrorKind::SemanticError("Expected a uint constant".into()), meta, }), }?; Ok((int, meta)) } fn parse_constant_expression( &mut self, frontend: &mut Frontend, module: &mut Module, ) -> Result<(Handle, Span)> { let mut ctx = Context::new(frontend, module, true)?; let mut stmt_ctx = ctx.stmt_ctx(); let expr = self.parse_conditional(frontend, &mut ctx, &mut stmt_ctx, None)?; let (root, meta) = ctx.lower_expect(stmt_ctx, frontend, expr, ExprPos::Rhs)?; Ok((root, meta)) } } impl Frontend { fn handle_directive(&mut self, directive: Directive, meta: Span) { let mut tokens = directive.tokens.into_iter(); match directive.kind { DirectiveKind::Version { is_first_directive } => { if !is_first_directive { self.errors.push(Error { kind: ErrorKind::SemanticError( "#version must occur first in shader".into(), ), meta, }) } match tokens.next() { Some(PPToken { value: PPTokenValue::Integer(int), location, }) => match int.value { 440 | 450 | 460 => self.meta.version = int.value as u16, _ => self.errors.push(Error { kind: ErrorKind::InvalidVersion(int.value), meta: location.into(), }), }, Some(PPToken { value, location }) => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( value, )), meta: location.into(), }), None => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine), meta, }), }; match tokens.next() { Some(PPToken { value: PPTokenValue::Ident(name), location, }) => match name.as_str() { "core" => self.meta.profile = Profile::Core, _ => self.errors.push(Error { kind: ErrorKind::InvalidProfile(name), meta: location.into(), }), }, Some(PPToken { value, location }) => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( value, )), meta: location.into(), }), None => {} }; if let Some(PPToken { value, location }) = tokens.next() { self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( value, )), meta: location.into(), }) } } DirectiveKind::Extension => { // TODO: Proper extension handling // - Checking for extension support in the compiler // - Handle behaviors such as warn // - Handle the all extension let name = match tokens.next() { Some(PPToken { value: PPTokenValue::Ident(name), .. }) => Some(name), Some(PPToken { value, location }) => { self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( value, )), meta: location.into(), }); None } None => { self.errors.push(Error { kind: ErrorKind::PreprocessorError( PreprocessorError::UnexpectedNewLine, ), meta, }); None } }; match tokens.next() { Some(PPToken { value: PPTokenValue::Punct(pp_rs::token::Punct::Colon), .. }) => {} Some(PPToken { value, location }) => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( value, )), meta: location.into(), }), None => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine), meta, }), }; match tokens.next() { Some(PPToken { value: PPTokenValue::Ident(behavior), location, }) => match behavior.as_str() { "require" | "enable" | "warn" | "disable" => { if let Some(name) = name { self.meta.extensions.insert(name); } } _ => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( PPTokenValue::Ident(behavior), )), meta: location.into(), }), }, Some(PPToken { value, location }) => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( value, )), meta: location.into(), }), None => self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedNewLine), meta, }), } if let Some(PPToken { value, location }) = tokens.next() { self.errors.push(Error { kind: ErrorKind::PreprocessorError(PreprocessorError::UnexpectedToken( value, )), meta: location.into(), }) } } DirectiveKind::Pragma => { // TODO: handle some common pragmas? } } } } pub struct DeclarationContext<'ctx, 'qualifiers, 'a> { qualifiers: TypeQualifiers<'qualifiers>, /// Indicates a global declaration external: bool, is_inside_loop: bool, ctx: &'ctx mut Context<'a>, } impl<'ctx, 'qualifiers, 'a> DeclarationContext<'ctx, 'qualifiers, 'a> { fn add_var( &mut self, frontend: &mut Frontend, ty: Handle, name: String, init: Option>, meta: Span, ) -> Result> { let decl = VarDeclaration { qualifiers: &mut self.qualifiers, ty, name: Some(name), init, meta, }; match self.external { true => { let global = frontend.add_global_var(self.ctx, decl)?; let expr = match global { GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle), GlobalOrConstant::Constant(handle) => Expression::Constant(handle), }; Ok(self.ctx.add_expression(expr, meta)?) } false => frontend.add_local_var(self.ctx, decl), } } }