summaryrefslogtreecommitdiffstats
path: root/third_party/rust/naga/src/front/glsl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/naga/src/front/glsl
parentInitial commit. (diff)
downloadfirefox-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.rs178
-rw-r--r--third_party/rust/naga/src/front/glsl/error.rs87
-rw-r--r--third_party/rust/naga/src/front/glsl/lex.rs380
-rw-r--r--third_party/rust/naga/src/front/glsl/lex_tests.rs346
-rw-r--r--third_party/rust/naga/src/front/glsl/mod.rs43
-rw-r--r--third_party/rust/naga/src/front/glsl/parser.rs1131
-rw-r--r--third_party/rust/naga/src/front/glsl/parser_tests.rs182
-rw-r--r--third_party/rust/naga/src/front/glsl/preprocess.rs152
-rw-r--r--third_party/rust/naga/src/front/glsl/preprocess_tests.rs218
-rw-r--r--third_party/rust/naga/src/front/glsl/token.rs8
-rw-r--r--third_party/rust/naga/src/front/glsl/types.rs120
-rw-r--r--third_party/rust/naga/src/front/glsl/variables.rs185
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")),
+ }
+ }
+}