diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/naga/src/front/glsl | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/naga/src/front/glsl')
-rw-r--r-- | third_party/rust/naga/src/front/glsl/ast.rs | 178 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/error.rs | 87 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/lex.rs | 380 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/lex_tests.rs | 346 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/mod.rs | 43 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/parser.rs | 1131 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/parser_tests.rs | 182 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/preprocess.rs | 152 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/preprocess_tests.rs | 218 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/token.rs | 8 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/types.rs | 120 | ||||
-rw-r--r-- | third_party/rust/naga/src/front/glsl/variables.rs | 185 |
12 files changed, 3030 insertions, 0 deletions
diff --git a/third_party/rust/naga/src/front/glsl/ast.rs b/third_party/rust/naga/src/front/glsl/ast.rs new file mode 100644 index 0000000000..58161ad7be --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/ast.rs @@ -0,0 +1,178 @@ +use super::error::ErrorKind; +use crate::{ + proc::{ResolveContext, Typifier}, + Arena, BinaryOperator, Binding, Expression, FastHashMap, Function, GlobalVariable, Handle, + Interpolation, LocalVariable, Module, ShaderStage, Statement, StorageClass, Type, +}; + +#[derive(Debug)] +pub struct Program { + pub version: u16, + pub profile: Profile, + pub shader_stage: ShaderStage, + pub entry: Option<String>, + pub lookup_function: FastHashMap<String, Handle<Function>>, + pub lookup_type: FastHashMap<String, Handle<Type>>, + pub lookup_global_variables: FastHashMap<String, Handle<GlobalVariable>>, + pub context: Context, + pub module: Module, +} + +impl Program { + pub fn new(shader_stage: ShaderStage, entry: &str) -> Program { + Program { + version: 0, + profile: Profile::Core, + shader_stage, + entry: Some(entry.to_string()), + lookup_function: FastHashMap::default(), + lookup_type: FastHashMap::default(), + lookup_global_variables: FastHashMap::default(), + context: Context { + expressions: Arena::<Expression>::new(), + local_variables: Arena::<LocalVariable>::new(), + scopes: vec![FastHashMap::default()], + lookup_global_var_exps: FastHashMap::default(), + typifier: Typifier::new(), + }, + module: Module::generate_empty(), + } + } + + pub fn binary_expr( + &mut self, + op: BinaryOperator, + left: &ExpressionRule, + right: &ExpressionRule, + ) -> ExpressionRule { + ExpressionRule::from_expression(self.context.expressions.append(Expression::Binary { + op, + left: left.expression, + right: right.expression, + })) + } + + pub fn resolve_type( + &mut self, + handle: Handle<crate::Expression>, + ) -> Result<&crate::TypeInner, ErrorKind> { + let functions = Arena::new(); //TODO + let arguments = Vec::new(); //TODO + let resolve_ctx = ResolveContext { + constants: &self.module.constants, + global_vars: &self.module.global_variables, + local_vars: &self.context.local_variables, + functions: &functions, + arguments: &arguments, + }; + match self.context.typifier.grow( + handle, + &self.context.expressions, + &mut self.module.types, + &resolve_ctx, + ) { + //TODO: better error report + Err(_) => Err(ErrorKind::SemanticError("Can't resolve type")), + Ok(()) => Ok(self.context.typifier.get(handle, &self.module.types)), + } + } +} + +#[derive(Debug)] +pub enum Profile { + Core, +} + +#[derive(Debug)] +pub struct Context { + pub expressions: Arena<Expression>, + pub local_variables: Arena<LocalVariable>, + //TODO: Find less allocation heavy representation + pub scopes: Vec<FastHashMap<String, Handle<Expression>>>, + pub lookup_global_var_exps: FastHashMap<String, Handle<Expression>>, + pub typifier: Typifier, +} + +impl Context { + pub fn lookup_local_var(&self, name: &str) -> Option<Handle<Expression>> { + for scope in self.scopes.iter().rev() { + if let Some(var) = scope.get(name) { + return Some(*var); + } + } + None + } + + #[cfg(feature = "glsl-validate")] + pub fn lookup_local_var_current_scope(&self, name: &str) -> Option<Handle<Expression>> { + if let Some(current) = self.scopes.last() { + current.get(name).cloned() + } else { + None + } + } + + pub fn clear_scopes(&mut self) { + self.scopes.clear(); + self.scopes.push(FastHashMap::default()); + } + + /// Add variable to current scope + pub fn add_local_var(&mut self, name: String, handle: Handle<Expression>) { + if let Some(current) = self.scopes.last_mut() { + (*current).insert(name, handle); + } + } + + /// Add new empty scope + pub fn push_scope(&mut self) { + self.scopes.push(FastHashMap::default()); + } + + pub fn remove_current_scope(&mut self) { + self.scopes.pop(); + } +} + +#[derive(Debug)] +pub struct ExpressionRule { + pub expression: Handle<Expression>, + pub statements: Vec<Statement>, + pub sampler: Option<Handle<Expression>>, +} + +impl ExpressionRule { + pub fn from_expression(expression: Handle<Expression>) -> ExpressionRule { + ExpressionRule { + expression, + statements: vec![], + sampler: None, + } + } +} + +#[derive(Debug)] +pub enum TypeQualifier { + StorageClass(StorageClass), + Binding(Binding), + Interpolation(Interpolation), +} + +#[derive(Debug)] +pub struct VarDeclaration { + pub type_qualifiers: Vec<TypeQualifier>, + pub ids_initializers: Vec<(Option<String>, Option<ExpressionRule>)>, + pub ty: Handle<Type>, +} + +#[derive(Debug)] +pub enum FunctionCallKind { + TypeConstructor(Handle<Type>), + Function(String), +} + +#[derive(Debug)] +pub struct FunctionCall { + pub kind: FunctionCallKind, + pub args: Vec<ExpressionRule>, +} diff --git a/third_party/rust/naga/src/front/glsl/error.rs b/third_party/rust/naga/src/front/glsl/error.rs new file mode 100644 index 0000000000..db5ffbe9f2 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/error.rs @@ -0,0 +1,87 @@ +use super::parser::Token; +use super::token::TokenMetadata; +use std::{fmt, io}; + +#[derive(Debug)] +pub enum ErrorKind { + EndOfFile, + InvalidInput, + InvalidProfile(TokenMetadata, String), + InvalidToken(Token), + InvalidVersion(TokenMetadata, i64), + IoError(io::Error), + ParserFail, + ParserStackOverflow, + NotImplemented(&'static str), + UnknownVariable(TokenMetadata, String), + UnknownField(TokenMetadata, String), + #[cfg(feature = "glsl-validate")] + VariableAlreadyDeclared(String), + #[cfg(feature = "glsl-validate")] + VariableNotAvailable(String), + ExpectedConstant, + SemanticError(&'static str), + PreprocessorError(String), +} + +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ErrorKind::EndOfFile => write!(f, "Unexpected end of file"), + ErrorKind::InvalidInput => write!(f, "InvalidInput"), + ErrorKind::InvalidProfile(meta, val) => { + write!(f, "Invalid profile {} at {:?}", val, meta) + } + ErrorKind::InvalidToken(token) => write!(f, "Invalid Token {:?}", token), + ErrorKind::InvalidVersion(meta, val) => { + write!(f, "Invalid version {} at {:?}", val, meta) + } + ErrorKind::IoError(error) => write!(f, "IO Error {}", error), + ErrorKind::ParserFail => write!(f, "Parser failed"), + ErrorKind::ParserStackOverflow => write!(f, "Parser stack overflow"), + ErrorKind::NotImplemented(msg) => write!(f, "Not implemented: {}", msg), + ErrorKind::UnknownVariable(meta, val) => { + write!(f, "Unknown variable {} at {:?}", val, meta) + } + ErrorKind::UnknownField(meta, val) => write!(f, "Unknown field {} at {:?}", val, meta), + #[cfg(feature = "glsl-validate")] + ErrorKind::VariableAlreadyDeclared(val) => { + write!(f, "Variable {} already decalred in current scope", val) + } + #[cfg(feature = "glsl-validate")] + ErrorKind::VariableNotAvailable(val) => { + write!(f, "Variable {} not available in this stage", val) + } + ErrorKind::ExpectedConstant => write!(f, "Expected constant"), + ErrorKind::SemanticError(msg) => write!(f, "Semantic error: {}", msg), + ErrorKind::PreprocessorError(val) => write!(f, "Preprocessor error: {}", val), + } + } +} + +#[derive(Debug)] +pub struct ParseError { + pub kind: ErrorKind, +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From<io::Error> for ParseError { + fn from(error: io::Error) -> Self { + ParseError { + kind: ErrorKind::IoError(error), + } + } +} + +impl From<ErrorKind> for ParseError { + fn from(kind: ErrorKind) -> Self { + ParseError { kind } + } +} + +impl std::error::Error for ParseError {} diff --git a/third_party/rust/naga/src/front/glsl/lex.rs b/third_party/rust/naga/src/front/glsl/lex.rs new file mode 100644 index 0000000000..57e86a4c2c --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/lex.rs @@ -0,0 +1,380 @@ +use super::parser::Token; +use super::{preprocess::LinePreProcessor, token::TokenMetadata, types::parse_type}; +use std::{iter::Enumerate, str::Lines}; + +fn _consume_str<'a>(input: &'a str, what: &str) -> Option<&'a str> { + if input.starts_with(what) { + Some(&input[what.len()..]) + } else { + None + } +} + +fn consume_any(input: &str, what: impl Fn(char) -> bool) -> (&str, &str, usize) { + let pos = input.find(|c| !what(c)).unwrap_or_else(|| input.len()); + let (o, i) = input.split_at(pos); + (o, i, pos) +} + +#[derive(Clone, Debug)] +pub struct Lexer<'a> { + lines: Enumerate<Lines<'a>>, + input: String, + line: usize, + offset: usize, + inside_comment: bool, + pub pp: LinePreProcessor, +} + +impl<'a> Lexer<'a> { + pub fn consume_token(&mut self) -> Option<Token> { + let start = self + .input + .find(|c: char| !c.is_whitespace()) + .unwrap_or_else(|| self.input.chars().count()); + let input = &self.input[start..]; + + let mut chars = input.chars(); + let cur = match chars.next() { + Some(c) => c, + None => { + self.input = self.input[start..].into(); + return None; + } + }; + let mut meta = TokenMetadata { + line: 0, + chars: start..start + 1, + }; + let mut consume_all = false; + let token = match cur { + ':' => Some(Token::Colon(meta)), + ';' => Some(Token::Semicolon(meta)), + ',' => Some(Token::Comma(meta)), + '.' => Some(Token::Dot(meta)), + + '(' => Some(Token::LeftParen(meta)), + ')' => Some(Token::RightParen(meta)), + '{' => Some(Token::LeftBrace(meta)), + '}' => Some(Token::RightBrace(meta)), + '[' => Some(Token::LeftBracket(meta)), + ']' => Some(Token::RightBracket(meta)), + '<' | '>' => { + let n1 = chars.next(); + let n2 = chars.next(); + match (cur, n1, n2) { + ('<', Some('<'), Some('=')) => { + meta.chars.end = start + 3; + Some(Token::LeftAssign(meta)) + } + ('>', Some('>'), Some('=')) => { + meta.chars.end = start + 3; + Some(Token::RightAssign(meta)) + } + ('<', Some('<'), _) => { + meta.chars.end = start + 2; + Some(Token::LeftOp(meta)) + } + ('>', Some('>'), _) => { + meta.chars.end = start + 2; + Some(Token::RightOp(meta)) + } + ('<', Some('='), _) => { + meta.chars.end = start + 2; + Some(Token::LeOp(meta)) + } + ('>', Some('='), _) => { + meta.chars.end = start + 2; + Some(Token::GeOp(meta)) + } + ('<', _, _) => Some(Token::LeftAngle(meta)), + ('>', _, _) => Some(Token::RightAngle(meta)), + _ => None, + } + } + '0'..='9' => { + let (number, _, pos) = consume_any(input, |c| (c >= '0' && c <= '9' || c == '.')); + if number.find('.').is_some() { + if ( + chars.next().map(|c| c.to_lowercase().next().unwrap()), + chars.next().map(|c| c.to_lowercase().next().unwrap()), + ) == (Some('l'), Some('f')) + { + meta.chars.end = start + pos + 2; + Some(Token::DoubleConstant((meta, number.parse().unwrap()))) + } else { + meta.chars.end = start + pos; + + Some(Token::FloatConstant((meta, number.parse().unwrap()))) + } + } else { + meta.chars.end = start + pos; + Some(Token::IntConstant((meta, number.parse().unwrap()))) + } + } + 'a'..='z' | 'A'..='Z' | '_' => { + let (word, _, pos) = consume_any(input, |c| c.is_ascii_alphanumeric() || c == '_'); + meta.chars.end = start + pos; + match word { + "layout" => Some(Token::Layout(meta)), + "in" => Some(Token::In(meta)), + "out" => Some(Token::Out(meta)), + "uniform" => Some(Token::Uniform(meta)), + "flat" => Some(Token::Interpolation((meta, crate::Interpolation::Flat))), + "noperspective" => { + Some(Token::Interpolation((meta, crate::Interpolation::Linear))) + } + "smooth" => Some(Token::Interpolation(( + meta, + crate::Interpolation::Perspective, + ))), + "centroid" => { + Some(Token::Interpolation((meta, crate::Interpolation::Centroid))) + } + "sample" => Some(Token::Interpolation((meta, crate::Interpolation::Sample))), + // values + "true" => Some(Token::BoolConstant((meta, true))), + "false" => Some(Token::BoolConstant((meta, false))), + // jump statements + "continue" => Some(Token::Continue(meta)), + "break" => Some(Token::Break(meta)), + "return" => Some(Token::Return(meta)), + "discard" => Some(Token::Discard(meta)), + // selection statements + "if" => Some(Token::If(meta)), + "else" => Some(Token::Else(meta)), + "switch" => Some(Token::Switch(meta)), + "case" => Some(Token::Case(meta)), + "default" => Some(Token::Default(meta)), + // iteration statements + "while" => Some(Token::While(meta)), + "do" => Some(Token::Do(meta)), + "for" => Some(Token::For(meta)), + // types + "void" => Some(Token::Void(meta)), + word => { + let token = match parse_type(word) { + Some(t) => Token::TypeName((meta, t)), + None => Token::Identifier((meta, String::from(word))), + }; + Some(token) + } + } + } + '+' | '-' | '&' | '|' | '^' => { + let next = chars.next(); + if next == Some(cur) { + meta.chars.end = start + 2; + match cur { + '+' => Some(Token::IncOp(meta)), + '-' => Some(Token::DecOp(meta)), + '&' => Some(Token::AndOp(meta)), + '|' => Some(Token::OrOp(meta)), + '^' => Some(Token::XorOp(meta)), + _ => None, + } + } else { + match next { + Some('=') => { + meta.chars.end = start + 2; + match cur { + '+' => Some(Token::AddAssign(meta)), + '-' => Some(Token::SubAssign(meta)), + '&' => Some(Token::AndAssign(meta)), + '|' => Some(Token::OrAssign(meta)), + '^' => Some(Token::XorAssign(meta)), + _ => None, + } + } + _ => match cur { + '+' => Some(Token::Plus(meta)), + '-' => Some(Token::Dash(meta)), + '&' => Some(Token::Ampersand(meta)), + '|' => Some(Token::VerticalBar(meta)), + '^' => Some(Token::Caret(meta)), + _ => None, + }, + } + } + } + + '%' | '!' | '=' => match chars.next() { + Some('=') => { + meta.chars.end = start + 2; + match cur { + '%' => Some(Token::ModAssign(meta)), + '!' => Some(Token::NeOp(meta)), + '=' => Some(Token::EqOp(meta)), + _ => None, + } + } + _ => match cur { + '%' => Some(Token::Percent(meta)), + '!' => Some(Token::Bang(meta)), + '=' => Some(Token::Equal(meta)), + _ => None, + }, + }, + + '*' => match chars.next() { + Some('=') => { + meta.chars.end = start + 2; + Some(Token::MulAssign(meta)) + } + Some('/') => { + meta.chars.end = start + 2; + Some(Token::CommentEnd((meta, ()))) + } + _ => Some(Token::Star(meta)), + }, + '/' => { + match chars.next() { + Some('=') => { + meta.chars.end = start + 2; + Some(Token::DivAssign(meta)) + } + Some('/') => { + // consume rest of line + consume_all = true; + None + } + Some('*') => { + meta.chars.end = start + 2; + Some(Token::CommentStart((meta, ()))) + } + _ => Some(Token::Slash(meta)), + } + } + '#' => { + if self.offset == 0 { + let mut input = chars.as_str(); + + // skip whitespace + let word_start = input + .find(|c: char| !c.is_whitespace()) + .unwrap_or_else(|| input.chars().count()); + input = &input[word_start..]; + + let (word, _, pos) = consume_any(input, |c| c.is_alphanumeric() || c == '_'); + meta.chars.end = start + word_start + 1 + pos; + match word { + "version" => Some(Token::Version(meta)), + w => Some(Token::Unknown((meta, w.into()))), + } + + //TODO: preprocessor + // if chars.next() == Some(cur) { + // (Token::TokenPasting, chars.as_str(), start, start + 2) + // } else { + // (Token::Preprocessor, input, start, start + 1) + // } + } else { + Some(Token::Unknown((meta, '#'.to_string()))) + } + } + '~' => Some(Token::Tilde(meta)), + '?' => Some(Token::Question(meta)), + ch => Some(Token::Unknown((meta, ch.to_string()))), + }; + if let Some(token) = token { + let skip_bytes = input + .chars() + .take(token.extra().chars.end - start) + .fold(0, |acc, c| acc + c.len_utf8()); + self.input = input[skip_bytes..].into(); + Some(token) + } else { + if consume_all { + self.input = "".into(); + } else { + self.input = self.input[start..].into(); + } + None + } + } + + pub fn new(input: &'a str) -> Self { + let mut lexer = Lexer { + lines: input.lines().enumerate(), + input: "".to_string(), + line: 0, + offset: 0, + inside_comment: false, + pp: LinePreProcessor::new(), + }; + lexer.next_line(); + lexer + } + + fn next_line(&mut self) -> bool { + if let Some((line, input)) = self.lines.next() { + let mut input = String::from(input); + + while input.ends_with('\\') { + if let Some((_, next)) = self.lines.next() { + input.pop(); + input.push_str(next); + } else { + break; + } + } + + if let Ok(processed) = self.pp.process_line(&input) { + self.input = processed.unwrap_or_default(); + self.line = line; + self.offset = 0; + true + } else { + //TODO: handle preprocessor error + false + } + } else { + false + } + } + + #[must_use] + pub fn next(&mut self) -> Option<Token> { + let token = self.consume_token(); + + if let Some(mut token) = token { + let meta = token.extra_mut(); + let end = meta.chars.end; + meta.line = self.line; + meta.chars.start += self.offset; + meta.chars.end += self.offset; + self.offset += end; + if !self.inside_comment { + match token { + Token::CommentStart(_) => { + self.inside_comment = true; + self.next() + } + _ => Some(token), + } + } else { + if let Token::CommentEnd(_) = token { + self.inside_comment = false; + } + self.next() + } + } else { + if !self.next_line() { + return None; + } + self.next() + } + } + + // #[must_use] + // pub fn peek(&mut self) -> Option<Token> { + // self.clone().next() + // } +} + +impl<'a> Iterator for Lexer<'a> { + type Item = Token; + fn next(&mut self) -> Option<Self::Item> { + self.next() + } +} diff --git a/third_party/rust/naga/src/front/glsl/lex_tests.rs b/third_party/rust/naga/src/front/glsl/lex_tests.rs new file mode 100644 index 0000000000..fde7fc9790 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/lex_tests.rs @@ -0,0 +1,346 @@ +use super::{lex::Lexer, parser::Token::*, token::TokenMetadata}; + +#[test] +fn tokens() { + // line comments + let mut lex = Lexer::new("void main // myfunction\n//()\n{}"); + assert_eq!( + lex.next().unwrap(), + Void(TokenMetadata { + line: 0, + chars: 0..4 + }) + ); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 5..9 + }, + "main".into() + )) + ); + assert_eq!( + lex.next().unwrap(), + LeftBrace(TokenMetadata { + line: 2, + chars: 0..1 + }) + ); + assert_eq!( + lex.next().unwrap(), + RightBrace(TokenMetadata { + line: 2, + chars: 1..2 + }) + ); + assert_eq!(lex.next(), None); + + // multi line comment + let mut lex = Lexer::new("void main /* comment [] {}\n/**\n{}*/{}"); + assert_eq!( + lex.next().unwrap(), + Void(TokenMetadata { + line: 0, + chars: 0..4 + }) + ); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 5..9 + }, + "main".into() + )) + ); + assert_eq!( + lex.next().unwrap(), + LeftBrace(TokenMetadata { + line: 2, + chars: 4..5 + }) + ); + assert_eq!( + lex.next().unwrap(), + RightBrace(TokenMetadata { + line: 2, + chars: 5..6 + }) + ); + assert_eq!(lex.next(), None); + + // identifiers + let mut lex = Lexer::new("id123_OK 92No æNoø No¾ No好"); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 0..8 + }, + "id123_OK".into() + )) + ); + assert_eq!( + lex.next().unwrap(), + IntConstant(( + TokenMetadata { + line: 0, + chars: 9..11 + }, + 92 + )) + ); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 11..13 + }, + "No".into() + )) + ); + assert_eq!( + lex.next().unwrap(), + Unknown(( + TokenMetadata { + line: 0, + chars: 14..15 + }, + 'æ'.to_string() + )) + ); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 15..17 + }, + "No".into() + )) + ); + assert_eq!( + lex.next().unwrap(), + Unknown(( + TokenMetadata { + line: 0, + chars: 17..18 + }, + 'ø'.to_string() + )) + ); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 19..21 + }, + "No".into() + )) + ); + assert_eq!( + lex.next().unwrap(), + Unknown(( + TokenMetadata { + line: 0, + chars: 21..22 + }, + '¾'.to_string() + )) + ); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 23..25 + }, + "No".into() + )) + ); + assert_eq!( + lex.next().unwrap(), + Unknown(( + TokenMetadata { + line: 0, + chars: 25..26 + }, + '好'.to_string() + )) + ); + assert_eq!(lex.next(), None); + + // version + let mut lex = Lexer::new("#version 890 core"); + assert_eq!( + lex.next().unwrap(), + Version(TokenMetadata { + line: 0, + chars: 0..8 + }) + ); + assert_eq!( + lex.next().unwrap(), + IntConstant(( + TokenMetadata { + line: 0, + chars: 9..12 + }, + 890 + )) + ); + assert_eq!( + lex.next().unwrap(), + Identifier(( + TokenMetadata { + line: 0, + chars: 13..17 + }, + "core".into() + )) + ); + assert_eq!(lex.next(), None); + + // operators + let mut lex = Lexer::new("+ - * | & % / += -= *= |= &= %= /= ++ -- || && ^^"); + assert_eq!( + lex.next().unwrap(), + Plus(TokenMetadata { + line: 0, + chars: 0..1 + }) + ); + assert_eq!( + lex.next().unwrap(), + Dash(TokenMetadata { + line: 0, + chars: 2..3 + }) + ); + assert_eq!( + lex.next().unwrap(), + Star(TokenMetadata { + line: 0, + chars: 4..5 + }) + ); + assert_eq!( + lex.next().unwrap(), + VerticalBar(TokenMetadata { + line: 0, + chars: 6..7 + }) + ); + assert_eq!( + lex.next().unwrap(), + Ampersand(TokenMetadata { + line: 0, + chars: 8..9 + }) + ); + assert_eq!( + lex.next().unwrap(), + Percent(TokenMetadata { + line: 0, + chars: 10..11 + }) + ); + assert_eq!( + lex.next().unwrap(), + Slash(TokenMetadata { + line: 0, + chars: 12..13 + }) + ); + assert_eq!( + lex.next().unwrap(), + AddAssign(TokenMetadata { + line: 0, + chars: 14..16 + }) + ); + assert_eq!( + lex.next().unwrap(), + SubAssign(TokenMetadata { + line: 0, + chars: 17..19 + }) + ); + assert_eq!( + lex.next().unwrap(), + MulAssign(TokenMetadata { + line: 0, + chars: 20..22 + }) + ); + assert_eq!( + lex.next().unwrap(), + OrAssign(TokenMetadata { + line: 0, + chars: 23..25 + }) + ); + assert_eq!( + lex.next().unwrap(), + AndAssign(TokenMetadata { + line: 0, + chars: 26..28 + }) + ); + assert_eq!( + lex.next().unwrap(), + ModAssign(TokenMetadata { + line: 0, + chars: 29..31 + }) + ); + assert_eq!( + lex.next().unwrap(), + DivAssign(TokenMetadata { + line: 0, + chars: 32..34 + }) + ); + assert_eq!( + lex.next().unwrap(), + IncOp(TokenMetadata { + line: 0, + chars: 35..37 + }) + ); + assert_eq!( + lex.next().unwrap(), + DecOp(TokenMetadata { + line: 0, + chars: 38..40 + }) + ); + assert_eq!( + lex.next().unwrap(), + OrOp(TokenMetadata { + line: 0, + chars: 41..43 + }) + ); + assert_eq!( + lex.next().unwrap(), + AndOp(TokenMetadata { + line: 0, + chars: 44..46 + }) + ); + assert_eq!( + lex.next().unwrap(), + XorOp(TokenMetadata { + line: 0, + chars: 47..49 + }) + ); + assert_eq!(lex.next(), None); +} diff --git a/third_party/rust/naga/src/front/glsl/mod.rs b/third_party/rust/naga/src/front/glsl/mod.rs new file mode 100644 index 0000000000..4661011828 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/mod.rs @@ -0,0 +1,43 @@ +use crate::{FastHashMap, Module, ShaderStage}; + +mod lex; +#[cfg(test)] +mod lex_tests; + +mod preprocess; +#[cfg(test)] +mod preprocess_tests; + +mod ast; +use ast::Program; + +use lex::Lexer; +mod error; +use error::ParseError; +mod parser; +#[cfg(test)] +mod parser_tests; +mod token; +mod types; +mod variables; + +pub fn parse_str( + source: &str, + entry: &str, + stage: ShaderStage, + defines: FastHashMap<String, String>, +) -> Result<Module, ParseError> { + let mut program = Program::new(stage, entry); + + let mut lex = Lexer::new(source); + lex.pp.defines = defines; + + let mut parser = parser::Parser::new(&mut program); + + for token in lex { + parser.parse(token)?; + } + parser.end_of_input()?; + + Ok(program.module) +} diff --git a/third_party/rust/naga/src/front/glsl/parser.rs b/third_party/rust/naga/src/front/glsl/parser.rs new file mode 100644 index 0000000000..db4935b0b4 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/parser.rs @@ -0,0 +1,1131 @@ +#![allow(clippy::panic)] +use pomelo::pomelo; + +pomelo! { + //%verbose; + %include { + use super::super::{error::ErrorKind, token::*, ast::*}; + use crate::{proc::Typifier, Arena, BinaryOperator, Binding, Block, Constant, + ConstantInner, EntryPoint, Expression, FallThrough, FastHashMap, Function, GlobalVariable, Handle, Interpolation, + LocalVariable, MemberOrigin, SampleLevel, ScalarKind, Statement, StorageAccess, + StorageClass, StructMember, Type, TypeInner, UnaryOperator}; + } + %token #[derive(Debug)] #[cfg_attr(test, derive(PartialEq))] pub enum Token {}; + %parser pub struct Parser<'a> {}; + %extra_argument &'a mut Program; + %extra_token TokenMetadata; + %error ErrorKind; + %syntax_error { + match token { + Some(token) => Err(ErrorKind::InvalidToken(token)), + None => Err(ErrorKind::EndOfFile), + } + } + %parse_fail { + ErrorKind::ParserFail + } + %stack_overflow { + ErrorKind::ParserStackOverflow + } + + %type Unknown String; + %type CommentStart (); + %type CommentEnd (); + + %type Identifier String; + // constants + %type IntConstant i64; + %type UintConstant u64; + %type FloatConstant f32; + %type BoolConstant bool; + %type DoubleConstant f64; + %type String String; + // function + %type function_prototype Function; + %type function_declarator Function; + %type function_header Function; + %type function_definition Function; + + // statements + %type compound_statement Block; + %type compound_statement_no_new_scope Block; + %type statement_list Block; + %type statement Statement; + %type simple_statement Statement; + %type expression_statement Statement; + %type declaration_statement Statement; + %type jump_statement Statement; + %type iteration_statement Statement; + %type selection_statement Statement; + %type switch_statement_list Vec<(Option<i32>, Block, Option<FallThrough>)>; + %type switch_statement (Option<i32>, Block, Option<FallThrough>); + %type for_init_statement Statement; + %type for_rest_statement (Option<ExpressionRule>, Option<ExpressionRule>); + %type condition_opt Option<ExpressionRule>; + + // expressions + %type unary_expression ExpressionRule; + %type postfix_expression ExpressionRule; + %type primary_expression ExpressionRule; + %type variable_identifier ExpressionRule; + + %type function_call ExpressionRule; + %type function_call_or_method FunctionCall; + %type function_call_generic FunctionCall; + %type function_call_header_no_parameters FunctionCall; + %type function_call_header_with_parameters FunctionCall; + %type function_call_header FunctionCall; + %type function_identifier FunctionCallKind; + + %type multiplicative_expression ExpressionRule; + %type additive_expression ExpressionRule; + %type shift_expression ExpressionRule; + %type relational_expression ExpressionRule; + %type equality_expression ExpressionRule; + %type and_expression ExpressionRule; + %type exclusive_or_expression ExpressionRule; + %type inclusive_or_expression ExpressionRule; + %type logical_and_expression ExpressionRule; + %type logical_xor_expression ExpressionRule; + %type logical_or_expression ExpressionRule; + %type conditional_expression ExpressionRule; + + %type assignment_expression ExpressionRule; + %type assignment_operator BinaryOperator; + %type expression ExpressionRule; + %type constant_expression Handle<Constant>; + + %type initializer ExpressionRule; + + // declarations + %type declaration Option<VarDeclaration>; + %type init_declarator_list VarDeclaration; + %type single_declaration VarDeclaration; + %type layout_qualifier Binding; + %type layout_qualifier_id_list Vec<(String, u32)>; + %type layout_qualifier_id (String, u32); + %type type_qualifier Vec<TypeQualifier>; + %type single_type_qualifier TypeQualifier; + %type storage_qualifier StorageClass; + %type interpolation_qualifier Interpolation; + %type Interpolation Interpolation; + + // types + %type fully_specified_type (Vec<TypeQualifier>, Option<Handle<Type>>); + %type type_specifier Option<Handle<Type>>; + %type type_specifier_nonarray Option<Type>; + %type struct_specifier Type; + %type struct_declaration_list Vec<StructMember>; + %type struct_declaration Vec<StructMember>; + %type struct_declarator_list Vec<String>; + %type struct_declarator String; + + %type TypeName Type; + + // precedence + %right Else; + + root ::= version_pragma translation_unit; + version_pragma ::= Version IntConstant(V) Identifier?(P) { + match V.1 { + 440 => (), + 450 => (), + 460 => (), + _ => return Err(ErrorKind::InvalidVersion(V.0, V.1)) + } + extra.version = V.1 as u16; + extra.profile = match P { + Some((meta, profile)) => { + match profile.as_str() { + "core" => Profile::Core, + _ => return Err(ErrorKind::InvalidProfile(meta, profile)) + } + }, + None => Profile::Core, + } + }; + + // expression + variable_identifier ::= Identifier(v) { + let var = extra.lookup_variable(&v.1)?; + match var { + Some(expression) => { + ExpressionRule::from_expression(expression) + }, + None => { + return Err(ErrorKind::UnknownVariable(v.0, v.1)); + } + } + } + + primary_expression ::= variable_identifier; + primary_expression ::= IntConstant(i) { + let ty = extra.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Sint, + width: 4, + } + }); + let ch = extra.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + ty, + inner: ConstantInner::Sint(i.1) + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + // primary_expression ::= UintConstant; + primary_expression ::= FloatConstant(f) { + let ty = extra.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Float, + width: 4, + } + }); + let ch = extra.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + ty, + inner: ConstantInner::Float(f.1 as f64) + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + primary_expression ::= BoolConstant(b) { + let ty = extra.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Bool, + width: 4, + } + }); + let ch = extra.module.constants.fetch_or_append(Constant { + name: None, + specialization: None, + ty, + inner: ConstantInner::Bool(b.1) + }); + ExpressionRule::from_expression( + extra.context.expressions.append(Expression::Constant(ch)) + ) + } + // primary_expression ::= DoubleConstant; + primary_expression ::= LeftParen expression(e) RightParen { + e + } + + postfix_expression ::= primary_expression; + postfix_expression ::= postfix_expression LeftBracket integer_expression RightBracket { + //TODO + return Err(ErrorKind::NotImplemented("[]")) + } + postfix_expression ::= function_call; + postfix_expression ::= postfix_expression(e) Dot Identifier(i) /* FieldSelection in spec */ { + //TODO: how will this work as l-value? + let expression = extra.field_selection(e.expression, &*i.1, i.0)?; + ExpressionRule { expression, statements: e.statements, sampler: None } + } + postfix_expression ::= postfix_expression(pe) IncOp { + //TODO + return Err(ErrorKind::NotImplemented("post++")) + } + postfix_expression ::= postfix_expression(pe) DecOp { + //TODO + return Err(ErrorKind::NotImplemented("post--")) + } + + integer_expression ::= expression; + + function_call ::= function_call_or_method(fc) { + match fc.kind { + FunctionCallKind::TypeConstructor(ty) => { + let h = if fc.args.len() == 1 { + let kind = extra.module.types[ty].inner + .scalar_kind() + .ok_or(ErrorKind::SemanticError("Can only cast to scalar or vector"))?; + extra.context.expressions.append(Expression::As { + kind, + expr: fc.args[0].expression, + convert: true, + }) + } else { + extra.context.expressions.append(Expression::Compose { + ty, + components: fc.args.iter().map(|a| a.expression).collect(), + }) + }; + ExpressionRule { + expression: h, + statements: fc.args.into_iter().map(|a| a.statements).flatten().collect(), + sampler: None + } + } + FunctionCallKind::Function(name) => { + match name.as_str() { + "sampler2D" => { + //TODO: check args len + ExpressionRule{ + expression: fc.args[0].expression, + sampler: Some(fc.args[1].expression), + statements: fc.args.into_iter().map(|a| a.statements).flatten().collect(), + } + } + "texture" => { + //TODO: check args len + if let Some(sampler) = fc.args[0].sampler { + ExpressionRule{ + expression: extra.context.expressions.append(Expression::ImageSample { + image: fc.args[0].expression, + sampler, + coordinate: fc.args[1].expression, + level: SampleLevel::Auto, + depth_ref: None, + }), + sampler: None, + statements: fc.args.into_iter().map(|a| a.statements).flatten().collect(), + } + } else { + return Err(ErrorKind::SemanticError("Bad call to texture")); + } + } + _ => { return Err(ErrorKind::NotImplemented("Function call")); } + } + } + } + } + function_call_or_method ::= function_call_generic; + function_call_generic ::= function_call_header_with_parameters(h) RightParen { + h + } + function_call_generic ::= function_call_header_no_parameters(h) RightParen { + h + } + function_call_header_no_parameters ::= function_call_header(h) Void { + h + } + function_call_header_no_parameters ::= function_call_header; + function_call_header_with_parameters ::= function_call_header(mut h) assignment_expression(ae) { + h.args.push(ae); + h + } + function_call_header_with_parameters ::= function_call_header_with_parameters(mut h) Comma assignment_expression(ae) { + h.args.push(ae); + h + } + function_call_header ::= function_identifier(i) LeftParen { + FunctionCall { + kind: i, + args: vec![], + } + } + + // Grammar Note: Constructors look like functions, but lexical analysis recognized most of them as + // keywords. They are now recognized through “type_specifier”. + function_identifier ::= type_specifier(t) { + if let Some(ty) = t { + FunctionCallKind::TypeConstructor(ty) + } else { + return Err(ErrorKind::NotImplemented("bad type ctor")) + } + } + + //TODO + // Methods (.length), subroutine array calls, and identifiers are recognized through postfix_expression. + // function_identifier ::= postfix_expression(e) { + // FunctionCallKind::Function(e.expression) + // } + + // Simplification of above + function_identifier ::= Identifier(i) { + FunctionCallKind::Function(i.1) + } + + + unary_expression ::= postfix_expression; + + unary_expression ::= IncOp unary_expression { + //TODO + return Err(ErrorKind::NotImplemented("++pre")) + } + unary_expression ::= DecOp unary_expression { + //TODO + return Err(ErrorKind::NotImplemented("--pre")) + } + unary_expression ::= unary_operator unary_expression { + //TODO + return Err(ErrorKind::NotImplemented("unary_op")) + } + + unary_operator ::= Plus; + unary_operator ::= Dash; + unary_operator ::= Bang; + unary_operator ::= Tilde; + multiplicative_expression ::= unary_expression; + multiplicative_expression ::= multiplicative_expression(left) Star unary_expression(right) { + extra.binary_expr(BinaryOperator::Multiply, &left, &right) + } + multiplicative_expression ::= multiplicative_expression(left) Slash unary_expression(right) { + extra.binary_expr(BinaryOperator::Divide, &left, &right) + } + multiplicative_expression ::= multiplicative_expression(left) Percent unary_expression(right) { + extra.binary_expr(BinaryOperator::Modulo, &left, &right) + } + additive_expression ::= multiplicative_expression; + additive_expression ::= additive_expression(left) Plus multiplicative_expression(right) { + extra.binary_expr(BinaryOperator::Add, &left, &right) + } + additive_expression ::= additive_expression(left) Dash multiplicative_expression(right) { + extra.binary_expr(BinaryOperator::Subtract, &left, &right) + } + shift_expression ::= additive_expression; + shift_expression ::= shift_expression(left) LeftOp additive_expression(right) { + extra.binary_expr(BinaryOperator::ShiftLeft, &left, &right) + } + shift_expression ::= shift_expression(left) RightOp additive_expression(right) { + extra.binary_expr(BinaryOperator::ShiftRight, &left, &right) + } + relational_expression ::= shift_expression; + relational_expression ::= relational_expression(left) LeftAngle shift_expression(right) { + extra.binary_expr(BinaryOperator::Less, &left, &right) + } + relational_expression ::= relational_expression(left) RightAngle shift_expression(right) { + extra.binary_expr(BinaryOperator::Greater, &left, &right) + } + relational_expression ::= relational_expression(left) LeOp shift_expression(right) { + extra.binary_expr(BinaryOperator::LessEqual, &left, &right) + } + relational_expression ::= relational_expression(left) GeOp shift_expression(right) { + extra.binary_expr(BinaryOperator::GreaterEqual, &left, &right) + } + equality_expression ::= relational_expression; + equality_expression ::= equality_expression(left) EqOp relational_expression(right) { + extra.binary_expr(BinaryOperator::Equal, &left, &right) + } + equality_expression ::= equality_expression(left) NeOp relational_expression(right) { + extra.binary_expr(BinaryOperator::NotEqual, &left, &right) + } + and_expression ::= equality_expression; + and_expression ::= and_expression(left) Ampersand equality_expression(right) { + extra.binary_expr(BinaryOperator::And, &left, &right) + } + exclusive_or_expression ::= and_expression; + exclusive_or_expression ::= exclusive_or_expression(left) Caret and_expression(right) { + extra.binary_expr(BinaryOperator::ExclusiveOr, &left, &right) + } + inclusive_or_expression ::= exclusive_or_expression; + inclusive_or_expression ::= inclusive_or_expression(left) VerticalBar exclusive_or_expression(right) { + extra.binary_expr(BinaryOperator::InclusiveOr, &left, &right) + } + logical_and_expression ::= inclusive_or_expression; + logical_and_expression ::= logical_and_expression(left) AndOp inclusive_or_expression(right) { + extra.binary_expr(BinaryOperator::LogicalAnd, &left, &right) + } + logical_xor_expression ::= logical_and_expression; + logical_xor_expression ::= logical_xor_expression(left) XorOp logical_and_expression(right) { + let exp1 = extra.binary_expr(BinaryOperator::LogicalOr, &left, &right); + let exp2 = { + let tmp = extra.binary_expr(BinaryOperator::LogicalAnd, &left, &right).expression; + ExpressionRule::from_expression(extra.context.expressions.append(Expression::Unary { op: UnaryOperator::Not, expr: tmp })) + }; + extra.binary_expr(BinaryOperator::LogicalAnd, &exp1, &exp2) + } + logical_or_expression ::= logical_xor_expression; + logical_or_expression ::= logical_or_expression(left) OrOp logical_xor_expression(right) { + extra.binary_expr(BinaryOperator::LogicalOr, &left, &right) + } + + conditional_expression ::= logical_or_expression; + conditional_expression ::= logical_or_expression Question expression Colon assignment_expression(ae) { + //TODO: how to do ternary here in naga? + return Err(ErrorKind::NotImplemented("ternary exp")) + } + + assignment_expression ::= conditional_expression; + assignment_expression ::= unary_expression(mut pointer) assignment_operator(op) assignment_expression(value) { + pointer.statements.extend(value.statements); + match op { + BinaryOperator::Equal => { + pointer.statements.push(Statement::Store{ + pointer: pointer.expression, + value: value.expression + }); + pointer + }, + _ => { + let h = extra.context.expressions.append( + Expression::Binary{ + op, + left: pointer.expression, + right: value.expression, + } + ); + pointer.statements.push(Statement::Store{ + pointer: pointer.expression, + value: h, + }); + pointer + } + } + } + + assignment_operator ::= Equal { + BinaryOperator::Equal + } + assignment_operator ::= MulAssign { + BinaryOperator::Multiply + } + assignment_operator ::= DivAssign { + BinaryOperator::Divide + } + assignment_operator ::= ModAssign { + BinaryOperator::Modulo + } + assignment_operator ::= AddAssign { + BinaryOperator::Add + } + assignment_operator ::= SubAssign { + BinaryOperator::Subtract + } + assignment_operator ::= LeftAssign { + BinaryOperator::ShiftLeft + } + assignment_operator ::= RightAssign { + BinaryOperator::ShiftRight + } + assignment_operator ::= AndAssign { + BinaryOperator::And + } + assignment_operator ::= XorAssign { + BinaryOperator::ExclusiveOr + } + assignment_operator ::= OrAssign { + BinaryOperator::InclusiveOr + } + + expression ::= assignment_expression; + expression ::= expression(e) Comma assignment_expression(mut ae) { + ae.statements.extend(e.statements); + ExpressionRule { + expression: e.expression, + statements: ae.statements, + sampler: None, + } + } + + //TODO: properly handle constant expressions + // constant_expression ::= conditional_expression(e) { + // if let Expression::Constant(h) = extra.context.expressions[e] { + // h + // } else { + // return Err(ErrorKind::ExpectedConstant) + // } + // } + + // declaration + declaration ::= init_declarator_list(idl) Semicolon { + Some(idl) + } + + declaration ::= type_qualifier(t) Identifier(i) LeftBrace + struct_declaration_list(sdl) RightBrace Semicolon { + if i.1 == "gl_PerVertex" { + None + } else { + Some(VarDeclaration { + type_qualifiers: t, + ids_initializers: vec![(None, None)], + ty: extra.module.types.fetch_or_append(Type{ + name: Some(i.1), + inner: TypeInner::Struct { + members: sdl + } + }), + }) + } + } + + declaration ::= type_qualifier(t) Identifier(i1) LeftBrace + struct_declaration_list(sdl) RightBrace Identifier(i2) Semicolon { + Some(VarDeclaration { + type_qualifiers: t, + ids_initializers: vec![(Some(i2.1), None)], + ty: extra.module.types.fetch_or_append(Type{ + name: Some(i1.1), + inner: TypeInner::Struct { + members: sdl + } + }), + }) + } + + // declaration ::= type_qualifier(t) Identifier(i1) LeftBrace + // struct_declaration_list RightBrace Identifier(i2) array_specifier Semicolon; + + init_declarator_list ::= single_declaration; + init_declarator_list ::= init_declarator_list(mut idl) Comma Identifier(i) { + idl.ids_initializers.push((Some(i.1), None)); + idl + } + // init_declarator_list ::= init_declarator_list Comma Identifier array_specifier; + // init_declarator_list ::= init_declarator_list Comma Identifier array_specifier Equal initializer; + init_declarator_list ::= init_declarator_list(mut idl) Comma Identifier(i) Equal initializer(init) { + idl.ids_initializers.push((Some(i.1), Some(init))); + idl + } + + single_declaration ::= fully_specified_type(t) { + let ty = t.1.ok_or(ErrorKind::SemanticError("Empty type for declaration"))?; + + VarDeclaration { + type_qualifiers: t.0, + ids_initializers: vec![], + ty, + } + } + single_declaration ::= fully_specified_type(t) Identifier(i) { + let ty = t.1.ok_or(ErrorKind::SemanticError("Empty type for declaration"))?; + + VarDeclaration { + type_qualifiers: t.0, + ids_initializers: vec![(Some(i.1), None)], + ty, + } + } + // single_declaration ::= fully_specified_type Identifier array_specifier; + // single_declaration ::= fully_specified_type Identifier array_specifier Equal initializer; + single_declaration ::= fully_specified_type(t) Identifier(i) Equal initializer(init) { + let ty = t.1.ok_or(ErrorKind::SemanticError("Empty type for declaration"))?; + + VarDeclaration { + type_qualifiers: t.0, + ids_initializers: vec![(Some(i.1), Some(init))], + ty, + } + } + + fully_specified_type ::= type_specifier(t) { + (vec![], t) + } + fully_specified_type ::= type_qualifier(q) type_specifier(t) { + (q,t) + } + + interpolation_qualifier ::= Interpolation((_, i)) { + i + } + + layout_qualifier ::= Layout LeftParen layout_qualifier_id_list(l) RightParen { + if let Some(&(_, loc)) = l.iter().find(|&q| q.0.as_str() == "location") { + Binding::Location(loc) + } else if let Some(&(_, binding)) = l.iter().find(|&q| q.0.as_str() == "binding") { + let group = if let Some(&(_, set)) = l.iter().find(|&q| q.0.as_str() == "set") { + set + } else { + 0 + }; + Binding::Resource{ group, binding } + } else { + return Err(ErrorKind::NotImplemented("unsupported layout qualifier(s)")); + } + } + layout_qualifier_id_list ::= layout_qualifier_id(lqi) { + vec![lqi] + } + layout_qualifier_id_list ::= layout_qualifier_id_list(mut l) Comma layout_qualifier_id(lqi) { + l.push(lqi); + l + } + layout_qualifier_id ::= Identifier(i) { + (i.1, 0) + } + //TODO: handle full constant_expression instead of IntConstant + layout_qualifier_id ::= Identifier(i) Equal IntConstant(ic) { + (i.1, ic.1 as u32) + } + // layout_qualifier_id ::= Shared; + + // precise_qualifier ::= Precise; + + type_qualifier ::= single_type_qualifier(t) { + vec![t] + } + type_qualifier ::= type_qualifier(mut l) single_type_qualifier(t) { + l.push(t); + l + } + + single_type_qualifier ::= storage_qualifier(s) { + TypeQualifier::StorageClass(s) + } + single_type_qualifier ::= layout_qualifier(l) { + TypeQualifier::Binding(l) + } + // single_type_qualifier ::= precision_qualifier; + single_type_qualifier ::= interpolation_qualifier(i) { + TypeQualifier::Interpolation(i) + } + // single_type_qualifier ::= invariant_qualifier; + // single_type_qualifier ::= precise_qualifier; + + // storage_qualifier ::= Const + // storage_qualifier ::= InOut; + storage_qualifier ::= In { + StorageClass::Input + } + storage_qualifier ::= Out { + StorageClass::Output + } + // storage_qualifier ::= Centroid; + // storage_qualifier ::= Patch; + // storage_qualifier ::= Sample; + storage_qualifier ::= Uniform { + StorageClass::Uniform + } + //TODO: other storage qualifiers + + type_specifier ::= type_specifier_nonarray(t) { + t.map(|t| { + let name = t.name.clone(); + let handle = extra.module.types.fetch_or_append(t); + if let Some(name) = name { + extra.lookup_type.insert(name, handle); + } + handle + }) + } + //TODO: array + + type_specifier_nonarray ::= Void { + None + } + type_specifier_nonarray ::= TypeName(t) { + Some(t.1) + }; + type_specifier_nonarray ::= struct_specifier(s) { + Some(s) + } + + // struct + struct_specifier ::= Struct Identifier(i) LeftBrace struct_declaration_list RightBrace { + Type{ + name: Some(i.1), + inner: TypeInner::Struct { + members: vec![] + } + } + } + //struct_specifier ::= Struct LeftBrace struct_declaration_list RightBrace; + + struct_declaration_list ::= struct_declaration(sd) { + sd + } + struct_declaration_list ::= struct_declaration_list(mut sdl) struct_declaration(sd) { + sdl.extend(sd); + sdl + } + + struct_declaration ::= type_specifier(t) struct_declarator_list(sdl) Semicolon { + if let Some(ty) = t { + sdl.iter().map(|name| StructMember { + name: Some(name.clone()), + origin: MemberOrigin::Empty, + ty, + }).collect() + } else { + return Err(ErrorKind::SemanticError("Struct member can't be void")) + } + } + //struct_declaration ::= type_qualifier type_specifier struct_declarator_list Semicolon; + + struct_declarator_list ::= struct_declarator(sd) { + vec![sd] + } + struct_declarator_list ::= struct_declarator_list(mut sdl) Comma struct_declarator(sd) { + sdl.push(sd); + sdl + } + + struct_declarator ::= Identifier(i) { + i.1 + } + //struct_declarator ::= Identifier array_specifier; + + + initializer ::= assignment_expression; + // initializer ::= LeftBrace initializer_list RightBrace; + // initializer ::= LeftBrace initializer_list Comma RightBrace; + + // initializer_list ::= initializer; + // initializer_list ::= initializer_list Comma initializer; + + declaration_statement ::= declaration(d) { + let mut statements = Vec::<Statement>::new(); + // local variables + if let Some(d) = d { + for (id, initializer) in d.ids_initializers { + let id = id.ok_or(ErrorKind::SemanticError("local var must be named"))?; + // check if already declared in current scope + #[cfg(feature = "glsl-validate")] + { + if extra.context.lookup_local_var_current_scope(&id).is_some() { + return Err(ErrorKind::VariableAlreadyDeclared(id)) + } + } + let mut init_exp: Option<Handle<Expression>> = None; + let localVar = extra.context.local_variables.append( + LocalVariable { + name: Some(id.clone()), + ty: d.ty, + init: initializer.map(|i| { + statements.extend(i.statements); + if let Expression::Constant(constant) = extra.context.expressions[i.expression] { + Some(constant) + } else { + init_exp = Some(i.expression); + None + } + }).flatten(), + } + ); + let exp = extra.context.expressions.append(Expression::LocalVariable(localVar)); + extra.context.add_local_var(id, exp); + + if let Some(value) = init_exp { + statements.push( + Statement::Store { + pointer: exp, + value, + } + ); + } + } + }; + match statements.len() { + 1 => statements.remove(0), + _ => Statement::Block(statements), + } + } + + // statement + statement ::= compound_statement(cs) { + Statement::Block(cs) + } + statement ::= simple_statement; + + simple_statement ::= declaration_statement; + simple_statement ::= expression_statement; + simple_statement ::= selection_statement; + simple_statement ::= jump_statement; + simple_statement ::= iteration_statement; + + + selection_statement ::= If LeftParen expression(e) RightParen statement(s1) Else statement(s2) { + Statement::If { + condition: e.expression, + accept: vec![s1], + reject: vec![s2], + } + } + + selection_statement ::= If LeftParen expression(e) RightParen statement(s) [Else] { + Statement::If { + condition: e.expression, + accept: vec![s], + reject: vec![], + } + } + + selection_statement ::= Switch LeftParen expression(e) RightParen LeftBrace switch_statement_list(ls) RightBrace { + let mut default = Vec::new(); + let mut cases = FastHashMap::default(); + for (v, s, ft) in ls { + if let Some(v) = v { + cases.insert(v, (s, ft)); + } else { + default.extend_from_slice(&s); + } + } + Statement::Switch { + selector: e.expression, + cases, + default, + } + } + + switch_statement_list ::= { + vec![] + } + switch_statement_list ::= switch_statement_list(mut ssl) switch_statement((v, sl, ft)) { + ssl.push((v, sl, ft)); + ssl + } + switch_statement ::= Case IntConstant(v) Colon statement_list(sl) { + let fallthrough = match sl.last() { + Some(Statement::Break) => None, + _ => Some(FallThrough), + }; + (Some(v.1 as i32), sl, fallthrough) + } + switch_statement ::= Default Colon statement_list(sl) { + let fallthrough = match sl.last() { + Some(Statement::Break) => Some(FallThrough), + _ => None, + }; + (None, sl, fallthrough) + } + + iteration_statement ::= While LeftParen expression(e) RightParen compound_statement_no_new_scope(sl) { + let mut body = Vec::with_capacity(sl.len() + 1); + body.push( + Statement::If { + condition: e.expression, + accept: vec![Statement::Break], + reject: vec![], + } + ); + body.extend_from_slice(&sl); + Statement::Loop { + body, + continuing: vec![], + } + } + + iteration_statement ::= Do compound_statement(sl) While LeftParen expression(e) RightParen { + let mut body = sl; + body.push( + Statement::If { + condition: e.expression, + accept: vec![Statement::Break], + reject: vec![], + } + ); + Statement::Loop { + body, + continuing: vec![], + } + } + + iteration_statement ::= For LeftParen for_init_statement(s_init) for_rest_statement((cond_e, loop_e)) RightParen compound_statement_no_new_scope(sl) { + let mut body = Vec::with_capacity(sl.len() + 2); + if let Some(cond_e) = cond_e { + body.push( + Statement::If { + condition: cond_e.expression, + accept: vec![Statement::Break], + reject: vec![], + } + ); + } + body.extend_from_slice(&sl); + if let Some(loop_e) = loop_e { + body.extend_from_slice(&loop_e.statements); + } + Statement::Block(vec![ + s_init, + Statement::Loop { + body, + continuing: vec![], + } + ]) + } + + for_init_statement ::= expression_statement; + for_init_statement ::= declaration_statement; + for_rest_statement ::= condition_opt(c) Semicolon { + (c, None) + } + for_rest_statement ::= condition_opt(c) Semicolon expression(e) { + (c, Some(e)) + } + + condition_opt ::= { + None + } + condition_opt ::= conditional_expression(c) { + Some(c) + } + + compound_statement ::= LeftBrace RightBrace { + vec![] + } + compound_statement ::= left_brace_scope statement_list(sl) RightBrace { + extra.context.remove_current_scope(); + sl + } + + // extra rule to add scope before statement_list + left_brace_scope ::= LeftBrace { + extra.context.push_scope(); + } + + + compound_statement_no_new_scope ::= LeftBrace RightBrace { + vec![] + } + compound_statement_no_new_scope ::= LeftBrace statement_list(sl) RightBrace { + sl + } + + statement_list ::= statement(s) { + vec![s] + } + statement_list ::= statement_list(mut ss) statement(s) { ss.push(s); ss } + + expression_statement ::= Semicolon { + Statement::Block(Vec::new()) + } + expression_statement ::= expression(mut e) Semicolon { + match e.statements.len() { + 1 => e.statements.remove(0), + _ => Statement::Block(e.statements), + } + } + + + + // function + function_prototype ::= function_declarator(f) RightParen { + // prelude, add global var expressions + for (var_handle, var) in extra.module.global_variables.iter() { + if let Some(name) = var.name.as_ref() { + let exp = extra.context.expressions.append( + Expression::GlobalVariable(var_handle) + ); + extra.context.lookup_global_var_exps.insert(name.clone(), exp); + } else { + let ty = &extra.module.types[var.ty]; + // anonymous structs + if let TypeInner::Struct { members } = &ty.inner { + let base = extra.context.expressions.append( + Expression::GlobalVariable(var_handle) + ); + for (idx, member) in members.iter().enumerate() { + if let Some(name) = member.name.as_ref() { + let exp = extra.context.expressions.append( + Expression::AccessIndex{ + base, + index: idx as u32, + } + ); + extra.context.lookup_global_var_exps.insert(name.clone(), exp); + } + } + } + } + } + f + } + function_declarator ::= function_header; + function_header ::= fully_specified_type(t) Identifier(n) LeftParen { + Function { + name: Some(n.1), + arguments: vec![], + return_type: t.1, + global_usage: vec![], + local_variables: Arena::<LocalVariable>::new(), + expressions: Arena::<Expression>::new(), + body: vec![], + } + } + + jump_statement ::= Continue Semicolon { + Statement::Continue + } + jump_statement ::= Break Semicolon { + Statement::Break + } + jump_statement ::= Return Semicolon { + Statement::Return { value: None } + } + jump_statement ::= Return expression(mut e) Semicolon { + let ret = Statement::Return{ value: Some(e.expression) }; + if !e.statements.is_empty() { + e.statements.push(ret); + Statement::Block(e.statements) + } else { + ret + } + } + jump_statement ::= Discard Semicolon { + Statement::Kill + } // Fragment shader only + + // Grammar Note: No 'goto'. Gotos are not supported. + + // misc + translation_unit ::= external_declaration; + translation_unit ::= translation_unit external_declaration; + + external_declaration ::= function_definition(f) { + if f.name == extra.entry { + let name = extra.entry.take().unwrap(); + extra.module.entry_points.insert( + (extra.shader_stage, name), + EntryPoint { + early_depth_test: None, + workgroup_size: [0; 3], //TODO + function: f, + }, + ); + } else { + let name = f.name.clone().unwrap(); + let handle = extra.module.functions.append(f); + extra.lookup_function.insert(name, handle); + } + } + external_declaration ::= declaration(d) { + if let Some(d) = d { + let class = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::StorageClass(sc) = tq { Some(*sc) } else { None } + }).ok_or(ErrorKind::SemanticError("Missing storage class for global var"))?; + + let binding = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::Binding(b) = tq { Some(b.clone()) } else { None } + }); + + let interpolation = d.type_qualifiers.iter().find_map(|tq| { + if let TypeQualifier::Interpolation(i) = tq { Some(*i) } else { None } + }); + + for (id, initializer) in d.ids_initializers { + let h = extra.module.global_variables.fetch_or_append( + GlobalVariable { + name: id.clone(), + class, + binding: binding.clone(), + ty: d.ty, + init: None, + interpolation, + storage_access: StorageAccess::empty(), //TODO + }, + ); + if let Some(id) = id { + extra.lookup_global_variables.insert(id, h); + } + } + } + } + + function_definition ::= function_prototype(mut f) compound_statement_no_new_scope(mut cs) { + std::mem::swap(&mut f.expressions, &mut extra.context.expressions); + std::mem::swap(&mut f.local_variables, &mut extra.context.local_variables); + extra.context.clear_scopes(); + extra.context.lookup_global_var_exps.clear(); + extra.context.typifier = Typifier::new(); + // make sure function ends with return + match cs.last() { + Some(Statement::Return {..}) => {} + _ => {cs.push(Statement::Return { value:None });} + } + f.body = cs; + f.fill_global_use(&extra.module.global_variables); + f + }; +} + +pub use parser::*; diff --git a/third_party/rust/naga/src/front/glsl/parser_tests.rs b/third_party/rust/naga/src/front/glsl/parser_tests.rs new file mode 100644 index 0000000000..ccdc657a78 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/parser_tests.rs @@ -0,0 +1,182 @@ +use super::ast::Program; +use super::error::ErrorKind; +use super::lex::Lexer; +use super::parser; +use crate::ShaderStage; + +fn parse_program(source: &str, stage: ShaderStage) -> Result<Program, ErrorKind> { + let mut program = Program::new(stage, ""); + let lex = Lexer::new(source); + let mut parser = parser::Parser::new(&mut program); + + for token in lex { + parser.parse(token)?; + } + parser.end_of_input()?; + Ok(program) +} + +#[test] +fn version() { + // invalid versions + assert_eq!( + format!( + "{:?}", + parse_program("#version 99000", ShaderStage::Vertex) + .err() + .unwrap() + ), + "InvalidVersion(TokenMetadata { line: 0, chars: 9..14 }, 99000)" + ); + + assert_eq!( + format!( + "{:?}", + parse_program("#version 449", ShaderStage::Vertex) + .err() + .unwrap() + ), + "InvalidVersion(TokenMetadata { line: 0, chars: 9..12 }, 449)" + ); + + assert_eq!( + format!( + "{:?}", + parse_program("#version 450 smart", ShaderStage::Vertex) + .err() + .unwrap() + ), + "InvalidProfile(TokenMetadata { line: 0, chars: 13..18 }, \"smart\")" + ); + + assert_eq!( + format!( + "{:?}", + parse_program("#version 450\nvoid f(){} #version 450", ShaderStage::Vertex) + .err() + .unwrap() + ), + "InvalidToken(Unknown((TokenMetadata { line: 1, chars: 11..12 }, \"#\")))" + ); + + // valid versions + let program = parse_program(" # version 450\nvoid main() {}", ShaderStage::Vertex).unwrap(); + assert_eq!( + format!("{:?}", (program.version, program.profile)), + "(450, Core)" + ); + + let program = parse_program("#version 450\nvoid main() {}", ShaderStage::Vertex).unwrap(); + assert_eq!( + format!("{:?}", (program.version, program.profile)), + "(450, Core)" + ); + + let program = parse_program("#version 450 core\nvoid main() {}", ShaderStage::Vertex).unwrap(); + assert_eq!( + format!("{:?}", (program.version, program.profile)), + "(450, Core)" + ); +} + +#[test] +fn control_flow() { + let _program = parse_program( + r#" + # version 450 + void main() { + if (true) { + return 1; + } else { + return 2; + } + } + "#, + ShaderStage::Vertex, + ) + .unwrap(); + + let _program = parse_program( + r#" + # version 450 + void main() { + if (true) { + return 1; + } + } + "#, + ShaderStage::Vertex, + ) + .unwrap(); + + let _program = parse_program( + r#" + # version 450 + void main() { + int x; + int y = 3; + switch (5) { + case 2: + x = 2; + case 5: + x = 5; + y = 2; + break; + default: + x = 0; + } + } + "#, + ShaderStage::Vertex, + ) + .unwrap(); + let _program = parse_program( + r#" + # version 450 + void main() { + int x = 0; + while(x < 5) { + x = x + 1; + } + do { + x = x - 1; + } while(x >= 4) + } + "#, + ShaderStage::Vertex, + ) + .unwrap(); + + let _program = parse_program( + r#" + # version 450 + void main() { + int x = 0; + for(int i = 0; i < 10;) { + x = x + 2; + } + return x; + } + "#, + ShaderStage::Vertex, + ) + .unwrap(); +} + +#[test] +fn textures() { + let _program = parse_program( + r#" + #version 450 + layout(location = 0) in vec2 v_uv; + layout(location = 0) out vec4 o_color; + layout(set = 1, binding = 1) uniform texture2D tex; + layout(set = 1, binding = 2) uniform sampler tex_sampler; + void main() { + o_color = texture(sampler2D(tex, tex_sampler), v_uv); + } + "#, + ShaderStage::Fragment, + ) + .unwrap(); +} diff --git a/third_party/rust/naga/src/front/glsl/preprocess.rs b/third_party/rust/naga/src/front/glsl/preprocess.rs new file mode 100644 index 0000000000..6050594719 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/preprocess.rs @@ -0,0 +1,152 @@ +use crate::FastHashMap; +use thiserror::Error; + +#[derive(Clone, Debug, Error)] +#[cfg_attr(test, derive(PartialEq))] +pub enum Error { + #[error("unmatched else")] + UnmatchedElse, + #[error("unmatched endif")] + UnmatchedEndif, + #[error("missing macro name")] + MissingMacro, +} + +#[derive(Clone, Debug)] +pub struct IfState { + true_branch: bool, + else_seen: bool, +} + +#[derive(Clone, Debug)] +pub struct LinePreProcessor { + pub defines: FastHashMap<String, String>, + if_stack: Vec<IfState>, + inside_comment: bool, + in_preprocess: bool, +} + +impl LinePreProcessor { + pub fn new() -> Self { + LinePreProcessor { + defines: FastHashMap::default(), + if_stack: vec![], + inside_comment: false, + in_preprocess: false, + } + } + + fn subst_defines(&self, input: &str) -> String { + //TODO: don't subst in commments, strings literals? + self.defines + .iter() + .fold(input.to_string(), |acc, (k, v)| acc.replace(k, v)) + } + + pub fn process_line(&mut self, line: &str) -> Result<Option<String>, Error> { + let mut skip = !self.if_stack.last().map(|i| i.true_branch).unwrap_or(true); + let mut inside_comment = self.inside_comment; + let mut in_preprocess = inside_comment && self.in_preprocess; + // single-line comment + let mut processed = line; + if let Some(pos) = line.find("//") { + processed = line.split_at(pos).0; + } + // multi-line comment + let mut processed_string: String; + loop { + if inside_comment { + if let Some(pos) = processed.find("*/") { + processed = processed.split_at(pos + 2).1; + inside_comment = false; + self.inside_comment = false; + continue; + } + } else if let Some(pos) = processed.find("/*") { + if let Some(end_pos) = processed[pos + 2..].find("*/") { + // comment ends during this line + processed_string = processed.to_string(); + processed_string.replace_range(pos..pos + end_pos + 4, ""); + processed = &processed_string; + } else { + processed = processed.split_at(pos).0; + inside_comment = true; + } + continue; + } + break; + } + // strip leading whitespace + processed = processed.trim_start(); + if processed.starts_with('#') && !self.inside_comment { + let mut iter = processed[1..] + .trim_start() + .splitn(2, |c: char| c.is_whitespace()); + if let Some(directive) = iter.next() { + skip = true; + in_preprocess = true; + match directive { + "version" => { + skip = false; + } + "define" => { + let rest = iter.next().ok_or(Error::MissingMacro)?; + let pos = rest + .find(|c: char| !c.is_ascii_alphanumeric() && c != '_' && c != '(') + .unwrap_or_else(|| rest.len()); + let (key, mut value) = rest.split_at(pos); + value = value.trim(); + self.defines.insert(key.into(), self.subst_defines(value)); + } + "undef" => { + let rest = iter.next().ok_or(Error::MissingMacro)?; + let key = rest.trim(); + self.defines.remove(key); + } + "ifdef" => { + let rest = iter.next().ok_or(Error::MissingMacro)?; + let key = rest.trim(); + self.if_stack.push(IfState { + true_branch: self.defines.contains_key(key), + else_seen: false, + }); + } + "ifndef" => { + let rest = iter.next().ok_or(Error::MissingMacro)?; + let key = rest.trim(); + self.if_stack.push(IfState { + true_branch: !self.defines.contains_key(key), + else_seen: false, + }); + } + "else" => { + let if_state = self.if_stack.last_mut().ok_or(Error::UnmatchedElse)?; + if !if_state.else_seen { + // this is first else + if_state.true_branch = !if_state.true_branch; + if_state.else_seen = true; + } else { + return Err(Error::UnmatchedElse); + } + } + "endif" => { + self.if_stack.pop().ok_or(Error::UnmatchedEndif)?; + } + _ => {} + } + } + } + let res = if !skip && !self.inside_comment { + Ok(Some(self.subst_defines(&line))) + } else { + Ok(if in_preprocess && !self.in_preprocess { + Some("".to_string()) + } else { + None + }) + }; + self.in_preprocess = in_preprocess || skip; + self.inside_comment = inside_comment; + res + } +} diff --git a/third_party/rust/naga/src/front/glsl/preprocess_tests.rs b/third_party/rust/naga/src/front/glsl/preprocess_tests.rs new file mode 100644 index 0000000000..253a99935e --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/preprocess_tests.rs @@ -0,0 +1,218 @@ +use super::preprocess::{Error, LinePreProcessor}; +use std::{iter::Enumerate, str::Lines}; + +#[derive(Clone, Debug)] +pub struct PreProcessor<'a> { + lines: Enumerate<Lines<'a>>, + input: String, + line: usize, + offset: usize, + line_pp: LinePreProcessor, +} + +impl<'a> PreProcessor<'a> { + pub fn new(input: &'a str) -> Self { + let mut lexer = PreProcessor { + lines: input.lines().enumerate(), + input: "".to_string(), + line: 0, + offset: 0, + line_pp: LinePreProcessor::new(), + }; + lexer.next_line(); + lexer + } + + fn next_line(&mut self) -> bool { + if let Some((line, input)) = self.lines.next() { + let mut input = String::from(input); + + while input.ends_with('\\') { + if let Some((_, next)) = self.lines.next() { + input.pop(); + input.push_str(next); + } else { + break; + } + } + + self.input = input; + self.line = line; + self.offset = 0; + true + } else { + false + } + } + + pub fn process(&mut self) -> Result<String, Error> { + let mut res = String::new(); + loop { + let line = &self.line_pp.process_line(&self.input)?; + if let Some(line) = line { + res.push_str(line); + } + if !self.next_line() { + break; + } + if line.is_some() { + res.push_str("\n"); + } + } + Ok(res) + } +} + +#[test] +fn preprocess() { + // line continuation + let mut pp = PreProcessor::new( + "void main my_\ + func", + ); + assert_eq!(pp.process().unwrap(), "void main my_func"); + + // preserve #version + let mut pp = PreProcessor::new( + "#version 450 core\n\ + void main()", + ); + assert_eq!(pp.process().unwrap(), "#version 450 core\nvoid main()"); + + // simple define + let mut pp = PreProcessor::new( + "#define FOO 42 \n\ + fun=FOO", + ); + assert_eq!(pp.process().unwrap(), "\nfun=42"); + + // ifdef with else + let mut pp = PreProcessor::new( + "#define FOO\n\ + #ifdef FOO\n\ + foo=42\n\ + #endif\n\ + some=17\n\ + #ifdef BAR\n\ + bar=88\n\ + #else\n\ + mm=49\n\ + #endif\n\ + done=1", + ); + assert_eq!( + pp.process().unwrap(), + "\n\ + foo=42\n\ + \n\ + some=17\n\ + \n\ + mm=49\n\ + \n\ + done=1" + ); + + // nested ifdef/ifndef + let mut pp = PreProcessor::new( + "#define FOO\n\ + #define BOO\n\ + #ifdef FOO\n\ + foo=42\n\ + #ifdef BOO\n\ + boo=44\n\ + #endif\n\ + ifd=0\n\ + #ifndef XYZ\n\ + nxyz=8\n\ + #endif\n\ + #endif\n\ + some=17\n\ + #ifdef BAR\n\ + bar=88\n\ + #else\n\ + mm=49\n\ + #endif\n\ + done=1", + ); + assert_eq!( + pp.process().unwrap(), + "\n\ + foo=42\n\ + \n\ + boo=44\n\ + \n\ + ifd=0\n\ + \n\ + nxyz=8\n\ + \n\ + some=17\n\ + \n\ + mm=49\n\ + \n\ + done=1" + ); + + // undef + let mut pp = PreProcessor::new( + "#define FOO\n\ + #ifdef FOO\n\ + foo=42\n\ + #endif\n\ + some=17\n\ + #undef FOO\n\ + #ifdef FOO\n\ + foo=88\n\ + #else\n\ + nofoo=66\n\ + #endif\n\ + done=1", + ); + assert_eq!( + pp.process().unwrap(), + "\n\ + foo=42\n\ + \n\ + some=17\n\ + \n\ + nofoo=66\n\ + \n\ + done=1" + ); + + // single-line comment + let mut pp = PreProcessor::new( + "#define FOO 42//1234\n\ + fun=FOO", + ); + assert_eq!(pp.process().unwrap(), "\nfun=42"); + + // multi-line comments + let mut pp = PreProcessor::new( + "#define FOO 52/*/1234\n\ + #define FOO 88\n\ + end of comment*/ /* one more comment */ #define FOO 56\n\ + fun=FOO", + ); + assert_eq!(pp.process().unwrap(), "\nfun=56"); + + // unmatched endif + let mut pp = PreProcessor::new( + "#ifdef FOO\n\ + foo=42\n\ + #endif\n\ + #endif", + ); + assert_eq!(pp.process(), Err(Error::UnmatchedEndif)); + + // unmatched else + let mut pp = PreProcessor::new( + "#ifdef FOO\n\ + foo=42\n\ + #else\n\ + bar=88\n\ + #else\n\ + bad=true\n\ + #endif", + ); + assert_eq!(pp.process(), Err(Error::UnmatchedElse)); +} diff --git a/third_party/rust/naga/src/front/glsl/token.rs b/third_party/rust/naga/src/front/glsl/token.rs new file mode 100644 index 0000000000..7b849d361f --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/token.rs @@ -0,0 +1,8 @@ +use std::ops::Range; + +#[derive(Debug, Clone)] +#[cfg_attr(test, derive(PartialEq))] +pub struct TokenMetadata { + pub line: usize, + pub chars: Range<usize>, +} diff --git a/third_party/rust/naga/src/front/glsl/types.rs b/third_party/rust/naga/src/front/glsl/types.rs new file mode 100644 index 0000000000..715c0c7430 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/types.rs @@ -0,0 +1,120 @@ +use crate::{ScalarKind, Type, TypeInner, VectorSize}; + +pub fn parse_type(type_name: &str) -> Option<Type> { + match type_name { + "bool" => Some(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Bool, + width: 4, // https://stackoverflow.com/questions/9419781/what-is-the-size-of-glsl-boolean + }, + }), + "float" => Some(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Float, + width: 4, + }, + }), + "double" => Some(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Float, + width: 8, + }, + }), + "int" => Some(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Sint, + width: 4, + }, + }), + "uint" => Some(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Uint, + width: 4, + }, + }), + "texture2D" => Some(Type { + name: None, + inner: TypeInner::Image { + dim: crate::ImageDimension::D2, + arrayed: false, + class: crate::ImageClass::Sampled { + kind: ScalarKind::Float, + multi: false, + }, + }, + }), + "sampler" => Some(Type { + name: None, + inner: TypeInner::Sampler { comparison: false }, + }), + word => { + fn kind_width_parse(ty: &str) -> Option<(ScalarKind, u8)> { + Some(match ty { + "" => (ScalarKind::Float, 4), + "b" => (ScalarKind::Bool, 4), + "i" => (ScalarKind::Sint, 4), + "u" => (ScalarKind::Uint, 4), + "d" => (ScalarKind::Float, 8), + _ => return None, + }) + } + + fn size_parse(n: &str) -> Option<VectorSize> { + Some(match n { + "2" => VectorSize::Bi, + "3" => VectorSize::Tri, + "4" => VectorSize::Quad, + _ => return None, + }) + } + + let vec_parse = |word: &str| { + let mut iter = word.split("vec"); + + let kind = iter.next()?; + let size = iter.next()?; + let (kind, width) = kind_width_parse(kind)?; + let size = size_parse(size)?; + + Some(Type { + name: None, + inner: TypeInner::Vector { size, kind, width }, + }) + }; + + let mat_parse = |word: &str| { + let mut iter = word.split("mat"); + + let kind = iter.next()?; + let size = iter.next()?; + let (_, width) = kind_width_parse(kind)?; + + let (columns, rows) = if let Some(size) = size_parse(size) { + (size, size) + } else { + let mut iter = size.split('x'); + match (iter.next()?, iter.next()?, iter.next()) { + (col, row, None) => (size_parse(col)?, size_parse(row)?), + _ => return None, + } + }; + + Some(Type { + name: None, + inner: TypeInner::Matrix { + columns, + rows, + width, + }, + }) + }; + + vec_parse(word).or_else(|| mat_parse(word)) + } + } +} diff --git a/third_party/rust/naga/src/front/glsl/variables.rs b/third_party/rust/naga/src/front/glsl/variables.rs new file mode 100644 index 0000000000..9f76335685 --- /dev/null +++ b/third_party/rust/naga/src/front/glsl/variables.rs @@ -0,0 +1,185 @@ +use crate::{ + Binding, BuiltIn, Expression, GlobalVariable, Handle, ScalarKind, ShaderStage, StorageAccess, + StorageClass, Type, TypeInner, VectorSize, +}; + +use super::ast::*; +use super::error::ErrorKind; +use super::token::TokenMetadata; + +impl Program { + pub fn lookup_variable(&mut self, name: &str) -> Result<Option<Handle<Expression>>, ErrorKind> { + let mut expression: Option<Handle<Expression>> = None; + match name { + "gl_Position" => { + #[cfg(feature = "glsl-validate")] + match self.shader_stage { + ShaderStage::Vertex | ShaderStage::Fragment { .. } => {} + _ => { + return Err(ErrorKind::VariableNotAvailable(name.into())); + } + }; + let h = self + .module + .global_variables + .fetch_or_append(GlobalVariable { + name: Some(name.into()), + class: if self.shader_stage == ShaderStage::Vertex { + StorageClass::Output + } else { + StorageClass::Input + }, + binding: Some(Binding::BuiltIn(BuiltIn::Position)), + ty: self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Vector { + size: VectorSize::Quad, + kind: ScalarKind::Float, + width: 4, + }, + }), + init: None, + interpolation: None, + storage_access: StorageAccess::empty(), + }); + self.lookup_global_variables.insert(name.into(), h); + let exp = self + .context + .expressions + .append(Expression::GlobalVariable(h)); + self.context.lookup_global_var_exps.insert(name.into(), exp); + + expression = Some(exp); + } + "gl_VertexIndex" => { + #[cfg(feature = "glsl-validate")] + match self.shader_stage { + ShaderStage::Vertex => {} + _ => { + return Err(ErrorKind::VariableNotAvailable(name.into())); + } + }; + let h = self + .module + .global_variables + .fetch_or_append(GlobalVariable { + name: Some(name.into()), + class: StorageClass::Input, + binding: Some(Binding::BuiltIn(BuiltIn::VertexIndex)), + ty: self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Scalar { + kind: ScalarKind::Uint, + width: 4, + }, + }), + init: None, + interpolation: None, + storage_access: StorageAccess::empty(), + }); + self.lookup_global_variables.insert(name.into(), h); + let exp = self + .context + .expressions + .append(Expression::GlobalVariable(h)); + self.context.lookup_global_var_exps.insert(name.into(), exp); + + expression = Some(exp); + } + _ => {} + } + + if let Some(expression) = expression { + Ok(Some(expression)) + } else if let Some(local_var) = self.context.lookup_local_var(name) { + Ok(Some(local_var)) + } else if let Some(global_var) = self.context.lookup_global_var_exps.get(name) { + Ok(Some(*global_var)) + } else { + Ok(None) + } + } + + pub fn field_selection( + &mut self, + expression: Handle<Expression>, + name: &str, + meta: TokenMetadata, + ) -> Result<Handle<Expression>, ErrorKind> { + match *self.resolve_type(expression)? { + TypeInner::Struct { ref members } => { + let index = members + .iter() + .position(|m| m.name == Some(name.into())) + .ok_or_else(|| ErrorKind::UnknownField(meta, name.into()))?; + Ok(self.context.expressions.append(Expression::AccessIndex { + base: expression, + index: index as u32, + })) + } + // swizzles (xyzw, rgba, stpq) + TypeInner::Vector { size, kind, width } => { + let check_swizzle_components = |comps: &str| { + name.chars() + .map(|c| { + comps + .find(c) + .and_then(|i| if i < size as usize { Some(i) } else { None }) + }) + .fold(Some(Vec::<usize>::new()), |acc, cur| { + cur.and_then(|i| { + acc.map(|mut v| { + v.push(i); + v + }) + }) + }) + }; + + let indices = check_swizzle_components("xyzw") + .or_else(|| check_swizzle_components("rgba")) + .or_else(|| check_swizzle_components("stpq")); + + if let Some(v) = indices { + let components: Vec<Handle<Expression>> = v + .iter() + .map(|idx| { + self.context.expressions.append(Expression::AccessIndex { + base: expression, + index: *idx as u32, + }) + }) + .collect(); + if components.len() == 1 { + // only single element swizzle, like pos.y, just return that component + Ok(components[0]) + } else { + Ok(self.context.expressions.append(Expression::Compose { + ty: self.module.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Vector { + kind, + width, + size: match components.len() { + 2 => VectorSize::Bi, + 3 => VectorSize::Tri, + 4 => VectorSize::Quad, + _ => { + return Err(ErrorKind::SemanticError( + "Bad swizzle size", + )); + } + }, + }, + }), + components, + })) + } + } else { + Err(ErrorKind::SemanticError("Invalid swizzle for vector")) + } + } + _ => Err(ErrorKind::SemanticError("Can't lookup field on this type")), + } + } +} |