mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
[glsl-in] improved WIP parsing for declarations
This commit is contained in:
@@ -301,10 +301,12 @@ impl ExpressionRule {
|
||||
#[derive(Debug)]
|
||||
pub enum TypeQualifier {
|
||||
StorageQualifier(StorageQualifier),
|
||||
Interpolation(Interpolation),
|
||||
ResourceBinding(ResourceBinding),
|
||||
Binding(Binding),
|
||||
Interpolation(Interpolation),
|
||||
Sampling(Sampling),
|
||||
Layout(StructLayout),
|
||||
EarlyFragmentTests,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -337,7 +339,5 @@ pub enum StorageQualifier {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StructLayout {
|
||||
Binding(Binding),
|
||||
Resource(ResourceBinding),
|
||||
PushConstant,
|
||||
Std140,
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ pub enum ErrorKind {
|
||||
NotImplemented(&'static str),
|
||||
UnknownVariable(TokenMetadata, String),
|
||||
UnknownField(TokenMetadata, String),
|
||||
UnknownLayoutQualifier(TokenMetadata, String),
|
||||
#[cfg(feature = "glsl-validate")]
|
||||
VariableAlreadyDeclared(String),
|
||||
ExpectedConstant,
|
||||
@@ -45,6 +46,9 @@ impl fmt::Display for ErrorKind {
|
||||
ErrorKind::UnknownField(ref meta, ref val) => {
|
||||
write!(f, "Unknown field {} at {:?}", val, meta)
|
||||
}
|
||||
ErrorKind::UnknownLayoutQualifier(ref meta, ref val) => {
|
||||
write!(f, "Unknown layout qualifier name {} at {:?}", val, meta)
|
||||
}
|
||||
#[cfg(feature = "glsl-validate")]
|
||||
ErrorKind::VariableAlreadyDeclared(ref val) => {
|
||||
write!(f, "Variable {} already declared in current scope", val)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use super::{
|
||||
ast::{FunctionContext, Profile, StorageQualifier, TypeQualifier},
|
||||
ast::{FunctionContext, Profile, StorageQualifier, StructLayout, TypeQualifier},
|
||||
error::ErrorKind,
|
||||
lex::Lexer,
|
||||
token::{Token, TokenMetadata, TokenValue},
|
||||
Program,
|
||||
};
|
||||
use crate::{arena::Handle, ArraySize, Function, FunctionResult, StorageClass, Type, TypeInner};
|
||||
use std::iter::Peekable;
|
||||
use crate::{
|
||||
arena::Handle, ArraySize, Binding, Function, FunctionResult, ResourceBinding, StorageClass,
|
||||
Type, TypeInner,
|
||||
};
|
||||
use core::convert::TryFrom;
|
||||
use core::iter::Peekable;
|
||||
|
||||
type Result<T> = std::result::Result<T, ErrorKind>;
|
||||
|
||||
@@ -46,6 +50,15 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
self.lexer.next().ok_or(ErrorKind::EndOfFile)
|
||||
}
|
||||
|
||||
/// Returns None on the end of the file rather than an error like other methods
|
||||
fn bump_if(&mut self, value: TokenValue) -> Option<Token> {
|
||||
if self.lexer.peek().filter(|t| t.value == value).is_some() {
|
||||
self.bump().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_peek(&mut self) -> Result<&Token> {
|
||||
self.lexer.peek().ok_or(ErrorKind::EndOfFile)
|
||||
}
|
||||
@@ -146,7 +159,8 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
| TokenValue::In
|
||||
| TokenValue::Out
|
||||
| TokenValue::InOut
|
||||
| TokenValue::Uniform => true,
|
||||
| TokenValue::Uniform
|
||||
| TokenValue::Layout => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
@@ -154,8 +168,15 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
fn parse_type_qualifiers(&mut self) -> Result<Vec<TypeQualifier>> {
|
||||
let mut qualifiers = Vec::new();
|
||||
|
||||
if self.peek_type_qualifier() {
|
||||
while self.peek_type_qualifier() {
|
||||
let token = self.bump()?;
|
||||
|
||||
// Handle layout qualifiers outside the match since this can push multiple values
|
||||
if token.value == TokenValue::Layout {
|
||||
self.parse_layout_qualifier_id_list(&mut qualifiers)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
qualifiers.push(match token.value {
|
||||
TokenValue::Interpolation(i) => TypeQualifier::Interpolation(i),
|
||||
TokenValue::Const => TypeQualifier::StorageQualifier(StorageQualifier::Const),
|
||||
@@ -165,6 +186,7 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
TokenValue::Uniform => TypeQualifier::StorageQualifier(
|
||||
StorageQualifier::StorageClass(StorageClass::Uniform),
|
||||
),
|
||||
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
@@ -172,6 +194,105 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
Ok(qualifiers)
|
||||
}
|
||||
|
||||
fn parse_layout_qualifier_id_list(
|
||||
&mut self,
|
||||
qualifiers: &mut Vec<TypeQualifier>,
|
||||
) -> Result<()> {
|
||||
// We need both of these to produce a ResourceBinding
|
||||
let mut group = None;
|
||||
let mut binding = None;
|
||||
|
||||
self.expect(TokenValue::LeftParen)?;
|
||||
loop {
|
||||
self.parse_layout_qualifier_id(qualifiers, &mut group, &mut binding)?;
|
||||
|
||||
if self.bump_if(TokenValue::Comma).is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
self.expect(TokenValue::RightParen)?;
|
||||
|
||||
match (group, binding) {
|
||||
(Some(group), Some(binding)) => {
|
||||
qualifiers.push(TypeQualifier::ResourceBinding(ResourceBinding {
|
||||
group,
|
||||
binding,
|
||||
}))
|
||||
}
|
||||
// Produce an error if we have one of group or binding but not the other
|
||||
// TODO: include at least line info in errors?
|
||||
(Some(_), None) => {
|
||||
return Err(ErrorKind::SemanticError(
|
||||
"set specified with no binding".into(),
|
||||
))
|
||||
}
|
||||
(None, Some(_)) => {
|
||||
return Err(ErrorKind::SemanticError(
|
||||
"binding specified with no set".into(),
|
||||
))
|
||||
}
|
||||
(None, None) => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_layout_qualifier_id(
|
||||
&mut self,
|
||||
qualifiers: &mut Vec<TypeQualifier>,
|
||||
group: &mut Option<u32>,
|
||||
binding: &mut Option<u32>,
|
||||
) -> Result<()> {
|
||||
// layout_qualifier_id:
|
||||
// IDENTIFIER
|
||||
// IDENTIFIER EQUAL constant_expression
|
||||
// SHARED
|
||||
let token = self.bump()?;
|
||||
match token.value {
|
||||
TokenValue::Identifier(name) => {
|
||||
if self.bump_if(TokenValue::Assign).is_some() {
|
||||
let value = self.parse_constant_expression()?;
|
||||
|
||||
match name.as_str() {
|
||||
"location" => qualifiers.push(TypeQualifier::Binding(Binding::Location {
|
||||
// TODO: use better error here
|
||||
location: u32::try_from(value).map_err(|_| ErrorKind::InvalidInput)?,
|
||||
interpolation: None,
|
||||
sampling: None,
|
||||
})),
|
||||
// TODO: produce error when try_from fails
|
||||
"set" => *group = u32::try_from(value).ok(),
|
||||
"binding" => *binding = u32::try_from(value).ok(),
|
||||
_ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)),
|
||||
}
|
||||
} else {
|
||||
match name.as_str() {
|
||||
"std140" => qualifiers.push(TypeQualifier::Layout(StructLayout::Std140)),
|
||||
"early_fragment_tests" => {
|
||||
qualifiers.push(TypeQualifier::EarlyFragmentTests)
|
||||
}
|
||||
_ => return Err(ErrorKind::UnknownLayoutQualifier(token.meta, name)),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// TODO: handle Shared?
|
||||
_ => Err(ErrorKind::InvalidToken(token)),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: parse more than just integer literal
|
||||
fn parse_constant_expression(&mut self) -> Result<i64> {
|
||||
let token = self.bump()?;
|
||||
match token.value {
|
||||
TokenValue::IntConstant(value) => Ok(value),
|
||||
_ => Err(ErrorKind::InvalidToken(token)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_external_declaration(&mut self) -> Result<()> {
|
||||
if !self.parse_declaration(true)? {
|
||||
let token = self.bump()?;
|
||||
@@ -182,6 +303,7 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn peek_type_name(&mut self) -> bool {
|
||||
@@ -191,8 +313,17 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
})
|
||||
}
|
||||
|
||||
/// `external` wheter or not we are in a global or local context
|
||||
/// `external` whether or not we are in a global or local context
|
||||
fn parse_declaration(&mut self, external: bool) -> Result<bool> {
|
||||
//declaration:
|
||||
// function_prototype SEMICOLON
|
||||
// init_declarator_list SEMICOLON
|
||||
// PRECISION precision_qualifier type_specifier SEMICOLON
|
||||
// type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE SEMICOLON
|
||||
// type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON
|
||||
// type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER array_specifier SEMICOLON
|
||||
// type_qualifier SEMICOLON type_qualifier IDENTIFIER SEMICOLON
|
||||
// type_qualifier IDENTIFIER identifier_list SEMICOLON
|
||||
// TODO: Handle precision qualifiers
|
||||
if self.peek_type_qualifier() || self.peek_type_name() {
|
||||
// TODO: Use qualifiers
|
||||
@@ -236,7 +367,12 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
}
|
||||
}
|
||||
// Variable Declaration
|
||||
TokenValue::Semicolon => todo!(),
|
||||
TokenValue::Semicolon => {
|
||||
// TODO: are we supposed to consume the semicolon here
|
||||
self.bump()?;
|
||||
// TODO: use parsed type & qualifiers
|
||||
Ok(true)
|
||||
}
|
||||
TokenValue::Comma => todo!(),
|
||||
_ => Err(ErrorKind::InvalidToken(self.bump()?)),
|
||||
},
|
||||
@@ -276,8 +412,7 @@ impl<'source, 'program, 'options> Parser<'source, 'program, 'options> {
|
||||
|
||||
context.add_function_arg(Some(name), ty);
|
||||
|
||||
if self.expect_peek()?.value == TokenValue::Comma {
|
||||
self.bump()?;
|
||||
if self.bump_if(TokenValue::Comma).is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,41 @@ fn control_flow() {
|
||||
// .unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declarations() {
|
||||
let mut entry_points = crate::FastHashMap::default();
|
||||
entry_points.insert("".to_string(), ShaderStage::Fragment);
|
||||
|
||||
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;
|
||||
|
||||
layout(early_fragment_tests) in;
|
||||
"#,
|
||||
&entry_points,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// TODO: Reenable tests later
|
||||
// let _program = parse_program(
|
||||
// r#"
|
||||
// #version 450
|
||||
// layout(std140, set = 2, binding = 0)
|
||||
// uniform u_locals {
|
||||
// vec3 model_offs;
|
||||
// float load_time;
|
||||
// ivec4 atlas_offs;
|
||||
// };
|
||||
// "#,
|
||||
// &entry_points,
|
||||
// )
|
||||
// .unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn textures() {
|
||||
// TODO: Reenable tests later
|
||||
|
||||
Reference in New Issue
Block a user