Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
703
third-party/vendor/naga/src/front/wgsl/error.rs
vendored
Normal file
703
third-party/vendor/naga/src/front/wgsl/error.rs
vendored
Normal file
|
|
@ -0,0 +1,703 @@
|
|||
use crate::front::wgsl::parse::lexer::Token;
|
||||
use crate::proc::{Alignment, ResolveError};
|
||||
use crate::{SourceLocation, Span};
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use codespan_reporting::files::SimpleFile;
|
||||
use codespan_reporting::term;
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Range;
|
||||
use termcolor::{ColorChoice, NoColor, StandardStream};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ParseError {
|
||||
message: String,
|
||||
labels: Vec<(Span, Cow<'static, str>)>,
|
||||
notes: Vec<String>,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn labels(&self) -> impl Iterator<Item = (Span, &str)> + ExactSizeIterator + '_ {
|
||||
self.labels
|
||||
.iter()
|
||||
.map(|&(span, ref msg)| (span, msg.as_ref()))
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
fn diagnostic(&self) -> Diagnostic<()> {
|
||||
let diagnostic = Diagnostic::error()
|
||||
.with_message(self.message.to_string())
|
||||
.with_labels(
|
||||
self.labels
|
||||
.iter()
|
||||
.map(|label| {
|
||||
Label::primary((), label.0.to_range().unwrap())
|
||||
.with_message(label.1.to_string())
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.with_notes(
|
||||
self.notes
|
||||
.iter()
|
||||
.map(|note| format!("note: {note}"))
|
||||
.collect(),
|
||||
);
|
||||
diagnostic
|
||||
}
|
||||
|
||||
/// Emits a summary of the error to standard error stream.
|
||||
pub fn emit_to_stderr(&self, source: &str) {
|
||||
self.emit_to_stderr_with_path(source, "wgsl")
|
||||
}
|
||||
|
||||
/// Emits a summary of the error to standard error stream.
|
||||
pub fn emit_to_stderr_with_path(&self, source: &str, path: &str) {
|
||||
let files = SimpleFile::new(path, source);
|
||||
let config = codespan_reporting::term::Config::default();
|
||||
let writer = StandardStream::stderr(ColorChoice::Auto);
|
||||
term::emit(&mut writer.lock(), &config, &files, &self.diagnostic())
|
||||
.expect("cannot write error");
|
||||
}
|
||||
|
||||
/// Emits a summary of the error to a string.
|
||||
pub fn emit_to_string(&self, source: &str) -> String {
|
||||
self.emit_to_string_with_path(source, "wgsl")
|
||||
}
|
||||
|
||||
/// Emits a summary of the error to a string.
|
||||
pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String {
|
||||
let files = SimpleFile::new(path, source);
|
||||
let config = codespan_reporting::term::Config::default();
|
||||
let mut writer = NoColor::new(Vec::new());
|
||||
term::emit(&mut writer, &config, &files, &self.diagnostic()).expect("cannot write error");
|
||||
String::from_utf8(writer.into_inner()).unwrap()
|
||||
}
|
||||
|
||||
/// Returns a [`SourceLocation`] for the first label in the error message.
|
||||
pub fn location(&self, source: &str) -> Option<SourceLocation> {
|
||||
self.labels.get(0).map(|label| label.0.location(source))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ExpectedToken<'a> {
|
||||
Token(Token<'a>),
|
||||
Identifier,
|
||||
Number,
|
||||
Integer,
|
||||
/// Expected: constant, parenthesized expression, identifier
|
||||
PrimaryExpression,
|
||||
/// Expected: assignment, increment/decrement expression
|
||||
Assignment,
|
||||
/// Expected: 'case', 'default', '}'
|
||||
SwitchItem,
|
||||
/// Expected: ',', ')'
|
||||
WorkgroupSizeSeparator,
|
||||
/// Expected: 'struct', 'let', 'var', 'type', ';', 'fn', eof
|
||||
GlobalItem,
|
||||
/// Expected a type.
|
||||
Type,
|
||||
/// Access of `var`, `let`, `const`.
|
||||
Variable,
|
||||
/// Access of a function
|
||||
Function,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Error, PartialEq)]
|
||||
pub enum NumberError {
|
||||
#[error("invalid numeric literal format")]
|
||||
Invalid,
|
||||
#[error("numeric literal not representable by target type")]
|
||||
NotRepresentable,
|
||||
#[error("unimplemented f16 type")]
|
||||
UnimplementedF16,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum InvalidAssignmentType {
|
||||
Other,
|
||||
Swizzle,
|
||||
ImmutableBinding(Span),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error<'a> {
|
||||
Unexpected(Span, ExpectedToken<'a>),
|
||||
UnexpectedComponents(Span),
|
||||
UnexpectedOperationInConstContext(Span),
|
||||
BadNumber(Span, NumberError),
|
||||
/// A negative signed integer literal where both signed and unsigned,
|
||||
/// but only non-negative literals are allowed.
|
||||
NegativeInt(Span),
|
||||
BadU32Constant(Span),
|
||||
BadMatrixScalarKind(Span, crate::ScalarKind, u8),
|
||||
BadAccessor(Span),
|
||||
BadTexture(Span),
|
||||
BadTypeCast {
|
||||
span: Span,
|
||||
from_type: String,
|
||||
to_type: String,
|
||||
},
|
||||
BadTextureSampleType {
|
||||
span: Span,
|
||||
kind: crate::ScalarKind,
|
||||
width: u8,
|
||||
},
|
||||
BadIncrDecrReferenceType(Span),
|
||||
InvalidResolve(ResolveError),
|
||||
InvalidForInitializer(Span),
|
||||
/// A break if appeared outside of a continuing block
|
||||
InvalidBreakIf(Span),
|
||||
InvalidGatherComponent(Span),
|
||||
InvalidConstructorComponentType(Span, i32),
|
||||
InvalidIdentifierUnderscore(Span),
|
||||
ReservedIdentifierPrefix(Span),
|
||||
UnknownAddressSpace(Span),
|
||||
UnknownAttribute(Span),
|
||||
UnknownBuiltin(Span),
|
||||
UnknownAccess(Span),
|
||||
UnknownIdent(Span, &'a str),
|
||||
UnknownScalarType(Span),
|
||||
UnknownType(Span),
|
||||
UnknownStorageFormat(Span),
|
||||
UnknownConservativeDepth(Span),
|
||||
SizeAttributeTooLow(Span, u32),
|
||||
AlignAttributeTooLow(Span, Alignment),
|
||||
NonPowerOfTwoAlignAttribute(Span),
|
||||
InconsistentBinding(Span),
|
||||
TypeNotConstructible(Span),
|
||||
TypeNotInferrable(Span),
|
||||
InitializationTypeMismatch(Span, String, String),
|
||||
MissingType(Span),
|
||||
MissingAttribute(&'static str, Span),
|
||||
InvalidAtomicPointer(Span),
|
||||
InvalidAtomicOperandType(Span),
|
||||
InvalidRayQueryPointer(Span),
|
||||
Pointer(&'static str, Span),
|
||||
NotPointer(Span),
|
||||
NotReference(&'static str, Span),
|
||||
InvalidAssignment {
|
||||
span: Span,
|
||||
ty: InvalidAssignmentType,
|
||||
},
|
||||
ReservedKeyword(Span),
|
||||
/// Redefinition of an identifier (used for both module-scope and local redefinitions).
|
||||
Redefinition {
|
||||
/// Span of the identifier in the previous definition.
|
||||
previous: Span,
|
||||
|
||||
/// Span of the identifier in the new definition.
|
||||
current: Span,
|
||||
},
|
||||
/// A declaration refers to itself directly.
|
||||
RecursiveDeclaration {
|
||||
/// The location of the name of the declaration.
|
||||
ident: Span,
|
||||
|
||||
/// The point at which it is used.
|
||||
usage: Span,
|
||||
},
|
||||
/// A declaration refers to itself indirectly, through one or more other
|
||||
/// definitions.
|
||||
CyclicDeclaration {
|
||||
/// The location of the name of some declaration in the cycle.
|
||||
ident: Span,
|
||||
|
||||
/// The edges of the cycle of references.
|
||||
///
|
||||
/// Each `(decl, reference)` pair indicates that the declaration whose
|
||||
/// name is `decl` has an identifier at `reference` whose definition is
|
||||
/// the next declaration in the cycle. The last pair's `reference` is
|
||||
/// the same identifier as `ident`, above.
|
||||
path: Vec<(Span, Span)>,
|
||||
},
|
||||
InvalidSwitchValue {
|
||||
uint: bool,
|
||||
span: Span,
|
||||
},
|
||||
CalledEntryPoint(Span),
|
||||
WrongArgumentCount {
|
||||
span: Span,
|
||||
expected: Range<u32>,
|
||||
found: u32,
|
||||
},
|
||||
FunctionReturnsVoid(Span),
|
||||
InvalidWorkGroupUniformLoad(Span),
|
||||
Other,
|
||||
ExpectedArraySize(Span),
|
||||
NonPositiveArrayLength(Span),
|
||||
}
|
||||
|
||||
impl<'a> Error<'a> {
|
||||
pub(crate) fn as_parse_error(&self, source: &'a str) -> ParseError {
|
||||
match *self {
|
||||
Error::Unexpected(unexpected_span, expected) => {
|
||||
let expected_str = match expected {
|
||||
ExpectedToken::Token(token) => {
|
||||
match token {
|
||||
Token::Separator(c) => format!("'{c}'"),
|
||||
Token::Paren(c) => format!("'{c}'"),
|
||||
Token::Attribute => "@".to_string(),
|
||||
Token::Number(_) => "number".to_string(),
|
||||
Token::Word(s) => s.to_string(),
|
||||
Token::Operation(c) => format!("operation ('{c}')"),
|
||||
Token::LogicalOperation(c) => format!("logical operation ('{c}')"),
|
||||
Token::ShiftOperation(c) => format!("bitshift ('{c}{c}')"),
|
||||
Token::AssignmentOperation(c) if c=='<' || c=='>' => format!("bitshift ('{c}{c}=')"),
|
||||
Token::AssignmentOperation(c) => format!("operation ('{c}=')"),
|
||||
Token::IncrementOperation => "increment operation".to_string(),
|
||||
Token::DecrementOperation => "decrement operation".to_string(),
|
||||
Token::Arrow => "->".to_string(),
|
||||
Token::Unknown(c) => format!("unknown ('{c}')"),
|
||||
Token::Trivia => "trivia".to_string(),
|
||||
Token::End => "end".to_string(),
|
||||
}
|
||||
}
|
||||
ExpectedToken::Identifier => "identifier".to_string(),
|
||||
ExpectedToken::Number => "32-bit signed integer literal".to_string(),
|
||||
ExpectedToken::Integer => "unsigned/signed integer literal".to_string(),
|
||||
ExpectedToken::PrimaryExpression => "expression".to_string(),
|
||||
ExpectedToken::Assignment => "assignment or increment/decrement".to_string(),
|
||||
ExpectedToken::SwitchItem => "switch item ('case' or 'default') or a closing curly bracket to signify the end of the switch statement ('}')".to_string(),
|
||||
ExpectedToken::WorkgroupSizeSeparator => "workgroup size separator (',') or a closing parenthesis".to_string(),
|
||||
ExpectedToken::GlobalItem => "global item ('struct', 'const', 'var', 'alias', ';', 'fn') or the end of the file".to_string(),
|
||||
ExpectedToken::Type => "type".to_string(),
|
||||
ExpectedToken::Variable => "variable access".to_string(),
|
||||
ExpectedToken::Function => "function name".to_string(),
|
||||
};
|
||||
ParseError {
|
||||
message: format!(
|
||||
"expected {}, found '{}'",
|
||||
expected_str, &source[unexpected_span],
|
||||
),
|
||||
labels: vec![(unexpected_span, format!("expected {expected_str}").into())],
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
Error::UnexpectedComponents(bad_span) => ParseError {
|
||||
message: "unexpected components".to_string(),
|
||||
labels: vec![(bad_span, "unexpected components".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnexpectedOperationInConstContext(span) => ParseError {
|
||||
message: "this operation is not supported in a const context".to_string(),
|
||||
labels: vec![(span, "operation not supported here".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadNumber(bad_span, ref err) => ParseError {
|
||||
message: format!("{}: `{}`", err, &source[bad_span],),
|
||||
labels: vec![(bad_span, err.to_string().into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::NegativeInt(bad_span) => ParseError {
|
||||
message: format!(
|
||||
"expected non-negative integer literal, found `{}`",
|
||||
&source[bad_span],
|
||||
),
|
||||
labels: vec![(bad_span, "expected non-negative integer".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadU32Constant(bad_span) => ParseError {
|
||||
message: format!(
|
||||
"expected unsigned integer constant expression, found `{}`",
|
||||
&source[bad_span],
|
||||
),
|
||||
labels: vec![(bad_span, "expected unsigned integer".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadMatrixScalarKind(span, kind, width) => ParseError {
|
||||
message: format!(
|
||||
"matrix scalar type must be floating-point, but found `{}`",
|
||||
kind.to_wgsl(width)
|
||||
),
|
||||
labels: vec![(span, "must be floating-point (e.g. `f32`)".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadAccessor(accessor_span) => ParseError {
|
||||
message: format!("invalid field accessor `{}`", &source[accessor_span],),
|
||||
labels: vec![(accessor_span, "invalid accessor".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownIdent(ident_span, ident) => ParseError {
|
||||
message: format!("no definition in scope for identifier: '{ident}'"),
|
||||
labels: vec![(ident_span, "unknown identifier".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownScalarType(bad_span) => ParseError {
|
||||
message: format!("unknown scalar type: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown scalar type".into())],
|
||||
notes: vec!["Valid scalar types are f32, f64, i32, u32, bool".into()],
|
||||
},
|
||||
Error::BadTextureSampleType { span, kind, width } => ParseError {
|
||||
message: format!(
|
||||
"texture sample type must be one of f32, i32 or u32, but found {}",
|
||||
kind.to_wgsl(width)
|
||||
),
|
||||
labels: vec![(span, "must be one of f32, i32 or u32".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadIncrDecrReferenceType(span) => ParseError {
|
||||
message:
|
||||
"increment/decrement operation requires reference type to be one of i32 or u32"
|
||||
.to_string(),
|
||||
labels: vec![(span, "must be a reference type of i32 or u32".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadTexture(bad_span) => ParseError {
|
||||
message: format!(
|
||||
"expected an image, but found '{}' which is not an image",
|
||||
&source[bad_span]
|
||||
),
|
||||
labels: vec![(bad_span, "not an image".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadTypeCast {
|
||||
span,
|
||||
ref from_type,
|
||||
ref to_type,
|
||||
} => {
|
||||
let msg = format!("cannot cast a {from_type} to a {to_type}");
|
||||
ParseError {
|
||||
message: msg.clone(),
|
||||
labels: vec![(span, msg.into())],
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
Error::InvalidResolve(ref resolve_error) => ParseError {
|
||||
message: resolve_error.to_string(),
|
||||
labels: vec![],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidForInitializer(bad_span) => ParseError {
|
||||
message: format!(
|
||||
"for(;;) initializer is not an assignment or a function call: '{}'",
|
||||
&source[bad_span]
|
||||
),
|
||||
labels: vec![(bad_span, "not an assignment or function call".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidBreakIf(bad_span) => ParseError {
|
||||
message: "A break if is only allowed in a continuing block".to_string(),
|
||||
labels: vec![(bad_span, "not in a continuing block".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidGatherComponent(bad_span) => ParseError {
|
||||
message: format!(
|
||||
"textureGather component '{}' doesn't exist, must be 0, 1, 2, or 3",
|
||||
&source[bad_span]
|
||||
),
|
||||
labels: vec![(bad_span, "invalid component".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidConstructorComponentType(bad_span, component) => ParseError {
|
||||
message: format!("invalid type for constructor component at index [{component}]"),
|
||||
labels: vec![(bad_span, "invalid component type".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidIdentifierUnderscore(bad_span) => ParseError {
|
||||
message: "Identifier can't be '_'".to_string(),
|
||||
labels: vec![(bad_span, "invalid identifier".into())],
|
||||
notes: vec![
|
||||
"Use phony assignment instead ('_ =' notice the absence of 'let' or 'var')"
|
||||
.to_string(),
|
||||
],
|
||||
},
|
||||
Error::ReservedIdentifierPrefix(bad_span) => ParseError {
|
||||
message: format!(
|
||||
"Identifier starts with a reserved prefix: '{}'",
|
||||
&source[bad_span]
|
||||
),
|
||||
labels: vec![(bad_span, "invalid identifier".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownAddressSpace(bad_span) => ParseError {
|
||||
message: format!("unknown address space: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown address space".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownAttribute(bad_span) => ParseError {
|
||||
message: format!("unknown attribute: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown attribute".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownBuiltin(bad_span) => ParseError {
|
||||
message: format!("unknown builtin: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown builtin".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownAccess(bad_span) => ParseError {
|
||||
message: format!("unknown access: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown access".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownStorageFormat(bad_span) => ParseError {
|
||||
message: format!("unknown storage format: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown storage format".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownConservativeDepth(bad_span) => ParseError {
|
||||
message: format!("unknown conservative depth: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown conservative depth".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::UnknownType(bad_span) => ParseError {
|
||||
message: format!("unknown type: '{}'", &source[bad_span]),
|
||||
labels: vec![(bad_span, "unknown type".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
|
||||
message: format!("struct member size must be at least {min_size}"),
|
||||
labels: vec![(bad_span, format!("must be at least {min_size}").into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::AlignAttributeTooLow(bad_span, min_align) => ParseError {
|
||||
message: format!("struct member alignment must be at least {min_align}"),
|
||||
labels: vec![(bad_span, format!("must be at least {min_align}").into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::NonPowerOfTwoAlignAttribute(bad_span) => ParseError {
|
||||
message: "struct member alignment must be a power of 2".to_string(),
|
||||
labels: vec![(bad_span, "must be a power of 2".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InconsistentBinding(span) => ParseError {
|
||||
message: "input/output binding is not consistent".to_string(),
|
||||
labels: vec![(span, "input/output binding is not consistent".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::TypeNotConstructible(span) => ParseError {
|
||||
message: format!("type `{}` is not constructible", &source[span]),
|
||||
labels: vec![(span, "type is not constructible".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::TypeNotInferrable(span) => ParseError {
|
||||
message: "type can't be inferred".to_string(),
|
||||
labels: vec![(span, "type can't be inferred".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InitializationTypeMismatch(name_span, ref expected_ty, ref got_ty) => {
|
||||
ParseError {
|
||||
message: format!(
|
||||
"the type of `{}` is expected to be `{}`, but got `{}`",
|
||||
&source[name_span], expected_ty, got_ty,
|
||||
),
|
||||
labels: vec![(
|
||||
name_span,
|
||||
format!("definition of `{}`", &source[name_span]).into(),
|
||||
)],
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
Error::MissingType(name_span) => ParseError {
|
||||
message: format!("variable `{}` needs a type", &source[name_span]),
|
||||
labels: vec![(
|
||||
name_span,
|
||||
format!("definition of `{}`", &source[name_span]).into(),
|
||||
)],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::MissingAttribute(name, name_span) => ParseError {
|
||||
message: format!(
|
||||
"variable `{}` needs a '{}' attribute",
|
||||
&source[name_span], name
|
||||
),
|
||||
labels: vec![(
|
||||
name_span,
|
||||
format!("definition of `{}`", &source[name_span]).into(),
|
||||
)],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidAtomicPointer(span) => ParseError {
|
||||
message: "atomic operation is done on a pointer to a non-atomic".to_string(),
|
||||
labels: vec![(span, "atomic pointer is invalid".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidAtomicOperandType(span) => ParseError {
|
||||
message: "atomic operand type is inconsistent with the operation".to_string(),
|
||||
labels: vec![(span, "atomic operand type is invalid".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidRayQueryPointer(span) => ParseError {
|
||||
message: "ray query operation is done on a pointer to a non-ray-query".to_string(),
|
||||
labels: vec![(span, "ray query pointer is invalid".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::NotPointer(span) => ParseError {
|
||||
message: "the operand of the `*` operator must be a pointer".to_string(),
|
||||
labels: vec![(span, "expression is not a pointer".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::NotReference(what, span) => ParseError {
|
||||
message: format!("{what} must be a reference"),
|
||||
labels: vec![(span, "expression is not a reference".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidAssignment { span, ty } => {
|
||||
let (extra_label, notes) = match ty {
|
||||
InvalidAssignmentType::Swizzle => (
|
||||
None,
|
||||
vec![
|
||||
"WGSL does not support assignments to swizzles".into(),
|
||||
"consider assigning each component individually".into(),
|
||||
],
|
||||
),
|
||||
InvalidAssignmentType::ImmutableBinding(binding_span) => (
|
||||
Some((binding_span, "this is an immutable binding".into())),
|
||||
vec![format!(
|
||||
"consider declaring '{}' with `var` instead of `let`",
|
||||
&source[binding_span]
|
||||
)],
|
||||
),
|
||||
InvalidAssignmentType::Other => (None, vec![]),
|
||||
};
|
||||
|
||||
ParseError {
|
||||
message: "invalid left-hand side of assignment".into(),
|
||||
labels: std::iter::once((span, "cannot assign to this expression".into()))
|
||||
.chain(extra_label)
|
||||
.collect(),
|
||||
notes,
|
||||
}
|
||||
}
|
||||
Error::Pointer(what, span) => ParseError {
|
||||
message: format!("{what} must not be a pointer"),
|
||||
labels: vec![(span, "expression is a pointer".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::ReservedKeyword(name_span) => ParseError {
|
||||
message: format!("name `{}` is a reserved keyword", &source[name_span]),
|
||||
labels: vec![(
|
||||
name_span,
|
||||
format!("definition of `{}`", &source[name_span]).into(),
|
||||
)],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::Redefinition { previous, current } => ParseError {
|
||||
message: format!("redefinition of `{}`", &source[current]),
|
||||
labels: vec![
|
||||
(
|
||||
current,
|
||||
format!("redefinition of `{}`", &source[current]).into(),
|
||||
),
|
||||
(
|
||||
previous,
|
||||
format!("previous definition of `{}`", &source[previous]).into(),
|
||||
),
|
||||
],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::RecursiveDeclaration { ident, usage } => ParseError {
|
||||
message: format!("declaration of `{}` is recursive", &source[ident]),
|
||||
labels: vec![(ident, "".into()), (usage, "uses itself here".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::CyclicDeclaration { ident, ref path } => ParseError {
|
||||
message: format!("declaration of `{}` is cyclic", &source[ident]),
|
||||
labels: path
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, &(ident, usage))| {
|
||||
[
|
||||
(ident, "".into()),
|
||||
(
|
||||
usage,
|
||||
if i == path.len() - 1 {
|
||||
"ending the cycle".into()
|
||||
} else {
|
||||
format!("uses `{}`", &source[ident]).into()
|
||||
},
|
||||
),
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidSwitchValue { uint, span } => ParseError {
|
||||
message: "invalid switch value".to_string(),
|
||||
labels: vec![(
|
||||
span,
|
||||
if uint {
|
||||
"expected unsigned integer"
|
||||
} else {
|
||||
"expected signed integer"
|
||||
}
|
||||
.into(),
|
||||
)],
|
||||
notes: vec![if uint {
|
||||
format!("suffix the integer with a `u`: '{}u'", &source[span])
|
||||
} else {
|
||||
let span = span.to_range().unwrap();
|
||||
format!(
|
||||
"remove the `u` suffix: '{}'",
|
||||
&source[span.start..span.end - 1]
|
||||
)
|
||||
}],
|
||||
},
|
||||
Error::CalledEntryPoint(span) => ParseError {
|
||||
message: "entry point cannot be called".to_string(),
|
||||
labels: vec![(span, "entry point cannot be called".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::WrongArgumentCount {
|
||||
span,
|
||||
ref expected,
|
||||
found,
|
||||
} => ParseError {
|
||||
message: format!(
|
||||
"wrong number of arguments: expected {}, found {}",
|
||||
if expected.len() < 2 {
|
||||
format!("{}", expected.start)
|
||||
} else {
|
||||
format!("{}..{}", expected.start, expected.end)
|
||||
},
|
||||
found
|
||||
),
|
||||
labels: vec![(span, "wrong number of arguments".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::FunctionReturnsVoid(span) => ParseError {
|
||||
message: "function does not return any value".to_string(),
|
||||
labels: vec![(span, "".into())],
|
||||
notes: vec![
|
||||
"perhaps you meant to call the function in a separate statement?".into(),
|
||||
],
|
||||
},
|
||||
Error::InvalidWorkGroupUniformLoad(span) => ParseError {
|
||||
message: "incorrect type passed to workgroupUniformLoad".into(),
|
||||
labels: vec![(span, "".into())],
|
||||
notes: vec!["passed type must be a workgroup pointer".into()],
|
||||
},
|
||||
Error::Other => ParseError {
|
||||
message: "other error".to_string(),
|
||||
labels: vec![],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::ExpectedArraySize(span) => ParseError {
|
||||
message: "array element count must resolve to an integer scalar (u32 or i32)"
|
||||
.to_string(),
|
||||
labels: vec![(span, "must resolve to u32/i32".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::NonPositiveArrayLength(span) => ParseError {
|
||||
message: "array element count must be greater than zero".to_string(),
|
||||
labels: vec![(span, "must be greater than zero".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
193
third-party/vendor/naga/src/front/wgsl/index.rs
vendored
Normal file
193
third-party/vendor/naga/src/front/wgsl/index.rs
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
use super::Error;
|
||||
use crate::front::wgsl::parse::ast;
|
||||
use crate::{FastHashMap, Handle, Span};
|
||||
|
||||
/// A `GlobalDecl` list in which each definition occurs before all its uses.
|
||||
pub struct Index<'a> {
|
||||
dependency_order: Vec<Handle<ast::GlobalDecl<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Index<'a> {
|
||||
/// Generate an `Index` for the given translation unit.
|
||||
///
|
||||
/// Perform a topological sort on `tu`'s global declarations, placing
|
||||
/// referents before the definitions that refer to them.
|
||||
///
|
||||
/// Return an error if the graph of references between declarations contains
|
||||
/// any cycles.
|
||||
pub fn generate(tu: &ast::TranslationUnit<'a>) -> Result<Self, Error<'a>> {
|
||||
// Produce a map from global definitions' names to their `Handle<GlobalDecl>`s.
|
||||
// While doing so, reject conflicting definitions.
|
||||
let mut globals = FastHashMap::with_capacity_and_hasher(tu.decls.len(), Default::default());
|
||||
for (handle, decl) in tu.decls.iter() {
|
||||
let ident = decl_ident(decl);
|
||||
let name = ident.name;
|
||||
if let Some(old) = globals.insert(name, handle) {
|
||||
return Err(Error::Redefinition {
|
||||
previous: decl_ident(&tu.decls[old]).span,
|
||||
current: ident.span,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let len = tu.decls.len();
|
||||
let solver = DependencySolver {
|
||||
globals: &globals,
|
||||
module: tu,
|
||||
visited: vec![false; len],
|
||||
temp_visited: vec![false; len],
|
||||
path: Vec::new(),
|
||||
out: Vec::with_capacity(len),
|
||||
};
|
||||
let dependency_order = solver.solve()?;
|
||||
|
||||
Ok(Self { dependency_order })
|
||||
}
|
||||
|
||||
/// Iterate over `GlobalDecl`s, visiting each definition before all its uses.
|
||||
///
|
||||
/// Produce handles for all of the `GlobalDecl`s of the `TranslationUnit`
|
||||
/// passed to `Index::generate`, ordered so that a given declaration is
|
||||
/// produced before any other declaration that uses it.
|
||||
pub fn visit_ordered(&self) -> impl Iterator<Item = Handle<ast::GlobalDecl<'a>>> + '_ {
|
||||
self.dependency_order.iter().copied()
|
||||
}
|
||||
}
|
||||
|
||||
/// An edge from a reference to its referent in the current depth-first
|
||||
/// traversal.
|
||||
///
|
||||
/// This is like `ast::Dependency`, except that we've determined which
|
||||
/// `GlobalDecl` it refers to.
|
||||
struct ResolvedDependency<'a> {
|
||||
/// The referent of some identifier used in the current declaration.
|
||||
decl: Handle<ast::GlobalDecl<'a>>,
|
||||
|
||||
/// Where that use occurs within the current declaration.
|
||||
usage: Span,
|
||||
}
|
||||
|
||||
/// Local state for ordering a `TranslationUnit`'s module-scope declarations.
|
||||
///
|
||||
/// Values of this type are used temporarily by `Index::generate`
|
||||
/// to perform a depth-first sort on the declarations.
|
||||
/// Technically, what we want is a topological sort, but a depth-first sort
|
||||
/// has one key benefit - it's much more efficient in storing
|
||||
/// the path of each node for error generation.
|
||||
struct DependencySolver<'source, 'temp> {
|
||||
/// A map from module-scope definitions' names to their handles.
|
||||
globals: &'temp FastHashMap<&'source str, Handle<ast::GlobalDecl<'source>>>,
|
||||
|
||||
/// The translation unit whose declarations we're ordering.
|
||||
module: &'temp ast::TranslationUnit<'source>,
|
||||
|
||||
/// For each handle, whether we have pushed it onto `out` yet.
|
||||
visited: Vec<bool>,
|
||||
|
||||
/// For each handle, whether it is an predecessor in the current depth-first
|
||||
/// traversal. This is used to detect cycles in the reference graph.
|
||||
temp_visited: Vec<bool>,
|
||||
|
||||
/// The current path in our depth-first traversal. Used for generating
|
||||
/// error messages for non-trivial reference cycles.
|
||||
path: Vec<ResolvedDependency<'source>>,
|
||||
|
||||
/// The list of declaration handles, with declarations before uses.
|
||||
out: Vec<Handle<ast::GlobalDecl<'source>>>,
|
||||
}
|
||||
|
||||
impl<'a> DependencySolver<'a, '_> {
|
||||
/// Produce the sorted list of declaration handles, and check for cycles.
|
||||
fn solve(mut self) -> Result<Vec<Handle<ast::GlobalDecl<'a>>>, Error<'a>> {
|
||||
for (id, _) in self.module.decls.iter() {
|
||||
if self.visited[id.index()] {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.dfs(id)?;
|
||||
}
|
||||
|
||||
Ok(self.out)
|
||||
}
|
||||
|
||||
/// Ensure that all declarations used by `id` have been added to the
|
||||
/// ordering, and then append `id` itself.
|
||||
fn dfs(&mut self, id: Handle<ast::GlobalDecl<'a>>) -> Result<(), Error<'a>> {
|
||||
let decl = &self.module.decls[id];
|
||||
let id_usize = id.index();
|
||||
|
||||
self.temp_visited[id_usize] = true;
|
||||
for dep in decl.dependencies.iter() {
|
||||
if let Some(&dep_id) = self.globals.get(dep.ident) {
|
||||
self.path.push(ResolvedDependency {
|
||||
decl: dep_id,
|
||||
usage: dep.usage,
|
||||
});
|
||||
let dep_id_usize = dep_id.index();
|
||||
|
||||
if self.temp_visited[dep_id_usize] {
|
||||
// Found a cycle.
|
||||
return if dep_id == id {
|
||||
// A declaration refers to itself directly.
|
||||
Err(Error::RecursiveDeclaration {
|
||||
ident: decl_ident(decl).span,
|
||||
usage: dep.usage,
|
||||
})
|
||||
} else {
|
||||
// A declaration refers to itself indirectly, through
|
||||
// one or more other definitions. Report the entire path
|
||||
// of references.
|
||||
let start_at = self
|
||||
.path
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find_map(|(i, dep)| (dep.decl == dep_id).then_some(i))
|
||||
.unwrap_or(0);
|
||||
|
||||
Err(Error::CyclicDeclaration {
|
||||
ident: decl_ident(&self.module.decls[dep_id]).span,
|
||||
path: self.path[start_at..]
|
||||
.iter()
|
||||
.map(|curr_dep| {
|
||||
let curr_id = curr_dep.decl;
|
||||
let curr_decl = &self.module.decls[curr_id];
|
||||
|
||||
(decl_ident(curr_decl).span, curr_dep.usage)
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
};
|
||||
} else if !self.visited[dep_id_usize] {
|
||||
self.dfs(dep_id)?;
|
||||
}
|
||||
|
||||
// Remove this edge from the current path.
|
||||
self.path.pop();
|
||||
}
|
||||
|
||||
// Ignore unresolved identifiers; they may be predeclared objects.
|
||||
}
|
||||
|
||||
// Remove this node from the current path.
|
||||
self.temp_visited[id_usize] = false;
|
||||
|
||||
// Now everything this declaration uses has been visited, and is already
|
||||
// present in `out`. That means we we can append this one to the
|
||||
// ordering, and mark it as visited.
|
||||
self.out.push(id);
|
||||
self.visited[id_usize] = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const fn decl_ident<'a>(decl: &ast::GlobalDecl<'a>) -> ast::Ident<'a> {
|
||||
match decl.kind {
|
||||
ast::GlobalDeclKind::Fn(ref f) => f.name,
|
||||
ast::GlobalDeclKind::Var(ref v) => v.name,
|
||||
ast::GlobalDeclKind::Const(ref c) => c.name,
|
||||
ast::GlobalDeclKind::Struct(ref s) => s.name,
|
||||
ast::GlobalDeclKind::Type(ref t) => t.name,
|
||||
}
|
||||
}
|
||||
606
third-party/vendor/naga/src/front/wgsl/lower/construction.rs
vendored
Normal file
606
third-party/vendor/naga/src/front/wgsl/lower/construction.rs
vendored
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
use std::num::NonZeroU32;
|
||||
|
||||
use crate::front::wgsl::parse::ast;
|
||||
use crate::{Handle, Span};
|
||||
|
||||
use crate::front::wgsl::error::Error;
|
||||
use crate::front::wgsl::lower::{ExpressionContext, Lowerer};
|
||||
use crate::proc::TypeResolution;
|
||||
|
||||
enum ConcreteConstructorHandle {
|
||||
PartialVector {
|
||||
size: crate::VectorSize,
|
||||
},
|
||||
PartialMatrix {
|
||||
columns: crate::VectorSize,
|
||||
rows: crate::VectorSize,
|
||||
},
|
||||
PartialArray,
|
||||
Type(Handle<crate::Type>),
|
||||
}
|
||||
|
||||
impl ConcreteConstructorHandle {
|
||||
fn borrow<'a>(&self, module: &'a crate::Module) -> ConcreteConstructor<'a> {
|
||||
match *self {
|
||||
Self::PartialVector { size } => ConcreteConstructor::PartialVector { size },
|
||||
Self::PartialMatrix { columns, rows } => {
|
||||
ConcreteConstructor::PartialMatrix { columns, rows }
|
||||
}
|
||||
Self::PartialArray => ConcreteConstructor::PartialArray,
|
||||
Self::Type(handle) => ConcreteConstructor::Type(handle, &module.types[handle].inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ConcreteConstructor<'a> {
|
||||
PartialVector {
|
||||
size: crate::VectorSize,
|
||||
},
|
||||
PartialMatrix {
|
||||
columns: crate::VectorSize,
|
||||
rows: crate::VectorSize,
|
||||
},
|
||||
PartialArray,
|
||||
Type(Handle<crate::Type>, &'a crate::TypeInner),
|
||||
}
|
||||
|
||||
impl ConcreteConstructorHandle {
|
||||
fn to_error_string(&self, ctx: ExpressionContext) -> String {
|
||||
match *self {
|
||||
Self::PartialVector { size } => {
|
||||
format!("vec{}<?>", size as u32,)
|
||||
}
|
||||
Self::PartialMatrix { columns, rows } => {
|
||||
format!("mat{}x{}<?>", columns as u32, rows as u32,)
|
||||
}
|
||||
Self::PartialArray => "array<?, ?>".to_string(),
|
||||
Self::Type(ty) => ctx.format_type(ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ComponentsHandle<'a> {
|
||||
None,
|
||||
One {
|
||||
component: Handle<crate::Expression>,
|
||||
span: Span,
|
||||
ty: &'a TypeResolution,
|
||||
},
|
||||
Many {
|
||||
components: Vec<Handle<crate::Expression>>,
|
||||
spans: Vec<Span>,
|
||||
first_component_ty: &'a TypeResolution,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> ComponentsHandle<'a> {
|
||||
fn borrow(self, module: &'a crate::Module) -> Components<'a> {
|
||||
match self {
|
||||
Self::None => Components::None,
|
||||
Self::One {
|
||||
component,
|
||||
span,
|
||||
ty,
|
||||
} => Components::One {
|
||||
component,
|
||||
span,
|
||||
ty_inner: ty.inner_with(&module.types),
|
||||
},
|
||||
Self::Many {
|
||||
components,
|
||||
spans,
|
||||
first_component_ty,
|
||||
} => Components::Many {
|
||||
components,
|
||||
spans,
|
||||
first_component_ty_inner: first_component_ty.inner_with(&module.types),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Components<'a> {
|
||||
None,
|
||||
One {
|
||||
component: Handle<crate::Expression>,
|
||||
span: Span,
|
||||
ty_inner: &'a crate::TypeInner,
|
||||
},
|
||||
Many {
|
||||
components: Vec<Handle<crate::Expression>>,
|
||||
spans: Vec<Span>,
|
||||
first_component_ty_inner: &'a crate::TypeInner,
|
||||
},
|
||||
}
|
||||
|
||||
impl Components<'_> {
|
||||
fn into_components_vec(self) -> Vec<Handle<crate::Expression>> {
|
||||
match self {
|
||||
Self::None => vec![],
|
||||
Self::One { component, .. } => vec![component],
|
||||
Self::Many { components, .. } => components,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, 'temp> Lowerer<'source, 'temp> {
|
||||
/// Generate Naga IR for a type constructor expression.
|
||||
///
|
||||
/// The `constructor` value represents the head of the constructor
|
||||
/// expression, which is at least a hint of which type is being built; if
|
||||
/// it's one of the `Partial` variants, we need to consider the argument
|
||||
/// types as well.
|
||||
///
|
||||
/// This is used for [`Construct`] expressions, but also for [`Call`]
|
||||
/// expressions, once we've determined that the "callable" (in WGSL spec
|
||||
/// terms) is actually a type.
|
||||
///
|
||||
/// [`Construct`]: ast::Expression::Construct
|
||||
/// [`Call`]: ast::Expression::Call
|
||||
pub fn construct(
|
||||
&mut self,
|
||||
span: Span,
|
||||
constructor: &ast::ConstructorType<'source>,
|
||||
ty_span: Span,
|
||||
components: &[Handle<ast::Expression<'source>>],
|
||||
mut ctx: ExpressionContext<'source, '_, '_>,
|
||||
) -> Result<Handle<crate::Expression>, Error<'source>> {
|
||||
let constructor_h = self.constructor(constructor, ctx.reborrow())?;
|
||||
|
||||
let components_h = match *components {
|
||||
[] => ComponentsHandle::None,
|
||||
[component] => {
|
||||
let span = ctx.ast_expressions.get_span(component);
|
||||
let component = self.expression(component, ctx.reborrow())?;
|
||||
ctx.grow_types(component)?;
|
||||
let ty = &ctx.typifier()[component];
|
||||
|
||||
ComponentsHandle::One {
|
||||
component,
|
||||
span,
|
||||
ty,
|
||||
}
|
||||
}
|
||||
[component, ref rest @ ..] => {
|
||||
let span = ctx.ast_expressions.get_span(component);
|
||||
let component = self.expression(component, ctx.reborrow())?;
|
||||
|
||||
let components = std::iter::once(Ok(component))
|
||||
.chain(
|
||||
rest.iter()
|
||||
.map(|&component| self.expression(component, ctx.reborrow())),
|
||||
)
|
||||
.collect::<Result<_, _>>()?;
|
||||
let spans = std::iter::once(span)
|
||||
.chain(
|
||||
rest.iter()
|
||||
.map(|&component| ctx.ast_expressions.get_span(component)),
|
||||
)
|
||||
.collect();
|
||||
|
||||
ctx.grow_types(component)?;
|
||||
let ty = &ctx.typifier()[component];
|
||||
|
||||
ComponentsHandle::Many {
|
||||
components,
|
||||
spans,
|
||||
first_component_ty: ty,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (components, constructor) = (
|
||||
components_h.borrow(ctx.module),
|
||||
constructor_h.borrow(ctx.module),
|
||||
);
|
||||
let expr = match (components, constructor) {
|
||||
// Empty constructor
|
||||
(Components::None, dst_ty) => match dst_ty {
|
||||
ConcreteConstructor::Type(ty, _) => {
|
||||
return Ok(ctx.interrupt_emitter(crate::Expression::ZeroValue(ty), span))
|
||||
}
|
||||
_ => return Err(Error::TypeNotInferrable(ty_span)),
|
||||
},
|
||||
|
||||
// Scalar constructor & conversion (scalar -> scalar)
|
||||
(
|
||||
Components::One {
|
||||
component,
|
||||
ty_inner: &crate::TypeInner::Scalar { .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::Type(_, &crate::TypeInner::Scalar { kind, width }),
|
||||
) => crate::Expression::As {
|
||||
expr: component,
|
||||
kind,
|
||||
convert: Some(width),
|
||||
},
|
||||
|
||||
// Vector conversion (vector -> vector)
|
||||
(
|
||||
Components::One {
|
||||
component,
|
||||
ty_inner: &crate::TypeInner::Vector { size: src_size, .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::Type(
|
||||
_,
|
||||
&crate::TypeInner::Vector {
|
||||
size: dst_size,
|
||||
kind: dst_kind,
|
||||
width: dst_width,
|
||||
},
|
||||
),
|
||||
) if dst_size == src_size => crate::Expression::As {
|
||||
expr: component,
|
||||
kind: dst_kind,
|
||||
convert: Some(dst_width),
|
||||
},
|
||||
|
||||
// Vector conversion (vector -> vector) - partial
|
||||
(
|
||||
Components::One {
|
||||
component,
|
||||
ty_inner:
|
||||
&crate::TypeInner::Vector {
|
||||
size: src_size,
|
||||
kind: src_kind,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::PartialVector { size: dst_size },
|
||||
) if dst_size == src_size => crate::Expression::As {
|
||||
expr: component,
|
||||
kind: src_kind,
|
||||
convert: None,
|
||||
},
|
||||
|
||||
// Matrix conversion (matrix -> matrix)
|
||||
(
|
||||
Components::One {
|
||||
component,
|
||||
ty_inner:
|
||||
&crate::TypeInner::Matrix {
|
||||
columns: src_columns,
|
||||
rows: src_rows,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::Type(
|
||||
_,
|
||||
&crate::TypeInner::Matrix {
|
||||
columns: dst_columns,
|
||||
rows: dst_rows,
|
||||
width: dst_width,
|
||||
},
|
||||
),
|
||||
) if dst_columns == src_columns && dst_rows == src_rows => crate::Expression::As {
|
||||
expr: component,
|
||||
kind: crate::ScalarKind::Float,
|
||||
convert: Some(dst_width),
|
||||
},
|
||||
|
||||
// Matrix conversion (matrix -> matrix) - partial
|
||||
(
|
||||
Components::One {
|
||||
component,
|
||||
ty_inner:
|
||||
&crate::TypeInner::Matrix {
|
||||
columns: src_columns,
|
||||
rows: src_rows,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::PartialMatrix {
|
||||
columns: dst_columns,
|
||||
rows: dst_rows,
|
||||
},
|
||||
) if dst_columns == src_columns && dst_rows == src_rows => crate::Expression::As {
|
||||
expr: component,
|
||||
kind: crate::ScalarKind::Float,
|
||||
convert: None,
|
||||
},
|
||||
|
||||
// Vector constructor (splat) - infer type
|
||||
(
|
||||
Components::One {
|
||||
component,
|
||||
ty_inner: &crate::TypeInner::Scalar { .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::PartialVector { size },
|
||||
) => crate::Expression::Splat {
|
||||
size,
|
||||
value: component,
|
||||
},
|
||||
|
||||
// Vector constructor (splat)
|
||||
(
|
||||
Components::One {
|
||||
component,
|
||||
ty_inner:
|
||||
&crate::TypeInner::Scalar {
|
||||
kind: src_kind,
|
||||
width: src_width,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::Type(
|
||||
_,
|
||||
&crate::TypeInner::Vector {
|
||||
size,
|
||||
kind: dst_kind,
|
||||
width: dst_width,
|
||||
},
|
||||
),
|
||||
) if dst_kind == src_kind || dst_width == src_width => crate::Expression::Splat {
|
||||
size,
|
||||
value: component,
|
||||
},
|
||||
|
||||
// Vector constructor (by elements)
|
||||
(
|
||||
Components::Many {
|
||||
components,
|
||||
first_component_ty_inner:
|
||||
&crate::TypeInner::Scalar { kind, width }
|
||||
| &crate::TypeInner::Vector { kind, width, .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::PartialVector { size },
|
||||
)
|
||||
| (
|
||||
Components::Many {
|
||||
components,
|
||||
first_component_ty_inner:
|
||||
&crate::TypeInner::Scalar { .. } | &crate::TypeInner::Vector { .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::Type(_, &crate::TypeInner::Vector { size, width, kind }),
|
||||
) => {
|
||||
let inner = crate::TypeInner::Vector { size, kind, width };
|
||||
let ty = ctx.ensure_type_exists(inner);
|
||||
crate::Expression::Compose { ty, components }
|
||||
}
|
||||
|
||||
// Matrix constructor (by elements)
|
||||
(
|
||||
Components::Many {
|
||||
components,
|
||||
first_component_ty_inner: &crate::TypeInner::Scalar { width, .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::PartialMatrix { columns, rows },
|
||||
)
|
||||
| (
|
||||
Components::Many {
|
||||
components,
|
||||
first_component_ty_inner: &crate::TypeInner::Scalar { .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::Type(
|
||||
_,
|
||||
&crate::TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
},
|
||||
),
|
||||
) => {
|
||||
let vec_ty = ctx.ensure_type_exists(crate::TypeInner::Vector {
|
||||
width,
|
||||
kind: crate::ScalarKind::Float,
|
||||
size: rows,
|
||||
});
|
||||
|
||||
let components = components
|
||||
.chunks(rows as usize)
|
||||
.map(|vec_components| {
|
||||
ctx.append_expression(
|
||||
crate::Expression::Compose {
|
||||
ty: vec_ty,
|
||||
components: Vec::from(vec_components),
|
||||
},
|
||||
Default::default(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
});
|
||||
crate::Expression::Compose { ty, components }
|
||||
}
|
||||
|
||||
// Matrix constructor (by columns)
|
||||
(
|
||||
Components::Many {
|
||||
components,
|
||||
first_component_ty_inner: &crate::TypeInner::Vector { width, .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::PartialMatrix { columns, rows },
|
||||
)
|
||||
| (
|
||||
Components::Many {
|
||||
components,
|
||||
first_component_ty_inner: &crate::TypeInner::Vector { .. },
|
||||
..
|
||||
},
|
||||
ConcreteConstructor::Type(
|
||||
_,
|
||||
&crate::TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
},
|
||||
),
|
||||
) => {
|
||||
let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
});
|
||||
crate::Expression::Compose { ty, components }
|
||||
}
|
||||
|
||||
// Array constructor - infer type
|
||||
(components, ConcreteConstructor::PartialArray) => {
|
||||
let components = components.into_components_vec();
|
||||
|
||||
let base = ctx.register_type(components[0])?;
|
||||
|
||||
let inner = crate::TypeInner::Array {
|
||||
base,
|
||||
size: crate::ArraySize::Constant(
|
||||
NonZeroU32::new(u32::try_from(components.len()).unwrap()).unwrap(),
|
||||
),
|
||||
stride: {
|
||||
self.layouter.update(ctx.module.to_ctx()).unwrap();
|
||||
self.layouter[base].to_stride()
|
||||
},
|
||||
};
|
||||
let ty = ctx.ensure_type_exists(inner);
|
||||
|
||||
crate::Expression::Compose { ty, components }
|
||||
}
|
||||
|
||||
// Array constructor
|
||||
(components, ConcreteConstructor::Type(ty, &crate::TypeInner::Array { .. })) => {
|
||||
let components = components.into_components_vec();
|
||||
crate::Expression::Compose { ty, components }
|
||||
}
|
||||
|
||||
// Struct constructor
|
||||
(components, ConcreteConstructor::Type(ty, &crate::TypeInner::Struct { .. })) => {
|
||||
crate::Expression::Compose {
|
||||
ty,
|
||||
components: components.into_components_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
// ERRORS
|
||||
|
||||
// Bad conversion (type cast)
|
||||
(Components::One { span, ty_inner, .. }, _) => {
|
||||
let from_type = ctx.format_typeinner(ty_inner);
|
||||
return Err(Error::BadTypeCast {
|
||||
span,
|
||||
from_type,
|
||||
to_type: constructor_h.to_error_string(ctx.reborrow()),
|
||||
});
|
||||
}
|
||||
|
||||
// Too many parameters for scalar constructor
|
||||
(
|
||||
Components::Many { spans, .. },
|
||||
ConcreteConstructor::Type(_, &crate::TypeInner::Scalar { .. }),
|
||||
) => {
|
||||
let span = spans[1].until(spans.last().unwrap());
|
||||
return Err(Error::UnexpectedComponents(span));
|
||||
}
|
||||
|
||||
// Parameters are of the wrong type for vector or matrix constructor
|
||||
(
|
||||
Components::Many { spans, .. },
|
||||
ConcreteConstructor::Type(
|
||||
_,
|
||||
&crate::TypeInner::Vector { .. } | &crate::TypeInner::Matrix { .. },
|
||||
)
|
||||
| ConcreteConstructor::PartialVector { .. }
|
||||
| ConcreteConstructor::PartialMatrix { .. },
|
||||
) => {
|
||||
return Err(Error::InvalidConstructorComponentType(spans[0], 0));
|
||||
}
|
||||
|
||||
// Other types can't be constructed
|
||||
_ => return Err(Error::TypeNotConstructible(ty_span)),
|
||||
};
|
||||
|
||||
let expr = ctx.append_expression(expr, span);
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
/// Build a Naga IR [`Type`] for `constructor` if there is enough
|
||||
/// information to do so.
|
||||
///
|
||||
/// For `Partial` variants of [`ast::ConstructorType`], we don't know the
|
||||
/// component type, so in that case we return the appropriate `Partial`
|
||||
/// variant of [`ConcreteConstructorHandle`].
|
||||
///
|
||||
/// But for the other `ConstructorType` variants, we have everything we need
|
||||
/// to know to actually produce a Naga IR type. In this case we add to/find
|
||||
/// in [`ctx.module`] a suitable Naga `Type` and return a
|
||||
/// [`ConcreteConstructorHandle::Type`] value holding its handle.
|
||||
///
|
||||
/// Note that constructing an [`Array`] type may require inserting
|
||||
/// [`Constant`]s as well as `Type`s into `ctx.module`, to represent the
|
||||
/// array's length.
|
||||
///
|
||||
/// [`Type`]: crate::Type
|
||||
/// [`ctx.module`]: ExpressionContext::module
|
||||
/// [`Array`]: crate::TypeInner::Array
|
||||
/// [`Constant`]: crate::Constant
|
||||
fn constructor<'out>(
|
||||
&mut self,
|
||||
constructor: &ast::ConstructorType<'source>,
|
||||
mut ctx: ExpressionContext<'source, '_, 'out>,
|
||||
) -> Result<ConcreteConstructorHandle, Error<'source>> {
|
||||
let c = match *constructor {
|
||||
ast::ConstructorType::Scalar { width, kind } => {
|
||||
let ty = ctx.ensure_type_exists(crate::TypeInner::Scalar { width, kind });
|
||||
ConcreteConstructorHandle::Type(ty)
|
||||
}
|
||||
ast::ConstructorType::PartialVector { size } => {
|
||||
ConcreteConstructorHandle::PartialVector { size }
|
||||
}
|
||||
ast::ConstructorType::Vector { size, kind, width } => {
|
||||
let ty = ctx.ensure_type_exists(crate::TypeInner::Vector { size, kind, width });
|
||||
ConcreteConstructorHandle::Type(ty)
|
||||
}
|
||||
ast::ConstructorType::PartialMatrix { rows, columns } => {
|
||||
ConcreteConstructorHandle::PartialMatrix { rows, columns }
|
||||
}
|
||||
ast::ConstructorType::Matrix {
|
||||
rows,
|
||||
columns,
|
||||
width,
|
||||
} => {
|
||||
let ty = ctx.ensure_type_exists(crate::TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
});
|
||||
ConcreteConstructorHandle::Type(ty)
|
||||
}
|
||||
ast::ConstructorType::PartialArray => ConcreteConstructorHandle::PartialArray,
|
||||
ast::ConstructorType::Array { base, size } => {
|
||||
let base = self.resolve_ast_type(base, ctx.as_global())?;
|
||||
let size = match size {
|
||||
ast::ArraySize::Constant(expr) => {
|
||||
let const_expr = self.expression(expr, ctx.as_const())?;
|
||||
crate::ArraySize::Constant(ctx.array_length(const_expr)?)
|
||||
}
|
||||
ast::ArraySize::Dynamic => crate::ArraySize::Dynamic,
|
||||
};
|
||||
|
||||
self.layouter.update(ctx.module.to_ctx()).unwrap();
|
||||
let ty = ctx.ensure_type_exists(crate::TypeInner::Array {
|
||||
base,
|
||||
size,
|
||||
stride: self.layouter[base].to_stride(),
|
||||
});
|
||||
ConcreteConstructorHandle::Type(ty)
|
||||
}
|
||||
ast::ConstructorType::Type(ty) => ConcreteConstructorHandle::Type(ty),
|
||||
};
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
}
|
||||
2484
third-party/vendor/naga/src/front/wgsl/lower/mod.rs
vendored
Normal file
2484
third-party/vendor/naga/src/front/wgsl/lower/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
301
third-party/vendor/naga/src/front/wgsl/mod.rs
vendored
Normal file
301
third-party/vendor/naga/src/front/wgsl/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
/*!
|
||||
Frontend for [WGSL][wgsl] (WebGPU Shading Language).
|
||||
|
||||
[wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html
|
||||
*/
|
||||
|
||||
mod error;
|
||||
mod index;
|
||||
mod lower;
|
||||
mod parse;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::front::wgsl::error::Error;
|
||||
use crate::front::wgsl::parse::Parser;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use crate::front::wgsl::error::ParseError;
|
||||
use crate::front::wgsl::lower::Lowerer;
|
||||
|
||||
pub struct Frontend {
|
||||
parser: Parser,
|
||||
}
|
||||
|
||||
impl Frontend {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
parser: Parser::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, source: &str) -> Result<crate::Module, ParseError> {
|
||||
self.inner(source).map_err(|x| x.as_parse_error(source))
|
||||
}
|
||||
|
||||
fn inner<'a>(&mut self, source: &'a str) -> Result<crate::Module, Error<'a>> {
|
||||
let tu = self.parser.parse(source)?;
|
||||
let index = index::Index::generate(&tu)?;
|
||||
let module = Lowerer::new(&index).lower(&tu)?;
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_str(source: &str) -> Result<crate::Module, ParseError> {
|
||||
Frontend::new().parse(source)
|
||||
}
|
||||
|
||||
impl crate::StorageFormat {
|
||||
const fn to_wgsl(self) -> &'static str {
|
||||
use crate::StorageFormat as Sf;
|
||||
match self {
|
||||
Sf::R8Unorm => "r8unorm",
|
||||
Sf::R8Snorm => "r8snorm",
|
||||
Sf::R8Uint => "r8uint",
|
||||
Sf::R8Sint => "r8sint",
|
||||
Sf::R16Uint => "r16uint",
|
||||
Sf::R16Sint => "r16sint",
|
||||
Sf::R16Float => "r16float",
|
||||
Sf::Rg8Unorm => "rg8unorm",
|
||||
Sf::Rg8Snorm => "rg8snorm",
|
||||
Sf::Rg8Uint => "rg8uint",
|
||||
Sf::Rg8Sint => "rg8sint",
|
||||
Sf::R32Uint => "r32uint",
|
||||
Sf::R32Sint => "r32sint",
|
||||
Sf::R32Float => "r32float",
|
||||
Sf::Rg16Uint => "rg16uint",
|
||||
Sf::Rg16Sint => "rg16sint",
|
||||
Sf::Rg16Float => "rg16float",
|
||||
Sf::Rgba8Unorm => "rgba8unorm",
|
||||
Sf::Rgba8Snorm => "rgba8snorm",
|
||||
Sf::Rgba8Uint => "rgba8uint",
|
||||
Sf::Rgba8Sint => "rgba8sint",
|
||||
Sf::Rgb10a2Unorm => "rgb10a2unorm",
|
||||
Sf::Rg11b10Float => "rg11b10float",
|
||||
Sf::Rg32Uint => "rg32uint",
|
||||
Sf::Rg32Sint => "rg32sint",
|
||||
Sf::Rg32Float => "rg32float",
|
||||
Sf::Rgba16Uint => "rgba16uint",
|
||||
Sf::Rgba16Sint => "rgba16sint",
|
||||
Sf::Rgba16Float => "rgba16float",
|
||||
Sf::Rgba32Uint => "rgba32uint",
|
||||
Sf::Rgba32Sint => "rgba32sint",
|
||||
Sf::Rgba32Float => "rgba32float",
|
||||
Sf::R16Unorm => "r16unorm",
|
||||
Sf::R16Snorm => "r16snorm",
|
||||
Sf::Rg16Unorm => "rg16unorm",
|
||||
Sf::Rg16Snorm => "rg16snorm",
|
||||
Sf::Rgba16Unorm => "rgba16unorm",
|
||||
Sf::Rgba16Snorm => "rgba16snorm",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::TypeInner {
|
||||
/// Formats the type as it is written in wgsl.
|
||||
///
|
||||
/// For example `vec3<f32>`.
|
||||
///
|
||||
/// Note: The names of a `TypeInner::Struct` is not known. Therefore this method will simply return "struct" for them.
|
||||
fn to_wgsl(&self, gctx: crate::proc::GlobalCtx) -> String {
|
||||
use crate::TypeInner as Ti;
|
||||
|
||||
match *self {
|
||||
Ti::Scalar { kind, width } => kind.to_wgsl(width),
|
||||
Ti::Vector { size, kind, width } => {
|
||||
format!("vec{}<{}>", size as u32, kind.to_wgsl(width))
|
||||
}
|
||||
Ti::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
} => {
|
||||
format!(
|
||||
"mat{}x{}<{}>",
|
||||
columns as u32,
|
||||
rows as u32,
|
||||
crate::ScalarKind::Float.to_wgsl(width),
|
||||
)
|
||||
}
|
||||
Ti::Atomic { kind, width } => {
|
||||
format!("atomic<{}>", kind.to_wgsl(width))
|
||||
}
|
||||
Ti::Pointer { base, .. } => {
|
||||
let base = &gctx.types[base];
|
||||
let name = base.name.as_deref().unwrap_or("unknown");
|
||||
format!("ptr<{name}>")
|
||||
}
|
||||
Ti::ValuePointer { kind, width, .. } => {
|
||||
format!("ptr<{}>", kind.to_wgsl(width))
|
||||
}
|
||||
Ti::Array { base, size, .. } => {
|
||||
let member_type = &gctx.types[base];
|
||||
let base = member_type.name.as_deref().unwrap_or("unknown");
|
||||
match size {
|
||||
crate::ArraySize::Constant(size) => format!("array<{base}, {size}>"),
|
||||
crate::ArraySize::Dynamic => format!("array<{base}>"),
|
||||
}
|
||||
}
|
||||
Ti::Struct { .. } => {
|
||||
// TODO: Actually output the struct?
|
||||
"struct".to_string()
|
||||
}
|
||||
Ti::Image {
|
||||
dim,
|
||||
arrayed,
|
||||
class,
|
||||
} => {
|
||||
let dim_suffix = match dim {
|
||||
crate::ImageDimension::D1 => "_1d",
|
||||
crate::ImageDimension::D2 => "_2d",
|
||||
crate::ImageDimension::D3 => "_3d",
|
||||
crate::ImageDimension::Cube => "_cube",
|
||||
};
|
||||
let array_suffix = if arrayed { "_array" } else { "" };
|
||||
|
||||
let class_suffix = match class {
|
||||
crate::ImageClass::Sampled { multi: true, .. } => "_multisampled",
|
||||
crate::ImageClass::Depth { multi: false } => "_depth",
|
||||
crate::ImageClass::Depth { multi: true } => "_depth_multisampled",
|
||||
crate::ImageClass::Sampled { multi: false, .. }
|
||||
| crate::ImageClass::Storage { .. } => "",
|
||||
};
|
||||
|
||||
let type_in_brackets = match class {
|
||||
crate::ImageClass::Sampled { kind, .. } => {
|
||||
// Note: The only valid widths are 4 bytes wide.
|
||||
// The lexer has already verified this, so we can safely assume it here.
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type
|
||||
let element_type = kind.to_wgsl(4);
|
||||
format!("<{element_type}>")
|
||||
}
|
||||
crate::ImageClass::Depth { multi: _ } => String::new(),
|
||||
crate::ImageClass::Storage { format, access } => {
|
||||
if access.contains(crate::StorageAccess::STORE) {
|
||||
format!("<{},write>", format.to_wgsl())
|
||||
} else {
|
||||
format!("<{}>", format.to_wgsl())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
format!("texture{class_suffix}{dim_suffix}{array_suffix}{type_in_brackets}")
|
||||
}
|
||||
Ti::Sampler { .. } => "sampler".to_string(),
|
||||
Ti::AccelerationStructure => "acceleration_structure".to_string(),
|
||||
Ti::RayQuery => "ray_query".to_string(),
|
||||
Ti::BindingArray { base, size, .. } => {
|
||||
let member_type = &gctx.types[base];
|
||||
let base = member_type.name.as_deref().unwrap_or("unknown");
|
||||
match size {
|
||||
crate::ArraySize::Constant(size) => format!("binding_array<{base}, {size}>"),
|
||||
crate::ArraySize::Dynamic => format!("binding_array<{base}>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod type_inner_tests {
|
||||
|
||||
#[test]
|
||||
fn to_wgsl() {
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
let mut types = crate::UniqueArena::new();
|
||||
|
||||
let mytype1 = types.insert(
|
||||
crate::Type {
|
||||
name: Some("MyType1".to_string()),
|
||||
inner: crate::TypeInner::Struct {
|
||||
members: vec![],
|
||||
span: 0,
|
||||
},
|
||||
},
|
||||
Default::default(),
|
||||
);
|
||||
let mytype2 = types.insert(
|
||||
crate::Type {
|
||||
name: Some("MyType2".to_string()),
|
||||
inner: crate::TypeInner::Struct {
|
||||
members: vec![],
|
||||
span: 0,
|
||||
},
|
||||
},
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let gctx = crate::proc::GlobalCtx {
|
||||
types: &types,
|
||||
constants: &crate::Arena::new(),
|
||||
const_expressions: &crate::Arena::new(),
|
||||
};
|
||||
let array = crate::TypeInner::Array {
|
||||
base: mytype1,
|
||||
stride: 4,
|
||||
size: crate::ArraySize::Constant(unsafe { NonZeroU32::new_unchecked(32) }),
|
||||
};
|
||||
assert_eq!(array.to_wgsl(gctx), "array<MyType1, 32>");
|
||||
|
||||
let mat = crate::TypeInner::Matrix {
|
||||
rows: crate::VectorSize::Quad,
|
||||
columns: crate::VectorSize::Bi,
|
||||
width: 8,
|
||||
};
|
||||
assert_eq!(mat.to_wgsl(gctx), "mat2x4<f64>");
|
||||
|
||||
let ptr = crate::TypeInner::Pointer {
|
||||
base: mytype2,
|
||||
space: crate::AddressSpace::Storage {
|
||||
access: crate::StorageAccess::default(),
|
||||
},
|
||||
};
|
||||
assert_eq!(ptr.to_wgsl(gctx), "ptr<MyType2>");
|
||||
|
||||
let img1 = crate::TypeInner::Image {
|
||||
dim: crate::ImageDimension::D2,
|
||||
arrayed: false,
|
||||
class: crate::ImageClass::Sampled {
|
||||
kind: crate::ScalarKind::Float,
|
||||
multi: true,
|
||||
},
|
||||
};
|
||||
assert_eq!(img1.to_wgsl(gctx), "texture_multisampled_2d<f32>");
|
||||
|
||||
let img2 = crate::TypeInner::Image {
|
||||
dim: crate::ImageDimension::Cube,
|
||||
arrayed: true,
|
||||
class: crate::ImageClass::Depth { multi: false },
|
||||
};
|
||||
assert_eq!(img2.to_wgsl(gctx), "texture_depth_cube_array");
|
||||
|
||||
let img3 = crate::TypeInner::Image {
|
||||
dim: crate::ImageDimension::D2,
|
||||
arrayed: false,
|
||||
class: crate::ImageClass::Depth { multi: true },
|
||||
};
|
||||
assert_eq!(img3.to_wgsl(gctx), "texture_depth_multisampled_2d");
|
||||
|
||||
let array = crate::TypeInner::BindingArray {
|
||||
base: mytype1,
|
||||
size: crate::ArraySize::Constant(unsafe { NonZeroU32::new_unchecked(32) }),
|
||||
};
|
||||
assert_eq!(array.to_wgsl(gctx), "binding_array<MyType1, 32>");
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ScalarKind {
|
||||
/// Format a scalar kind+width as a type is written in wgsl.
|
||||
///
|
||||
/// Examples: `f32`, `u64`, `bool`.
|
||||
fn to_wgsl(self, width: u8) -> String {
|
||||
let prefix = match self {
|
||||
crate::ScalarKind::Sint => "i",
|
||||
crate::ScalarKind::Uint => "u",
|
||||
crate::ScalarKind::Float => "f",
|
||||
crate::ScalarKind::Bool => return "bool".to_string(),
|
||||
};
|
||||
format!("{}{}", prefix, width * 8)
|
||||
}
|
||||
}
|
||||
486
third-party/vendor/naga/src/front/wgsl/parse/ast.rs
vendored
Normal file
486
third-party/vendor/naga/src/front/wgsl/parse/ast.rs
vendored
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
use crate::front::wgsl::parse::number::Number;
|
||||
use crate::{Arena, FastHashSet, Handle, Span};
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TranslationUnit<'a> {
|
||||
pub decls: Arena<GlobalDecl<'a>>,
|
||||
/// The common expressions arena for the entire translation unit.
|
||||
///
|
||||
/// All functions, global initializers, array lengths, etc. store their
|
||||
/// expressions here. We apportion these out to individual Naga
|
||||
/// [`Function`]s' expression arenas at lowering time. Keeping them all in a
|
||||
/// single arena simplifies handling of things like array lengths (which are
|
||||
/// effectively global and thus don't clearly belong to any function) and
|
||||
/// initializers (which can appear in both function-local and module-scope
|
||||
/// contexts).
|
||||
///
|
||||
/// [`Function`]: crate::Function
|
||||
pub expressions: Arena<Expression<'a>>,
|
||||
|
||||
/// Non-user-defined types, like `vec4<f32>` or `array<i32, 10>`.
|
||||
///
|
||||
/// These are referred to by `Handle<ast::Type<'a>>` values.
|
||||
/// User-defined types are referred to by name until lowering.
|
||||
pub types: Arena<Type<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Ident<'a> {
|
||||
pub name: &'a str,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IdentExpr<'a> {
|
||||
Unresolved(&'a str),
|
||||
Local(Handle<Local>),
|
||||
}
|
||||
|
||||
/// A reference to a module-scope definition or predeclared object.
|
||||
///
|
||||
/// Each [`GlobalDecl`] holds a set of these values, to be resolved to
|
||||
/// specific definitions later. To support de-duplication, `Eq` and
|
||||
/// `Hash` on a `Dependency` value consider only the name, not the
|
||||
/// source location at which the reference occurs.
|
||||
#[derive(Debug)]
|
||||
pub struct Dependency<'a> {
|
||||
/// The name referred to.
|
||||
pub ident: &'a str,
|
||||
|
||||
/// The location at which the reference to that name occurs.
|
||||
pub usage: Span,
|
||||
}
|
||||
|
||||
impl Hash for Dependency<'_> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.ident.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Dependency<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ident == other.ident
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Dependency<'_> {}
|
||||
|
||||
/// A module-scope declaration.
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalDecl<'a> {
|
||||
pub kind: GlobalDeclKind<'a>,
|
||||
|
||||
/// Names of all module-scope or predeclared objects this
|
||||
/// declaration uses.
|
||||
pub dependencies: FastHashSet<Dependency<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GlobalDeclKind<'a> {
|
||||
Fn(Function<'a>),
|
||||
Var(GlobalVariable<'a>),
|
||||
Const(Const<'a>),
|
||||
Struct(Struct<'a>),
|
||||
Type(TypeAlias<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionArgument<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub ty: Handle<Type<'a>>,
|
||||
pub binding: Option<crate::Binding>,
|
||||
pub handle: Handle<Local>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionResult<'a> {
|
||||
pub ty: Handle<Type<'a>>,
|
||||
pub binding: Option<crate::Binding>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EntryPoint {
|
||||
pub stage: crate::ShaderStage,
|
||||
pub early_depth_test: Option<crate::EarlyDepthTest>,
|
||||
pub workgroup_size: [u32; 3],
|
||||
}
|
||||
|
||||
#[cfg(doc)]
|
||||
use crate::front::wgsl::lower::{RuntimeExpressionContext, StatementContext};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Function<'a> {
|
||||
pub entry_point: Option<EntryPoint>,
|
||||
pub name: Ident<'a>,
|
||||
pub arguments: Vec<FunctionArgument<'a>>,
|
||||
pub result: Option<FunctionResult<'a>>,
|
||||
|
||||
/// Local variable and function argument arena.
|
||||
///
|
||||
/// Note that the `Local` here is actually a zero-sized type. The AST keeps
|
||||
/// all the detailed information about locals - names, types, etc. - in
|
||||
/// [`LocalDecl`] statements. For arguments, that information is kept in
|
||||
/// [`arguments`]. This `Arena`'s only role is to assign a unique `Handle`
|
||||
/// to each of them, and track their definitions' spans for use in
|
||||
/// diagnostics.
|
||||
///
|
||||
/// In the AST, when an [`Ident`] expression refers to a local variable or
|
||||
/// argument, its [`IdentExpr`] holds the referent's `Handle<Local>` in this
|
||||
/// arena.
|
||||
///
|
||||
/// During lowering, [`LocalDecl`] statements add entries to a per-function
|
||||
/// table that maps `Handle<Local>` values to their Naga representations,
|
||||
/// accessed via [`StatementContext::local_table`] and
|
||||
/// [`RuntimeExpressionContext::local_table`]. This table is then consulted when
|
||||
/// lowering subsequent [`Ident`] expressions.
|
||||
///
|
||||
/// [`LocalDecl`]: StatementKind::LocalDecl
|
||||
/// [`arguments`]: Function::arguments
|
||||
/// [`Ident`]: Expression::Ident
|
||||
/// [`StatementContext::local_table`]: StatementContext::local_table
|
||||
/// [`RuntimeExpressionContext::local_table`]: RuntimeExpressionContext::local_table
|
||||
pub locals: Arena<Local>,
|
||||
|
||||
pub body: Block<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalVariable<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub space: crate::AddressSpace,
|
||||
pub binding: Option<crate::ResourceBinding>,
|
||||
pub ty: Handle<Type<'a>>,
|
||||
pub init: Option<Handle<Expression<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StructMember<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub ty: Handle<Type<'a>>,
|
||||
pub binding: Option<crate::Binding>,
|
||||
pub align: Option<(u32, Span)>,
|
||||
pub size: Option<(u32, Span)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Struct<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub members: Vec<StructMember<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TypeAlias<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub ty: Handle<Type<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Const<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub ty: Option<Handle<Type<'a>>>,
|
||||
pub init: Handle<Expression<'a>>,
|
||||
}
|
||||
|
||||
/// The size of an [`Array`] or [`BindingArray`].
|
||||
///
|
||||
/// [`Array`]: Type::Array
|
||||
/// [`BindingArray`]: Type::BindingArray
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ArraySize<'a> {
|
||||
/// The length as a constant expression.
|
||||
Constant(Handle<Expression<'a>>),
|
||||
Dynamic,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Type<'a> {
|
||||
Scalar {
|
||||
kind: crate::ScalarKind,
|
||||
width: crate::Bytes,
|
||||
},
|
||||
Vector {
|
||||
size: crate::VectorSize,
|
||||
kind: crate::ScalarKind,
|
||||
width: crate::Bytes,
|
||||
},
|
||||
Matrix {
|
||||
columns: crate::VectorSize,
|
||||
rows: crate::VectorSize,
|
||||
width: crate::Bytes,
|
||||
},
|
||||
Atomic {
|
||||
kind: crate::ScalarKind,
|
||||
width: crate::Bytes,
|
||||
},
|
||||
Pointer {
|
||||
base: Handle<Type<'a>>,
|
||||
space: crate::AddressSpace,
|
||||
},
|
||||
Array {
|
||||
base: Handle<Type<'a>>,
|
||||
size: ArraySize<'a>,
|
||||
},
|
||||
Image {
|
||||
dim: crate::ImageDimension,
|
||||
arrayed: bool,
|
||||
class: crate::ImageClass,
|
||||
},
|
||||
Sampler {
|
||||
comparison: bool,
|
||||
},
|
||||
AccelerationStructure,
|
||||
RayQuery,
|
||||
RayDesc,
|
||||
RayIntersection,
|
||||
BindingArray {
|
||||
base: Handle<Type<'a>>,
|
||||
size: ArraySize<'a>,
|
||||
},
|
||||
|
||||
/// A user-defined type, like a struct or a type alias.
|
||||
User(Ident<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Block<'a> {
|
||||
pub stmts: Vec<Statement<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Statement<'a> {
|
||||
pub kind: StatementKind<'a>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StatementKind<'a> {
|
||||
LocalDecl(LocalDecl<'a>),
|
||||
Block(Block<'a>),
|
||||
If {
|
||||
condition: Handle<Expression<'a>>,
|
||||
accept: Block<'a>,
|
||||
reject: Block<'a>,
|
||||
},
|
||||
Switch {
|
||||
selector: Handle<Expression<'a>>,
|
||||
cases: Vec<SwitchCase<'a>>,
|
||||
},
|
||||
Loop {
|
||||
body: Block<'a>,
|
||||
continuing: Block<'a>,
|
||||
break_if: Option<Handle<Expression<'a>>>,
|
||||
},
|
||||
Break,
|
||||
Continue,
|
||||
Return {
|
||||
value: Option<Handle<Expression<'a>>>,
|
||||
},
|
||||
Kill,
|
||||
Call {
|
||||
function: Ident<'a>,
|
||||
arguments: Vec<Handle<Expression<'a>>>,
|
||||
},
|
||||
Assign {
|
||||
target: Handle<Expression<'a>>,
|
||||
op: Option<crate::BinaryOperator>,
|
||||
value: Handle<Expression<'a>>,
|
||||
},
|
||||
Increment(Handle<Expression<'a>>),
|
||||
Decrement(Handle<Expression<'a>>),
|
||||
Ignore(Handle<Expression<'a>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SwitchValue {
|
||||
I32(i32),
|
||||
U32(u32),
|
||||
Default,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SwitchCase<'a> {
|
||||
pub value: SwitchValue,
|
||||
pub value_span: Span,
|
||||
pub body: Block<'a>,
|
||||
pub fall_through: bool,
|
||||
}
|
||||
|
||||
/// A type at the head of a [`Construct`] expression.
|
||||
///
|
||||
/// WGSL has two types of [`type constructor expressions`]:
|
||||
///
|
||||
/// - Those that fully specify the type being constructed, like
|
||||
/// `vec3<f32>(x,y,z)`, which obviously constructs a `vec3<f32>`.
|
||||
///
|
||||
/// - Those that leave the component type of the composite being constructed
|
||||
/// implicit, to be inferred from the argument types, like `vec3(x,y,z)`,
|
||||
/// which constructs a `vec3<T>` where `T` is the type of `x`, `y`, and `z`.
|
||||
///
|
||||
/// This enum represents the head type of both cases. The `PartialFoo` variants
|
||||
/// represent the second case, where the component type is implicit.
|
||||
///
|
||||
/// This does not cover structs or types referred to by type aliases. See the
|
||||
/// documentation for [`Construct`] and [`Call`] expressions for details.
|
||||
///
|
||||
/// [`Construct`]: Expression::Construct
|
||||
/// [`type constructor expressions`]: https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr
|
||||
/// [`Call`]: Expression::Call
|
||||
#[derive(Debug)]
|
||||
pub enum ConstructorType<'a> {
|
||||
/// A scalar type or conversion: `f32(1)`.
|
||||
Scalar {
|
||||
kind: crate::ScalarKind,
|
||||
width: crate::Bytes,
|
||||
},
|
||||
|
||||
/// A vector construction whose component type is inferred from the
|
||||
/// argument: `vec3(1.0)`.
|
||||
PartialVector { size: crate::VectorSize },
|
||||
|
||||
/// A vector construction whose component type is written out:
|
||||
/// `vec3<f32>(1.0)`.
|
||||
Vector {
|
||||
size: crate::VectorSize,
|
||||
kind: crate::ScalarKind,
|
||||
width: crate::Bytes,
|
||||
},
|
||||
|
||||
/// A matrix construction whose component type is inferred from the
|
||||
/// argument: `mat2x2(1,2,3,4)`.
|
||||
PartialMatrix {
|
||||
columns: crate::VectorSize,
|
||||
rows: crate::VectorSize,
|
||||
},
|
||||
|
||||
/// A matrix construction whose component type is written out:
|
||||
/// `mat2x2<f32>(1,2,3,4)`.
|
||||
Matrix {
|
||||
columns: crate::VectorSize,
|
||||
rows: crate::VectorSize,
|
||||
width: crate::Bytes,
|
||||
},
|
||||
|
||||
/// An array whose component type and size are inferred from the arguments:
|
||||
/// `array(3,4,5)`.
|
||||
PartialArray,
|
||||
|
||||
/// An array whose component type and size are written out:
|
||||
/// `array<u32, 4>(3,4,5)`.
|
||||
Array {
|
||||
base: Handle<Type<'a>>,
|
||||
size: ArraySize<'a>,
|
||||
},
|
||||
|
||||
/// Constructing a value of a known Naga IR type.
|
||||
///
|
||||
/// This variant is produced only during lowering, when we have Naga types
|
||||
/// available, never during parsing.
|
||||
Type(Handle<crate::Type>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Literal {
|
||||
Bool(bool),
|
||||
Number(Number),
|
||||
}
|
||||
|
||||
#[cfg(doc)]
|
||||
use crate::front::wgsl::lower::Lowerer;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Expression<'a> {
|
||||
Literal(Literal),
|
||||
Ident(IdentExpr<'a>),
|
||||
|
||||
/// A type constructor expression.
|
||||
///
|
||||
/// This is only used for expressions like `KEYWORD(EXPR...)` and
|
||||
/// `KEYWORD<PARAM>(EXPR...)`, where `KEYWORD` is a [type-defining keyword] like
|
||||
/// `vec3`. These keywords cannot be shadowed by user definitions, so we can
|
||||
/// tell that such an expression is a construction immediately.
|
||||
///
|
||||
/// For ordinary identifiers, we can't tell whether an expression like
|
||||
/// `IDENTIFIER(EXPR, ...)` is a construction expression or a function call
|
||||
/// until we know `IDENTIFIER`'s definition, so we represent those as
|
||||
/// [`Call`] expressions.
|
||||
///
|
||||
/// [type-defining keyword]: https://gpuweb.github.io/gpuweb/wgsl/#type-defining-keywords
|
||||
/// [`Call`]: Expression::Call
|
||||
Construct {
|
||||
ty: ConstructorType<'a>,
|
||||
ty_span: Span,
|
||||
components: Vec<Handle<Expression<'a>>>,
|
||||
},
|
||||
Unary {
|
||||
op: crate::UnaryOperator,
|
||||
expr: Handle<Expression<'a>>,
|
||||
},
|
||||
AddrOf(Handle<Expression<'a>>),
|
||||
Deref(Handle<Expression<'a>>),
|
||||
Binary {
|
||||
op: crate::BinaryOperator,
|
||||
left: Handle<Expression<'a>>,
|
||||
right: Handle<Expression<'a>>,
|
||||
},
|
||||
|
||||
/// A function call or type constructor expression.
|
||||
///
|
||||
/// We can't tell whether an expression like `IDENTIFIER(EXPR, ...)` is a
|
||||
/// construction expression or a function call until we know `IDENTIFIER`'s
|
||||
/// definition, so we represent everything of that form as one of these
|
||||
/// expressions until lowering. At that point, [`Lowerer::call`] has
|
||||
/// everything's definition in hand, and can decide whether to emit a Naga
|
||||
/// [`Constant`], [`As`], [`Splat`], or [`Compose`] expression.
|
||||
///
|
||||
/// [`Lowerer::call`]: Lowerer::call
|
||||
/// [`Constant`]: crate::Expression::Constant
|
||||
/// [`As`]: crate::Expression::As
|
||||
/// [`Splat`]: crate::Expression::Splat
|
||||
/// [`Compose`]: crate::Expression::Compose
|
||||
Call {
|
||||
function: Ident<'a>,
|
||||
arguments: Vec<Handle<Expression<'a>>>,
|
||||
},
|
||||
Index {
|
||||
base: Handle<Expression<'a>>,
|
||||
index: Handle<Expression<'a>>,
|
||||
},
|
||||
Member {
|
||||
base: Handle<Expression<'a>>,
|
||||
field: Ident<'a>,
|
||||
},
|
||||
Bitcast {
|
||||
expr: Handle<Expression<'a>>,
|
||||
to: Handle<Type<'a>>,
|
||||
ty_span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LocalVariable<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub ty: Option<Handle<Type<'a>>>,
|
||||
pub init: Option<Handle<Expression<'a>>>,
|
||||
pub handle: Handle<Local>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Let<'a> {
|
||||
pub name: Ident<'a>,
|
||||
pub ty: Option<Handle<Type<'a>>>,
|
||||
pub init: Handle<Expression<'a>>,
|
||||
pub handle: Handle<Local>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LocalDecl<'a> {
|
||||
Var(LocalVariable<'a>),
|
||||
Let(Let<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A placeholder for a local variable declaration.
|
||||
///
|
||||
/// See [`Function::locals`] for more information.
|
||||
pub struct Local;
|
||||
236
third-party/vendor/naga/src/front/wgsl/parse/conv.rs
vendored
Normal file
236
third-party/vendor/naga/src/front/wgsl/parse/conv.rs
vendored
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
use super::Error;
|
||||
use crate::Span;
|
||||
|
||||
pub fn map_address_space(word: &str, span: Span) -> Result<crate::AddressSpace, Error<'_>> {
|
||||
match word {
|
||||
"private" => Ok(crate::AddressSpace::Private),
|
||||
"workgroup" => Ok(crate::AddressSpace::WorkGroup),
|
||||
"uniform" => Ok(crate::AddressSpace::Uniform),
|
||||
"storage" => Ok(crate::AddressSpace::Storage {
|
||||
access: crate::StorageAccess::default(),
|
||||
}),
|
||||
"push_constant" => Ok(crate::AddressSpace::PushConstant),
|
||||
"function" => Ok(crate::AddressSpace::Function),
|
||||
_ => Err(Error::UnknownAddressSpace(span)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_built_in(word: &str, span: Span) -> Result<crate::BuiltIn, Error<'_>> {
|
||||
Ok(match word {
|
||||
"position" => crate::BuiltIn::Position { invariant: false },
|
||||
// vertex
|
||||
"vertex_index" => crate::BuiltIn::VertexIndex,
|
||||
"instance_index" => crate::BuiltIn::InstanceIndex,
|
||||
"view_index" => crate::BuiltIn::ViewIndex,
|
||||
// fragment
|
||||
"front_facing" => crate::BuiltIn::FrontFacing,
|
||||
"frag_depth" => crate::BuiltIn::FragDepth,
|
||||
"primitive_index" => crate::BuiltIn::PrimitiveIndex,
|
||||
"sample_index" => crate::BuiltIn::SampleIndex,
|
||||
"sample_mask" => crate::BuiltIn::SampleMask,
|
||||
// compute
|
||||
"global_invocation_id" => crate::BuiltIn::GlobalInvocationId,
|
||||
"local_invocation_id" => crate::BuiltIn::LocalInvocationId,
|
||||
"local_invocation_index" => crate::BuiltIn::LocalInvocationIndex,
|
||||
"workgroup_id" => crate::BuiltIn::WorkGroupId,
|
||||
"num_workgroups" => crate::BuiltIn::NumWorkGroups,
|
||||
_ => return Err(Error::UnknownBuiltin(span)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map_interpolation(word: &str, span: Span) -> Result<crate::Interpolation, Error<'_>> {
|
||||
match word {
|
||||
"linear" => Ok(crate::Interpolation::Linear),
|
||||
"flat" => Ok(crate::Interpolation::Flat),
|
||||
"perspective" => Ok(crate::Interpolation::Perspective),
|
||||
_ => Err(Error::UnknownAttribute(span)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_sampling(word: &str, span: Span) -> Result<crate::Sampling, Error<'_>> {
|
||||
match word {
|
||||
"center" => Ok(crate::Sampling::Center),
|
||||
"centroid" => Ok(crate::Sampling::Centroid),
|
||||
"sample" => Ok(crate::Sampling::Sample),
|
||||
_ => Err(Error::UnknownAttribute(span)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_storage_format(word: &str, span: Span) -> Result<crate::StorageFormat, Error<'_>> {
|
||||
use crate::StorageFormat as Sf;
|
||||
Ok(match word {
|
||||
"r8unorm" => Sf::R8Unorm,
|
||||
"r8snorm" => Sf::R8Snorm,
|
||||
"r8uint" => Sf::R8Uint,
|
||||
"r8sint" => Sf::R8Sint,
|
||||
"r16unorm" => Sf::R16Unorm,
|
||||
"r16snorm" => Sf::R16Snorm,
|
||||
"r16uint" => Sf::R16Uint,
|
||||
"r16sint" => Sf::R16Sint,
|
||||
"r16float" => Sf::R16Float,
|
||||
"rg8unorm" => Sf::Rg8Unorm,
|
||||
"rg8snorm" => Sf::Rg8Snorm,
|
||||
"rg8uint" => Sf::Rg8Uint,
|
||||
"rg8sint" => Sf::Rg8Sint,
|
||||
"r32uint" => Sf::R32Uint,
|
||||
"r32sint" => Sf::R32Sint,
|
||||
"r32float" => Sf::R32Float,
|
||||
"rg16unorm" => Sf::Rg16Unorm,
|
||||
"rg16snorm" => Sf::Rg16Snorm,
|
||||
"rg16uint" => Sf::Rg16Uint,
|
||||
"rg16sint" => Sf::Rg16Sint,
|
||||
"rg16float" => Sf::Rg16Float,
|
||||
"rgba8unorm" => Sf::Rgba8Unorm,
|
||||
"rgba8snorm" => Sf::Rgba8Snorm,
|
||||
"rgba8uint" => Sf::Rgba8Uint,
|
||||
"rgba8sint" => Sf::Rgba8Sint,
|
||||
"rgb10a2unorm" => Sf::Rgb10a2Unorm,
|
||||
"rg11b10float" => Sf::Rg11b10Float,
|
||||
"rg32uint" => Sf::Rg32Uint,
|
||||
"rg32sint" => Sf::Rg32Sint,
|
||||
"rg32float" => Sf::Rg32Float,
|
||||
"rgba16unorm" => Sf::Rgba16Unorm,
|
||||
"rgba16snorm" => Sf::Rgba16Snorm,
|
||||
"rgba16uint" => Sf::Rgba16Uint,
|
||||
"rgba16sint" => Sf::Rgba16Sint,
|
||||
"rgba16float" => Sf::Rgba16Float,
|
||||
"rgba32uint" => Sf::Rgba32Uint,
|
||||
"rgba32sint" => Sf::Rgba32Sint,
|
||||
"rgba32float" => Sf::Rgba32Float,
|
||||
_ => return Err(Error::UnknownStorageFormat(span)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_scalar_type(word: &str) -> Option<(crate::ScalarKind, crate::Bytes)> {
|
||||
match word {
|
||||
// "f16" => Some((crate::ScalarKind::Float, 2)),
|
||||
"f32" => Some((crate::ScalarKind::Float, 4)),
|
||||
"f64" => Some((crate::ScalarKind::Float, 8)),
|
||||
"i32" => Some((crate::ScalarKind::Sint, 4)),
|
||||
"u32" => Some((crate::ScalarKind::Uint, 4)),
|
||||
"bool" => Some((crate::ScalarKind::Bool, crate::BOOL_WIDTH)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_derivative(word: &str) -> Option<(crate::DerivativeAxis, crate::DerivativeControl)> {
|
||||
use crate::{DerivativeAxis as Axis, DerivativeControl as Ctrl};
|
||||
match word {
|
||||
"dpdxCoarse" => Some((Axis::X, Ctrl::Coarse)),
|
||||
"dpdyCoarse" => Some((Axis::Y, Ctrl::Coarse)),
|
||||
"fwidthCoarse" => Some((Axis::Width, Ctrl::Coarse)),
|
||||
"dpdxFine" => Some((Axis::X, Ctrl::Fine)),
|
||||
"dpdyFine" => Some((Axis::Y, Ctrl::Fine)),
|
||||
"fwidthFine" => Some((Axis::Width, Ctrl::Fine)),
|
||||
"dpdx" => Some((Axis::X, Ctrl::None)),
|
||||
"dpdy" => Some((Axis::Y, Ctrl::None)),
|
||||
"fwidth" => Some((Axis::Width, Ctrl::None)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_relational_fun(word: &str) -> Option<crate::RelationalFunction> {
|
||||
match word {
|
||||
"any" => Some(crate::RelationalFunction::Any),
|
||||
"all" => Some(crate::RelationalFunction::All),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_standard_fun(word: &str) -> Option<crate::MathFunction> {
|
||||
use crate::MathFunction as Mf;
|
||||
Some(match word {
|
||||
// comparison
|
||||
"abs" => Mf::Abs,
|
||||
"min" => Mf::Min,
|
||||
"max" => Mf::Max,
|
||||
"clamp" => Mf::Clamp,
|
||||
"saturate" => Mf::Saturate,
|
||||
// trigonometry
|
||||
"cos" => Mf::Cos,
|
||||
"cosh" => Mf::Cosh,
|
||||
"sin" => Mf::Sin,
|
||||
"sinh" => Mf::Sinh,
|
||||
"tan" => Mf::Tan,
|
||||
"tanh" => Mf::Tanh,
|
||||
"acos" => Mf::Acos,
|
||||
"acosh" => Mf::Acosh,
|
||||
"asin" => Mf::Asin,
|
||||
"asinh" => Mf::Asinh,
|
||||
"atan" => Mf::Atan,
|
||||
"atanh" => Mf::Atanh,
|
||||
"atan2" => Mf::Atan2,
|
||||
"radians" => Mf::Radians,
|
||||
"degrees" => Mf::Degrees,
|
||||
// decomposition
|
||||
"ceil" => Mf::Ceil,
|
||||
"floor" => Mf::Floor,
|
||||
"round" => Mf::Round,
|
||||
"fract" => Mf::Fract,
|
||||
"trunc" => Mf::Trunc,
|
||||
"modf" => Mf::Modf,
|
||||
"frexp" => Mf::Frexp,
|
||||
"ldexp" => Mf::Ldexp,
|
||||
// exponent
|
||||
"exp" => Mf::Exp,
|
||||
"exp2" => Mf::Exp2,
|
||||
"log" => Mf::Log,
|
||||
"log2" => Mf::Log2,
|
||||
"pow" => Mf::Pow,
|
||||
// geometry
|
||||
"dot" => Mf::Dot,
|
||||
"outerProduct" => Mf::Outer,
|
||||
"cross" => Mf::Cross,
|
||||
"distance" => Mf::Distance,
|
||||
"length" => Mf::Length,
|
||||
"normalize" => Mf::Normalize,
|
||||
"faceForward" => Mf::FaceForward,
|
||||
"reflect" => Mf::Reflect,
|
||||
"refract" => Mf::Refract,
|
||||
// computational
|
||||
"sign" => Mf::Sign,
|
||||
"fma" => Mf::Fma,
|
||||
"mix" => Mf::Mix,
|
||||
"step" => Mf::Step,
|
||||
"smoothstep" => Mf::SmoothStep,
|
||||
"sqrt" => Mf::Sqrt,
|
||||
"inverseSqrt" => Mf::InverseSqrt,
|
||||
"transpose" => Mf::Transpose,
|
||||
"determinant" => Mf::Determinant,
|
||||
// bits
|
||||
"countTrailingZeros" => Mf::CountTrailingZeros,
|
||||
"countLeadingZeros" => Mf::CountLeadingZeros,
|
||||
"countOneBits" => Mf::CountOneBits,
|
||||
"reverseBits" => Mf::ReverseBits,
|
||||
"extractBits" => Mf::ExtractBits,
|
||||
"insertBits" => Mf::InsertBits,
|
||||
"firstTrailingBit" => Mf::FindLsb,
|
||||
"firstLeadingBit" => Mf::FindMsb,
|
||||
// data packing
|
||||
"pack4x8snorm" => Mf::Pack4x8snorm,
|
||||
"pack4x8unorm" => Mf::Pack4x8unorm,
|
||||
"pack2x16snorm" => Mf::Pack2x16snorm,
|
||||
"pack2x16unorm" => Mf::Pack2x16unorm,
|
||||
"pack2x16float" => Mf::Pack2x16float,
|
||||
// data unpacking
|
||||
"unpack4x8snorm" => Mf::Unpack4x8snorm,
|
||||
"unpack4x8unorm" => Mf::Unpack4x8unorm,
|
||||
"unpack2x16snorm" => Mf::Unpack2x16snorm,
|
||||
"unpack2x16unorm" => Mf::Unpack2x16unorm,
|
||||
"unpack2x16float" => Mf::Unpack2x16float,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map_conservative_depth(
|
||||
word: &str,
|
||||
span: Span,
|
||||
) -> Result<crate::ConservativeDepth, Error<'_>> {
|
||||
use crate::ConservativeDepth as Cd;
|
||||
match word {
|
||||
"greater_equal" => Ok(Cd::GreaterEqual),
|
||||
"less_equal" => Ok(Cd::LessEqual),
|
||||
"unchanged" => Ok(Cd::Unchanged),
|
||||
_ => Err(Error::UnknownConservativeDepth(span)),
|
||||
}
|
||||
}
|
||||
723
third-party/vendor/naga/src/front/wgsl/parse/lexer.rs
vendored
Normal file
723
third-party/vendor/naga/src/front/wgsl/parse/lexer.rs
vendored
Normal file
|
|
@ -0,0 +1,723 @@
|
|||
use super::{number::consume_number, Error, ExpectedToken};
|
||||
use crate::front::wgsl::error::NumberError;
|
||||
use crate::front::wgsl::parse::{conv, Number};
|
||||
use crate::Span;
|
||||
|
||||
type TokenSpan<'a> = (Token<'a>, Span);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Token<'a> {
|
||||
Separator(char),
|
||||
Paren(char),
|
||||
Attribute,
|
||||
Number(Result<Number, NumberError>),
|
||||
Word(&'a str),
|
||||
Operation(char),
|
||||
LogicalOperation(char),
|
||||
ShiftOperation(char),
|
||||
AssignmentOperation(char),
|
||||
IncrementOperation,
|
||||
DecrementOperation,
|
||||
Arrow,
|
||||
Unknown(char),
|
||||
Trivia,
|
||||
End,
|
||||
}
|
||||
|
||||
fn consume_any(input: &str, what: impl Fn(char) -> bool) -> (&str, &str) {
|
||||
let pos = input.find(|c| !what(c)).unwrap_or(input.len());
|
||||
input.split_at(pos)
|
||||
}
|
||||
|
||||
/// Return the token at the start of `input`.
|
||||
///
|
||||
/// If `generic` is `false`, then the bit shift operators `>>` or `<<`
|
||||
/// are valid lookahead tokens for the current parser state (see [§3.1
|
||||
/// Parsing] in the WGSL specification). In other words:
|
||||
///
|
||||
/// - If `generic` is `true`, then we are expecting an angle bracket
|
||||
/// around a generic type parameter, like the `<` and `>` in
|
||||
/// `vec3<f32>`, so interpret `<` and `>` as `Token::Paren` tokens,
|
||||
/// even if they're part of `<<` or `>>` sequences.
|
||||
///
|
||||
/// - Otherwise, interpret `<<` and `>>` as shift operators:
|
||||
/// `Token::LogicalOperation` tokens.
|
||||
///
|
||||
/// [§3.1 Parsing]: https://gpuweb.github.io/gpuweb/wgsl/#parsing
|
||||
fn consume_token(input: &str, generic: bool) -> (Token<'_>, &str) {
|
||||
let mut chars = input.chars();
|
||||
let cur = match chars.next() {
|
||||
Some(c) => c,
|
||||
None => return (Token::End, ""),
|
||||
};
|
||||
match cur {
|
||||
':' | ';' | ',' => (Token::Separator(cur), chars.as_str()),
|
||||
'.' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('0'..='9') => consume_number(input),
|
||||
_ => (Token::Separator(cur), og_chars),
|
||||
}
|
||||
}
|
||||
'@' => (Token::Attribute, chars.as_str()),
|
||||
'(' | ')' | '{' | '}' | '[' | ']' => (Token::Paren(cur), chars.as_str()),
|
||||
'<' | '>' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('=') if !generic => (Token::LogicalOperation(cur), chars.as_str()),
|
||||
Some(c) if c == cur && !generic => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),
|
||||
_ => (Token::ShiftOperation(cur), og_chars),
|
||||
}
|
||||
}
|
||||
_ => (Token::Paren(cur), og_chars),
|
||||
}
|
||||
}
|
||||
'0'..='9' => consume_number(input),
|
||||
'/' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('/') => {
|
||||
let _ = chars.position(is_comment_end);
|
||||
(Token::Trivia, chars.as_str())
|
||||
}
|
||||
Some('*') => {
|
||||
let mut depth = 1;
|
||||
let mut prev = None;
|
||||
|
||||
for c in &mut chars {
|
||||
match (prev, c) {
|
||||
(Some('*'), '/') => {
|
||||
prev = None;
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
return (Token::Trivia, chars.as_str());
|
||||
}
|
||||
}
|
||||
(Some('/'), '*') => {
|
||||
prev = None;
|
||||
depth += 1;
|
||||
}
|
||||
_ => {
|
||||
prev = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(Token::End, "")
|
||||
}
|
||||
Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),
|
||||
_ => (Token::Operation(cur), og_chars),
|
||||
}
|
||||
}
|
||||
'-' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('>') => (Token::Arrow, chars.as_str()),
|
||||
Some('0'..='9' | '.') => consume_number(input),
|
||||
Some('-') => (Token::DecrementOperation, chars.as_str()),
|
||||
Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),
|
||||
_ => (Token::Operation(cur), og_chars),
|
||||
}
|
||||
}
|
||||
'+' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('+') => (Token::IncrementOperation, chars.as_str()),
|
||||
Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),
|
||||
_ => (Token::Operation(cur), og_chars),
|
||||
}
|
||||
}
|
||||
'*' | '%' | '^' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),
|
||||
_ => (Token::Operation(cur), og_chars),
|
||||
}
|
||||
}
|
||||
'~' => (Token::Operation(cur), chars.as_str()),
|
||||
'=' | '!' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some('=') => (Token::LogicalOperation(cur), chars.as_str()),
|
||||
_ => (Token::Operation(cur), og_chars),
|
||||
}
|
||||
}
|
||||
'&' | '|' => {
|
||||
let og_chars = chars.as_str();
|
||||
match chars.next() {
|
||||
Some(c) if c == cur => (Token::LogicalOperation(cur), chars.as_str()),
|
||||
Some('=') => (Token::AssignmentOperation(cur), chars.as_str()),
|
||||
_ => (Token::Operation(cur), og_chars),
|
||||
}
|
||||
}
|
||||
_ if is_blankspace(cur) => {
|
||||
let (_, rest) = consume_any(input, is_blankspace);
|
||||
(Token::Trivia, rest)
|
||||
}
|
||||
_ if is_word_start(cur) => {
|
||||
let (word, rest) = consume_any(input, is_word_part);
|
||||
(Token::Word(word), rest)
|
||||
}
|
||||
_ => (Token::Unknown(cur), chars.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not a char is a comment end
|
||||
/// (Unicode Pattern_White_Space excluding U+0020, U+0009, U+200E and U+200F)
|
||||
const fn is_comment_end(c: char) -> bool {
|
||||
match c {
|
||||
'\u{000a}'..='\u{000d}' | '\u{0085}' | '\u{2028}' | '\u{2029}' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not a char is a blankspace (Unicode Pattern_White_Space)
|
||||
const fn is_blankspace(c: char) -> bool {
|
||||
match c {
|
||||
'\u{0020}'
|
||||
| '\u{0009}'..='\u{000d}'
|
||||
| '\u{0085}'
|
||||
| '\u{200e}'
|
||||
| '\u{200f}'
|
||||
| '\u{2028}'
|
||||
| '\u{2029}' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not a char is a word start (Unicode XID_Start + '_')
|
||||
fn is_word_start(c: char) -> bool {
|
||||
c == '_' || unicode_xid::UnicodeXID::is_xid_start(c)
|
||||
}
|
||||
|
||||
/// Returns whether or not a char is a word part (Unicode XID_Continue)
|
||||
fn is_word_part(c: char) -> bool {
|
||||
unicode_xid::UnicodeXID::is_xid_continue(c)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(in crate::front::wgsl) struct Lexer<'a> {
|
||||
input: &'a str,
|
||||
pub(in crate::front::wgsl) source: &'a str,
|
||||
// The byte offset of the end of the last non-trivia token.
|
||||
last_end_offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub(in crate::front::wgsl) const fn new(input: &'a str) -> Self {
|
||||
Lexer {
|
||||
input,
|
||||
source: input,
|
||||
last_end_offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the function with a lexer and returns the result of the function as well as the span for everything the function parsed
|
||||
///
|
||||
/// # Examples
|
||||
/// ```ignore
|
||||
/// let lexer = Lexer::new("5");
|
||||
/// let (value, span) = lexer.capture_span(Lexer::next_uint_literal);
|
||||
/// assert_eq!(value, 5);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn capture_span<T, E>(
|
||||
&mut self,
|
||||
inner: impl FnOnce(&mut Self) -> Result<T, E>,
|
||||
) -> Result<(T, Span), E> {
|
||||
let start = self.current_byte_offset();
|
||||
let res = inner(self)?;
|
||||
let end = self.current_byte_offset();
|
||||
Ok((res, Span::from(start..end)))
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn start_byte_offset(&mut self) -> usize {
|
||||
loop {
|
||||
// Eat all trivia because `next` doesn't eat trailing trivia.
|
||||
let (token, rest) = consume_token(self.input, false);
|
||||
if let Token::Trivia = token {
|
||||
self.input = rest;
|
||||
} else {
|
||||
return self.current_byte_offset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_token_and_rest(&mut self) -> (TokenSpan<'a>, &'a str) {
|
||||
let mut cloned = self.clone();
|
||||
let token = cloned.next();
|
||||
let rest = cloned.input;
|
||||
(token, rest)
|
||||
}
|
||||
|
||||
const fn current_byte_offset(&self) -> usize {
|
||||
self.source.len() - self.input.len()
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn span_from(&self, offset: usize) -> Span {
|
||||
Span::from(offset..self.last_end_offset)
|
||||
}
|
||||
|
||||
/// Return the next non-whitespace token from `self`.
|
||||
///
|
||||
/// Assume we are a parse state where bit shift operators may
|
||||
/// occur, but not angle brackets.
|
||||
#[must_use]
|
||||
pub(in crate::front::wgsl) fn next(&mut self) -> TokenSpan<'a> {
|
||||
self.next_impl(false)
|
||||
}
|
||||
|
||||
/// Return the next non-whitespace token from `self`.
|
||||
///
|
||||
/// Assume we are in a parse state where angle brackets may occur,
|
||||
/// but not bit shift operators.
|
||||
#[must_use]
|
||||
pub(in crate::front::wgsl) fn next_generic(&mut self) -> TokenSpan<'a> {
|
||||
self.next_impl(true)
|
||||
}
|
||||
|
||||
/// Return the next non-whitespace token from `self`, with a span.
|
||||
///
|
||||
/// See [`consume_token`] for the meaning of `generic`.
|
||||
fn next_impl(&mut self, generic: bool) -> TokenSpan<'a> {
|
||||
let mut start_byte_offset = self.current_byte_offset();
|
||||
loop {
|
||||
let (token, rest) = consume_token(self.input, generic);
|
||||
self.input = rest;
|
||||
match token {
|
||||
Token::Trivia => start_byte_offset = self.current_byte_offset(),
|
||||
_ => {
|
||||
self.last_end_offset = self.current_byte_offset();
|
||||
return (token, self.span_from(start_byte_offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(in crate::front::wgsl) fn peek(&mut self) -> TokenSpan<'a> {
|
||||
let (token, _) = self.peek_token_and_rest();
|
||||
token
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn expect_span(
|
||||
&mut self,
|
||||
expected: Token<'a>,
|
||||
) -> Result<Span, Error<'a>> {
|
||||
let next = self.next();
|
||||
if next.0 == expected {
|
||||
Ok(next.1)
|
||||
} else {
|
||||
Err(Error::Unexpected(next.1, ExpectedToken::Token(expected)))
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn expect(&mut self, expected: Token<'a>) -> Result<(), Error<'a>> {
|
||||
self.expect_span(expected)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn expect_generic_paren(
|
||||
&mut self,
|
||||
expected: char,
|
||||
) -> Result<(), Error<'a>> {
|
||||
let next = self.next_generic();
|
||||
if next.0 == Token::Paren(expected) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Unexpected(
|
||||
next.1,
|
||||
ExpectedToken::Token(Token::Paren(expected)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// If the next token matches it is skipped and true is returned
|
||||
pub(in crate::front::wgsl) fn skip(&mut self, what: Token<'_>) -> bool {
|
||||
let (peeked_token, rest) = self.peek_token_and_rest();
|
||||
if peeked_token.0 == what {
|
||||
self.input = rest;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn next_ident_with_span(
|
||||
&mut self,
|
||||
) -> Result<(&'a str, Span), Error<'a>> {
|
||||
match self.next() {
|
||||
(Token::Word(word), span) if word == "_" => {
|
||||
Err(Error::InvalidIdentifierUnderscore(span))
|
||||
}
|
||||
(Token::Word(word), span) if word.starts_with("__") => {
|
||||
Err(Error::ReservedIdentifierPrefix(span))
|
||||
}
|
||||
(Token::Word(word), span) => Ok((word, span)),
|
||||
other => Err(Error::Unexpected(other.1, ExpectedToken::Identifier)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn next_ident(
|
||||
&mut self,
|
||||
) -> Result<super::ast::Ident<'a>, Error<'a>> {
|
||||
let ident = self
|
||||
.next_ident_with_span()
|
||||
.map(|(name, span)| super::ast::Ident { name, span })?;
|
||||
|
||||
if crate::keywords::wgsl::RESERVED.contains(&ident.name) {
|
||||
return Err(Error::ReservedKeyword(ident.span));
|
||||
}
|
||||
|
||||
Ok(ident)
|
||||
}
|
||||
|
||||
/// Parses a generic scalar type, for example `<f32>`.
|
||||
pub(in crate::front::wgsl) fn next_scalar_generic(
|
||||
&mut self,
|
||||
) -> Result<(crate::ScalarKind, crate::Bytes), Error<'a>> {
|
||||
self.expect_generic_paren('<')?;
|
||||
let pair = match self.next() {
|
||||
(Token::Word(word), span) => {
|
||||
conv::get_scalar_type(word).ok_or(Error::UnknownScalarType(span))
|
||||
}
|
||||
(_, span) => Err(Error::UnknownScalarType(span)),
|
||||
}?;
|
||||
self.expect_generic_paren('>')?;
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
/// Parses a generic scalar type, for example `<f32>`.
|
||||
///
|
||||
/// Returns the span covering the inner type, excluding the brackets.
|
||||
pub(in crate::front::wgsl) fn next_scalar_generic_with_span(
|
||||
&mut self,
|
||||
) -> Result<(crate::ScalarKind, crate::Bytes, Span), Error<'a>> {
|
||||
self.expect_generic_paren('<')?;
|
||||
let pair = match self.next() {
|
||||
(Token::Word(word), span) => conv::get_scalar_type(word)
|
||||
.map(|(a, b)| (a, b, span))
|
||||
.ok_or(Error::UnknownScalarType(span)),
|
||||
(_, span) => Err(Error::UnknownScalarType(span)),
|
||||
}?;
|
||||
self.expect_generic_paren('>')?;
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn next_storage_access(
|
||||
&mut self,
|
||||
) -> Result<crate::StorageAccess, Error<'a>> {
|
||||
let (ident, span) = self.next_ident_with_span()?;
|
||||
match ident {
|
||||
"read" => Ok(crate::StorageAccess::LOAD),
|
||||
"write" => Ok(crate::StorageAccess::STORE),
|
||||
"read_write" => Ok(crate::StorageAccess::LOAD | crate::StorageAccess::STORE),
|
||||
_ => Err(Error::UnknownAccess(span)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn next_format_generic(
|
||||
&mut self,
|
||||
) -> Result<(crate::StorageFormat, crate::StorageAccess), Error<'a>> {
|
||||
self.expect(Token::Paren('<'))?;
|
||||
let (ident, ident_span) = self.next_ident_with_span()?;
|
||||
let format = conv::map_storage_format(ident, ident_span)?;
|
||||
self.expect(Token::Separator(','))?;
|
||||
let access = self.next_storage_access()?;
|
||||
self.expect(Token::Paren('>'))?;
|
||||
Ok((format, access))
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn open_arguments(&mut self) -> Result<(), Error<'a>> {
|
||||
self.expect(Token::Paren('('))
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn close_arguments(&mut self) -> Result<(), Error<'a>> {
|
||||
let _ = self.skip(Token::Separator(','));
|
||||
self.expect(Token::Paren(')'))
|
||||
}
|
||||
|
||||
pub(in crate::front::wgsl) fn next_argument(&mut self) -> Result<bool, Error<'a>> {
|
||||
let paren = Token::Paren(')');
|
||||
if self.skip(Token::Separator(',')) {
|
||||
Ok(!self.skip(paren))
|
||||
} else {
|
||||
self.expect(paren).map(|()| false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn sub_test(source: &str, expected_tokens: &[Token]) {
|
||||
let mut lex = Lexer::new(source);
|
||||
for &token in expected_tokens {
|
||||
assert_eq!(lex.next().0, token);
|
||||
}
|
||||
assert_eq!(lex.next().0, Token::End);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_numbers() {
|
||||
// WGSL spec examples //
|
||||
|
||||
// decimal integer
|
||||
sub_test(
|
||||
"0x123 0X123u 1u 123 0 0i 0x3f",
|
||||
&[
|
||||
Token::Number(Ok(Number::I32(291))),
|
||||
Token::Number(Ok(Number::U32(291))),
|
||||
Token::Number(Ok(Number::U32(1))),
|
||||
Token::Number(Ok(Number::I32(123))),
|
||||
Token::Number(Ok(Number::I32(0))),
|
||||
Token::Number(Ok(Number::I32(0))),
|
||||
Token::Number(Ok(Number::I32(63))),
|
||||
],
|
||||
);
|
||||
// decimal floating point
|
||||
sub_test(
|
||||
"0.e+4f 01. .01 12.34 .0f 0h 1e-3 0xa.fp+2 0x1P+4f 0X.3 0x3p+2h 0X1.fp-4 0x3.2p+2h",
|
||||
&[
|
||||
Token::Number(Ok(Number::F32(0.))),
|
||||
Token::Number(Ok(Number::F32(1.))),
|
||||
Token::Number(Ok(Number::F32(0.01))),
|
||||
Token::Number(Ok(Number::F32(12.34))),
|
||||
Token::Number(Ok(Number::F32(0.))),
|
||||
Token::Number(Err(NumberError::UnimplementedF16)),
|
||||
Token::Number(Ok(Number::F32(0.001))),
|
||||
Token::Number(Ok(Number::F32(43.75))),
|
||||
Token::Number(Ok(Number::F32(16.))),
|
||||
Token::Number(Ok(Number::F32(0.1875))),
|
||||
Token::Number(Err(NumberError::UnimplementedF16)),
|
||||
Token::Number(Ok(Number::F32(0.12109375))),
|
||||
Token::Number(Err(NumberError::UnimplementedF16)),
|
||||
],
|
||||
);
|
||||
|
||||
// MIN / MAX //
|
||||
|
||||
// min / max decimal signed integer
|
||||
sub_test(
|
||||
"-2147483648i 2147483647i -2147483649i 2147483648i",
|
||||
&[
|
||||
Token::Number(Ok(Number::I32(i32::MIN))),
|
||||
Token::Number(Ok(Number::I32(i32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
// min / max decimal unsigned integer
|
||||
sub_test(
|
||||
"0u 4294967295u -1u 4294967296u",
|
||||
&[
|
||||
Token::Number(Ok(Number::U32(u32::MIN))),
|
||||
Token::Number(Ok(Number::U32(u32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
|
||||
// min / max hexadecimal signed integer
|
||||
sub_test(
|
||||
"-0x80000000i 0x7FFFFFFFi -0x80000001i 0x80000000i",
|
||||
&[
|
||||
Token::Number(Ok(Number::I32(i32::MIN))),
|
||||
Token::Number(Ok(Number::I32(i32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
// min / max hexadecimal unsigned integer
|
||||
sub_test(
|
||||
"0x0u 0xFFFFFFFFu -0x1u 0x100000000u",
|
||||
&[
|
||||
Token::Number(Ok(Number::U32(u32::MIN))),
|
||||
Token::Number(Ok(Number::U32(u32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
|
||||
/// ≈ 2^-126 * 2^−23 (= 2^−149)
|
||||
const SMALLEST_POSITIVE_SUBNORMAL_F32: f32 = 1e-45;
|
||||
/// ≈ 2^-126 * (1 − 2^−23)
|
||||
const LARGEST_SUBNORMAL_F32: f32 = 1.1754942e-38;
|
||||
/// ≈ 2^-126
|
||||
const SMALLEST_POSITIVE_NORMAL_F32: f32 = f32::MIN_POSITIVE;
|
||||
/// ≈ 1 − 2^−24
|
||||
const LARGEST_F32_LESS_THAN_ONE: f32 = 0.99999994;
|
||||
/// ≈ 1 + 2^−23
|
||||
const SMALLEST_F32_LARGER_THAN_ONE: f32 = 1.0000001;
|
||||
/// ≈ -(2^127 * (2 − 2^−23))
|
||||
const SMALLEST_NORMAL_F32: f32 = f32::MIN;
|
||||
/// ≈ 2^127 * (2 − 2^−23)
|
||||
const LARGEST_NORMAL_F32: f32 = f32::MAX;
|
||||
|
||||
// decimal floating point
|
||||
sub_test(
|
||||
"1e-45f 1.1754942e-38f 1.17549435e-38f 0.99999994f 1.0000001f -3.40282347e+38f 3.40282347e+38f",
|
||||
&[
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_F32_LESS_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_F32_LARGER_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_NORMAL_F32,
|
||||
))),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"-3.40282367e+38f 3.40282367e+38f",
|
||||
&[
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // ≈ -2^128
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // ≈ 2^128
|
||||
],
|
||||
);
|
||||
|
||||
// hexadecimal floating point
|
||||
sub_test(
|
||||
"0x1p-149f 0x7FFFFFp-149f 0x1p-126f 0xFFFFFFp-24f 0x800001p-23f -0xFFFFFFp+104f 0xFFFFFFp+104f",
|
||||
&[
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_F32_LESS_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_F32_LARGER_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_NORMAL_F32,
|
||||
))),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"-0x1p128f 0x1p128f 0x1.000001p0f",
|
||||
&[
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // = -2^128
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // = 2^128
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tokens() {
|
||||
sub_test("id123_OK", &[Token::Word("id123_OK")]);
|
||||
sub_test(
|
||||
"92No",
|
||||
&[Token::Number(Ok(Number::I32(92))), Token::Word("No")],
|
||||
);
|
||||
sub_test(
|
||||
"2u3o",
|
||||
&[
|
||||
Token::Number(Ok(Number::U32(2))),
|
||||
Token::Number(Ok(Number::I32(3))),
|
||||
Token::Word("o"),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"2.4f44po",
|
||||
&[
|
||||
Token::Number(Ok(Number::F32(2.4))),
|
||||
Token::Number(Ok(Number::I32(44))),
|
||||
Token::Word("po"),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"Δέλτα réflexion Кызыл 𐰓𐰏𐰇 朝焼け سلام 검정 שָׁלוֹם गुलाबी փիրուզ",
|
||||
&[
|
||||
Token::Word("Δέλτα"),
|
||||
Token::Word("réflexion"),
|
||||
Token::Word("Кызыл"),
|
||||
Token::Word("𐰓𐰏𐰇"),
|
||||
Token::Word("朝焼け"),
|
||||
Token::Word("سلام"),
|
||||
Token::Word("검정"),
|
||||
Token::Word("שָׁלוֹם"),
|
||||
Token::Word("गुलाबी"),
|
||||
Token::Word("փիրուզ"),
|
||||
],
|
||||
);
|
||||
sub_test("æNoø", &[Token::Word("æNoø")]);
|
||||
sub_test("No¾", &[Token::Word("No"), Token::Unknown('¾')]);
|
||||
sub_test("No好", &[Token::Word("No好")]);
|
||||
sub_test("_No", &[Token::Word("_No")]);
|
||||
sub_test(
|
||||
"*/*/***/*//=/*****//",
|
||||
&[
|
||||
Token::Operation('*'),
|
||||
Token::AssignmentOperation('/'),
|
||||
Token::Operation('/'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_decl() {
|
||||
sub_test(
|
||||
"@group(0 ) var< uniform> texture: texture_multisampled_2d <f32 >;",
|
||||
&[
|
||||
Token::Attribute,
|
||||
Token::Word("group"),
|
||||
Token::Paren('('),
|
||||
Token::Number(Ok(Number::I32(0))),
|
||||
Token::Paren(')'),
|
||||
Token::Word("var"),
|
||||
Token::Paren('<'),
|
||||
Token::Word("uniform"),
|
||||
Token::Paren('>'),
|
||||
Token::Word("texture"),
|
||||
Token::Separator(':'),
|
||||
Token::Word("texture_multisampled_2d"),
|
||||
Token::Paren('<'),
|
||||
Token::Word("f32"),
|
||||
Token::Paren('>'),
|
||||
Token::Separator(';'),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"var<storage,read_write> buffer: array<u32>;",
|
||||
&[
|
||||
Token::Word("var"),
|
||||
Token::Paren('<'),
|
||||
Token::Word("storage"),
|
||||
Token::Separator(','),
|
||||
Token::Word("read_write"),
|
||||
Token::Paren('>'),
|
||||
Token::Word("buffer"),
|
||||
Token::Separator(':'),
|
||||
Token::Word("array"),
|
||||
Token::Paren('<'),
|
||||
Token::Word("u32"),
|
||||
Token::Paren('>'),
|
||||
Token::Separator(';'),
|
||||
],
|
||||
);
|
||||
}
|
||||
2307
third-party/vendor/naga/src/front/wgsl/parse/mod.rs
vendored
Normal file
2307
third-party/vendor/naga/src/front/wgsl/parse/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
443
third-party/vendor/naga/src/front/wgsl/parse/number.rs
vendored
Normal file
443
third-party/vendor/naga/src/front/wgsl/parse/number.rs
vendored
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::front::wgsl::error::NumberError;
|
||||
use crate::front::wgsl::parse::lexer::Token;
|
||||
|
||||
/// When using this type assume no Abstract Int/Float for now
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Number {
|
||||
/// Abstract Int (-2^63 ≤ i < 2^63)
|
||||
AbstractInt(i64),
|
||||
/// Abstract Float (IEEE-754 binary64)
|
||||
AbstractFloat(f64),
|
||||
/// Concrete i32
|
||||
I32(i32),
|
||||
/// Concrete u32
|
||||
U32(u32),
|
||||
/// Concrete f32
|
||||
F32(f32),
|
||||
}
|
||||
|
||||
impl Number {
|
||||
/// Convert abstract numbers to a plausible concrete counterpart.
|
||||
///
|
||||
/// Return concrete numbers unchanged. If the conversion would be
|
||||
/// lossy, return an error.
|
||||
fn abstract_to_concrete(self) -> Result<Number, NumberError> {
|
||||
match self {
|
||||
Number::AbstractInt(num) => i32::try_from(num)
|
||||
.map(Number::I32)
|
||||
.map_err(|_| NumberError::NotRepresentable),
|
||||
Number::AbstractFloat(num) => {
|
||||
let num = num as f32;
|
||||
if num.is_finite() {
|
||||
Ok(Number::F32(num))
|
||||
} else {
|
||||
Err(NumberError::NotRepresentable)
|
||||
}
|
||||
}
|
||||
num => Ok(num),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: when implementing Creation-Time Expressions, remove the ability to match the minus sign
|
||||
|
||||
pub(in crate::front::wgsl) fn consume_number(input: &str) -> (Token<'_>, &str) {
|
||||
let (result, rest) = parse(input);
|
||||
(
|
||||
Token::Number(result.and_then(Number::abstract_to_concrete)),
|
||||
rest,
|
||||
)
|
||||
}
|
||||
|
||||
enum Kind {
|
||||
Int(IntKind),
|
||||
Float(FloatKind),
|
||||
}
|
||||
|
||||
enum IntKind {
|
||||
I32,
|
||||
U32,
|
||||
}
|
||||
|
||||
enum FloatKind {
|
||||
F32,
|
||||
F16,
|
||||
}
|
||||
|
||||
// The following regexes (from the WGSL spec) will be matched:
|
||||
|
||||
// int_literal:
|
||||
// | / 0 [iu]? /
|
||||
// | / [1-9][0-9]* [iu]? /
|
||||
// | / 0[xX][0-9a-fA-F]+ [iu]? /
|
||||
|
||||
// decimal_float_literal:
|
||||
// | / 0 [fh] /
|
||||
// | / [1-9][0-9]* [fh] /
|
||||
// | / [0-9]* \.[0-9]+ ([eE][+-]?[0-9]+)? [fh]? /
|
||||
// | / [0-9]+ \.[0-9]* ([eE][+-]?[0-9]+)? [fh]? /
|
||||
// | / [0-9]+ [eE][+-]?[0-9]+ [fh]? /
|
||||
|
||||
// hex_float_literal:
|
||||
// | / 0[xX][0-9a-fA-F]* \.[0-9a-fA-F]+ ([pP][+-]?[0-9]+ [fh]?)? /
|
||||
// | / 0[xX][0-9a-fA-F]+ \.[0-9a-fA-F]* ([pP][+-]?[0-9]+ [fh]?)? /
|
||||
// | / 0[xX][0-9a-fA-F]+ [pP][+-]?[0-9]+ [fh]? /
|
||||
|
||||
// You could visualize the regex below via https://debuggex.com to get a rough idea what `parse` is doing
|
||||
// -?(?:0[xX](?:([0-9a-fA-F]+\.[0-9a-fA-F]*|[0-9a-fA-F]*\.[0-9a-fA-F]+)(?:([pP][+-]?[0-9]+)([fh]?))?|([0-9a-fA-F]+)([pP][+-]?[0-9]+)([fh]?)|([0-9a-fA-F]+)([iu]?))|((?:[0-9]+[eE][+-]?[0-9]+|(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?))([fh]?)|((?:[0-9]|[1-9][0-9]+))([iufh]?))
|
||||
|
||||
fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
|
||||
/// returns `true` and consumes `X` bytes from the given byte buffer
|
||||
/// if the given `X` nr of patterns are found at the start of the buffer
|
||||
macro_rules! consume {
|
||||
($bytes:ident, $($pattern:pat),*) => {
|
||||
match $bytes {
|
||||
&[$($pattern),*, ref rest @ ..] => { $bytes = rest; true },
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// consumes one byte from the given byte buffer
|
||||
/// if one of the given patterns are found at the start of the buffer
|
||||
/// returning the corresponding expr for the matched pattern
|
||||
macro_rules! consume_map {
|
||||
($bytes:ident, [$($pattern:pat_param => $to:expr),*]) => {
|
||||
match $bytes {
|
||||
$( &[$pattern, ref rest @ ..] => { $bytes = rest; Some($to) }, )*
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// consumes all consecutive bytes matched by the `0-9` pattern from the given byte buffer
|
||||
/// returning the number of consumed bytes
|
||||
macro_rules! consume_dec_digits {
|
||||
($bytes:ident) => {{
|
||||
let start_len = $bytes.len();
|
||||
while let &[b'0'..=b'9', ref rest @ ..] = $bytes {
|
||||
$bytes = rest;
|
||||
}
|
||||
start_len - $bytes.len()
|
||||
}};
|
||||
}
|
||||
|
||||
/// consumes all consecutive bytes matched by the `0-9 | a-f | A-F` pattern from the given byte buffer
|
||||
/// returning the number of consumed bytes
|
||||
macro_rules! consume_hex_digits {
|
||||
($bytes:ident) => {{
|
||||
let start_len = $bytes.len();
|
||||
while let &[b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F', ref rest @ ..] = $bytes {
|
||||
$bytes = rest;
|
||||
}
|
||||
start_len - $bytes.len()
|
||||
}};
|
||||
}
|
||||
|
||||
/// maps the given `&[u8]` (tail of the initial `input: &str`) to a `&str`
|
||||
macro_rules! rest_to_str {
|
||||
($bytes:ident) => {
|
||||
&input[input.len() - $bytes.len()..]
|
||||
};
|
||||
}
|
||||
|
||||
struct ExtractSubStr<'a>(&'a str);
|
||||
|
||||
impl<'a> ExtractSubStr<'a> {
|
||||
/// given an `input` and a `start` (tail of the `input`)
|
||||
/// creates a new [`ExtractSubStr`](`Self`)
|
||||
fn start(input: &'a str, start: &'a [u8]) -> Self {
|
||||
let start = input.len() - start.len();
|
||||
Self(&input[start..])
|
||||
}
|
||||
/// given an `end` (tail of the initial `input`)
|
||||
/// returns a substring of `input`
|
||||
fn end(&self, end: &'a [u8]) -> &'a str {
|
||||
let end = self.0.len() - end.len();
|
||||
&self.0[..end]
|
||||
}
|
||||
}
|
||||
|
||||
let mut bytes = input.as_bytes();
|
||||
|
||||
let general_extract = ExtractSubStr::start(input, bytes);
|
||||
|
||||
let is_negative = consume!(bytes, b'-');
|
||||
|
||||
if consume!(bytes, b'0', b'x' | b'X') {
|
||||
let digits_extract = ExtractSubStr::start(input, bytes);
|
||||
|
||||
let consumed = consume_hex_digits!(bytes);
|
||||
|
||||
if consume!(bytes, b'.') {
|
||||
let consumed_after_period = consume_hex_digits!(bytes);
|
||||
|
||||
if consumed + consumed_after_period == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let significand = general_extract.end(bytes);
|
||||
|
||||
if consume!(bytes, b'p' | b'P') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let number = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(parse_hex_float(number, kind), rest_to_str!(bytes))
|
||||
} else {
|
||||
(
|
||||
parse_hex_float_missing_exponent(significand, None),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let significand = general_extract.end(bytes);
|
||||
let digits = digits_extract.end(bytes);
|
||||
|
||||
let exp_extract = ExtractSubStr::start(input, bytes);
|
||||
|
||||
if consume!(bytes, b'p' | b'P') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let exponent = exp_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(
|
||||
parse_hex_float_missing_period(significand, exponent, kind),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
} else {
|
||||
let kind = consume_map!(bytes, [b'i' => IntKind::I32, b'u' => IntKind::U32]);
|
||||
|
||||
(
|
||||
parse_hex_int(is_negative, digits, kind),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let is_first_zero = bytes.first() == Some(&b'0');
|
||||
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consume!(bytes, b'.') {
|
||||
let consumed_after_period = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed + consumed_after_period == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
if consume!(bytes, b'e' | b'E') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
let number = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(parse_dec_float(number, kind), rest_to_str!(bytes))
|
||||
} else {
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
if consume!(bytes, b'e' | b'E') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let number = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(parse_dec_float(number, kind), rest_to_str!(bytes))
|
||||
} else {
|
||||
// make sure the multi-digit numbers don't start with zero
|
||||
if consumed > 1 && is_first_zero {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let digits_with_sign = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [
|
||||
b'i' => Kind::Int(IntKind::I32),
|
||||
b'u' => Kind::Int(IntKind::U32),
|
||||
b'f' => Kind::Float(FloatKind::F32),
|
||||
b'h' => Kind::Float(FloatKind::F16)
|
||||
]);
|
||||
|
||||
(
|
||||
parse_dec(is_negative, digits_with_sign, kind),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_hex_float_missing_exponent(
|
||||
// format: -?0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ )
|
||||
significand: &str,
|
||||
kind: Option<FloatKind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
let hexf_input = format!("{}{}", significand, "p0");
|
||||
parse_hex_float(&hexf_input, kind)
|
||||
}
|
||||
|
||||
fn parse_hex_float_missing_period(
|
||||
// format: -?0[xX] [0-9a-fA-F]+
|
||||
significand: &str,
|
||||
// format: [pP][+-]?[0-9]+
|
||||
exponent: &str,
|
||||
kind: Option<FloatKind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
let hexf_input = format!("{significand}.{exponent}");
|
||||
parse_hex_float(&hexf_input, kind)
|
||||
}
|
||||
|
||||
fn parse_hex_int(
|
||||
is_negative: bool,
|
||||
// format: [0-9a-fA-F]+
|
||||
digits: &str,
|
||||
kind: Option<IntKind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
let digits_with_sign = if is_negative {
|
||||
Cow::Owned(format!("-{digits}"))
|
||||
} else {
|
||||
Cow::Borrowed(digits)
|
||||
};
|
||||
parse_int(&digits_with_sign, kind, 16, is_negative)
|
||||
}
|
||||
|
||||
fn parse_dec(
|
||||
is_negative: bool,
|
||||
// format: -? ( [0-9] | [1-9][0-9]+ )
|
||||
digits_with_sign: &str,
|
||||
kind: Option<Kind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
match kind {
|
||||
None => parse_int(digits_with_sign, None, 10, is_negative),
|
||||
Some(Kind::Int(kind)) => parse_int(digits_with_sign, Some(kind), 10, is_negative),
|
||||
Some(Kind::Float(kind)) => parse_dec_float(digits_with_sign, Some(kind)),
|
||||
}
|
||||
}
|
||||
|
||||
// Float parsing notes
|
||||
|
||||
// The following chapters of IEEE 754-2019 are relevant:
|
||||
//
|
||||
// 7.4 Overflow (largest finite number is exceeded by what would have been
|
||||
// the rounded floating-point result were the exponent range unbounded)
|
||||
//
|
||||
// 7.5 Underflow (tiny non-zero result is detected;
|
||||
// for decimal formats tininess is detected before rounding when a non-zero result
|
||||
// computed as though both the exponent range and the precision were unbounded
|
||||
// would lie strictly between 2^−126)
|
||||
//
|
||||
// 7.6 Inexact (rounded result differs from what would have been computed
|
||||
// were both exponent range and precision unbounded)
|
||||
|
||||
// The WGSL spec requires us to error:
|
||||
// on overflow for decimal floating point literals
|
||||
// on overflow and inexact for hexadecimal floating point literals
|
||||
// (underflow is not mentioned)
|
||||
|
||||
// hexf_parse errors on overflow, underflow, inexact
|
||||
// rust std lib float from str handles overflow, underflow, inexact transparently (rounds and will not error)
|
||||
|
||||
// Therefore we only check for overflow manually for decimal floating point literals
|
||||
|
||||
// input format: -?0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ ) [pP][+-]?[0-9]+
|
||||
fn parse_hex_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {
|
||||
match kind {
|
||||
None => match hexf_parse::parse_hexf64(input, false) {
|
||||
Ok(num) => Ok(Number::AbstractFloat(num)),
|
||||
// can only be ParseHexfErrorKind::Inexact but we can't check since it's private
|
||||
_ => Err(NumberError::NotRepresentable),
|
||||
},
|
||||
Some(FloatKind::F32) => match hexf_parse::parse_hexf32(input, false) {
|
||||
Ok(num) => Ok(Number::F32(num)),
|
||||
// can only be ParseHexfErrorKind::Inexact but we can't check since it's private
|
||||
_ => Err(NumberError::NotRepresentable),
|
||||
},
|
||||
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
|
||||
}
|
||||
}
|
||||
|
||||
// input format: -? ( [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) ([eE][+-]?[0-9]+)?
|
||||
// | -? [0-9]+ [eE][+-]?[0-9]+
|
||||
fn parse_dec_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {
|
||||
match kind {
|
||||
None => {
|
||||
let num = input.parse::<f64>().unwrap(); // will never fail
|
||||
num.is_finite()
|
||||
.then_some(Number::AbstractFloat(num))
|
||||
.ok_or(NumberError::NotRepresentable)
|
||||
}
|
||||
Some(FloatKind::F32) => {
|
||||
let num = input.parse::<f32>().unwrap(); // will never fail
|
||||
num.is_finite()
|
||||
.then_some(Number::F32(num))
|
||||
.ok_or(NumberError::NotRepresentable)
|
||||
}
|
||||
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_int(
|
||||
input: &str,
|
||||
kind: Option<IntKind>,
|
||||
radix: u32,
|
||||
is_negative: bool,
|
||||
) -> Result<Number, NumberError> {
|
||||
fn map_err(e: core::num::ParseIntError) -> NumberError {
|
||||
match *e.kind() {
|
||||
core::num::IntErrorKind::PosOverflow | core::num::IntErrorKind::NegOverflow => {
|
||||
NumberError::NotRepresentable
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
match kind {
|
||||
None => match i64::from_str_radix(input, radix) {
|
||||
Ok(num) => Ok(Number::AbstractInt(num)),
|
||||
Err(e) => Err(map_err(e)),
|
||||
},
|
||||
Some(IntKind::I32) => match i32::from_str_radix(input, radix) {
|
||||
Ok(num) => Ok(Number::I32(num)),
|
||||
Err(e) => Err(map_err(e)),
|
||||
},
|
||||
Some(IntKind::U32) if is_negative => Err(NumberError::NotRepresentable),
|
||||
Some(IntKind::U32) => match u32::from_str_radix(input, radix) {
|
||||
Ok(num) => Ok(Number::U32(num)),
|
||||
Err(e) => Err(map_err(e)),
|
||||
},
|
||||
}
|
||||
}
|
||||
483
third-party/vendor/naga/src/front/wgsl/tests.rs
vendored
Normal file
483
third-party/vendor/naga/src/front/wgsl/tests.rs
vendored
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
use super::parse_str;
|
||||
|
||||
#[test]
|
||||
fn parse_comment() {
|
||||
parse_str(
|
||||
"//
|
||||
////
|
||||
///////////////////////////////////////////////////////// asda
|
||||
//////////////////// dad ////////// /
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_types() {
|
||||
parse_str("const a : i32 = 2;").unwrap();
|
||||
assert!(parse_str("const a : x32 = 2;").is_err());
|
||||
parse_str("var t: texture_2d<f32>;").unwrap();
|
||||
parse_str("var t: texture_cube_array<i32>;").unwrap();
|
||||
parse_str("var t: texture_multisampled_2d<u32>;").unwrap();
|
||||
parse_str("var t: texture_storage_1d<rgba8uint,write>;").unwrap();
|
||||
parse_str("var t: texture_storage_3d<r32float,read>;").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_inference() {
|
||||
parse_str(
|
||||
"
|
||||
fn foo() {
|
||||
let a = 2u;
|
||||
let b: u32 = a;
|
||||
var x = 3.;
|
||||
var y = vec2<f32>(1, 2);
|
||||
}",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(parse_str(
|
||||
"
|
||||
fn foo() { let c : i32 = 2.0; }",
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_type_cast() {
|
||||
parse_str(
|
||||
"
|
||||
const a : i32 = 2;
|
||||
fn main() {
|
||||
var x: f32 = f32(a);
|
||||
x = f32(i32(a + 1) / 2);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
let x: vec2<f32> = vec2<f32>(1.0, 2.0);
|
||||
let y: vec2<u32> = vec2<u32>(x);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
let x: vec2<f32> = vec2<f32>(0.0);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(parse_str(
|
||||
"
|
||||
fn main() {
|
||||
let x: vec2<f32> = vec2<f32>(0);
|
||||
}
|
||||
",
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_struct() {
|
||||
parse_str(
|
||||
"
|
||||
struct Foo { x: i32 }
|
||||
struct Bar {
|
||||
@size(16) x: vec2<i32>,
|
||||
@align(16) y: f32,
|
||||
@size(32) @align(128) z: vec3<f32>,
|
||||
};
|
||||
struct Empty {}
|
||||
var<storage,read_write> s: Foo;
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_standard_fun() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var x: i32 = min(max(1, 2), 3);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_statement() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
;
|
||||
{}
|
||||
{;}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
parse_str(
|
||||
"
|
||||
fn foo() {}
|
||||
fn bar() { foo(); }
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_if() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
if true {
|
||||
discard;
|
||||
} else {}
|
||||
if 0 != 1 {}
|
||||
if false {
|
||||
return;
|
||||
} else if true {
|
||||
return;
|
||||
} else {}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_parentheses_if() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
if (true) {
|
||||
discard;
|
||||
} else {}
|
||||
if (0 != 1) {}
|
||||
if (false) {
|
||||
return;
|
||||
} else if (true) {
|
||||
return;
|
||||
} else {}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_loop() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var i: i32 = 0;
|
||||
loop {
|
||||
if i == 1 { break; }
|
||||
continuing { i = 1; }
|
||||
}
|
||||
loop {
|
||||
if i == 0 { continue; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var found: bool = false;
|
||||
var i: i32 = 0;
|
||||
while !found {
|
||||
if i == 10 {
|
||||
found = true;
|
||||
}
|
||||
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
while true {
|
||||
break;
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var a: i32 = 0;
|
||||
for(var i: i32 = 0; i < 4; i = i + 1) {
|
||||
a = a + 2;
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
for(;;) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_switch() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var pos: f32;
|
||||
switch (3) {
|
||||
case 0, 1: { pos = 0.0; }
|
||||
case 2: { pos = 1.0; }
|
||||
default: { pos = 3.0; }
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_switch_optional_colon_in_case() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var pos: f32;
|
||||
switch (3) {
|
||||
case 0, 1 { pos = 0.0; }
|
||||
case 2 { pos = 1.0; }
|
||||
default { pos = 3.0; }
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_switch_default_in_case() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var pos: f32;
|
||||
switch (3) {
|
||||
case 0, 1: { pos = 0.0; }
|
||||
case 2: {}
|
||||
case default, 3: { pos = 3.0; }
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_parentheses_switch() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var pos: f32;
|
||||
switch pos > 1.0 {
|
||||
default: { pos = 3.0; }
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_texture_load() {
|
||||
parse_str(
|
||||
"
|
||||
var t: texture_3d<u32>;
|
||||
fn foo() {
|
||||
let r: vec4<u32> = textureLoad(t, vec3<u32>(0.0, 1.0, 2.0), 1);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
var t: texture_multisampled_2d_array<i32>;
|
||||
fn foo() {
|
||||
let r: vec4<i32> = textureLoad(t, vec2<i32>(10, 20), 2, 3);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
var t: texture_storage_1d_array<r32float,read>;
|
||||
fn foo() {
|
||||
let r: vec4<f32> = textureLoad(t, 10, 2);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_texture_store() {
|
||||
parse_str(
|
||||
"
|
||||
var t: texture_storage_2d<rgba8unorm,write>;
|
||||
fn foo() {
|
||||
textureStore(t, vec2<i32>(10, 20), vec4<f32>(0.0, 1.0, 2.0, 3.0));
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_texture_query() {
|
||||
parse_str(
|
||||
"
|
||||
var t: texture_multisampled_2d_array<f32>;
|
||||
fn foo() {
|
||||
var dim: vec2<u32> = textureDimensions(t);
|
||||
dim = textureDimensions(t, 0);
|
||||
let layers: u32 = textureNumLayers(t);
|
||||
let samples: u32 = textureNumSamples(t);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_postfix() {
|
||||
parse_str(
|
||||
"fn foo() {
|
||||
let x: f32 = vec4<f32>(1.0, 2.0, 3.0, 4.0).xyz.rgbr.aaaa.wz.g;
|
||||
let y: f32 = fract(vec2<f32>(0.5, x)).x;
|
||||
}",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_expressions() {
|
||||
parse_str("fn foo() {
|
||||
let x: f32 = select(0.0, 1.0, true);
|
||||
let y: vec2<f32> = select(vec2<f32>(1.0, 1.0), vec2<f32>(x, x), vec2<bool>(x < 0.5, x > 0.5));
|
||||
let z: bool = !(0.0 == 1.0);
|
||||
}").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pointers() {
|
||||
parse_str(
|
||||
"fn foo() {
|
||||
var x: f32 = 1.0;
|
||||
let px = &x;
|
||||
let py = frexp(0.5, px);
|
||||
}",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_struct_instantiation() {
|
||||
parse_str(
|
||||
"
|
||||
struct Foo {
|
||||
a: f32,
|
||||
b: vec3<f32>,
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main() {
|
||||
var foo: Foo = Foo(0.0, vec3<f32>(0.0, 1.0, 42.0));
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_array_length() {
|
||||
parse_str(
|
||||
"
|
||||
struct Foo {
|
||||
data: array<u32>
|
||||
} // this is used as both input and output for convenience
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<storage> foo: Foo;
|
||||
|
||||
@group(0) @binding(1)
|
||||
var<storage> bar: array<u32>;
|
||||
|
||||
fn baz() {
|
||||
var x: u32 = arrayLength(foo.data);
|
||||
var y: u32 = arrayLength(bar);
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_storage_buffers() {
|
||||
parse_str(
|
||||
"
|
||||
@group(0) @binding(0)
|
||||
var<storage> foo: array<u32>;
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
@group(0) @binding(0)
|
||||
var<storage,read> foo: array<u32>;
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
@group(0) @binding(0)
|
||||
var<storage,write> foo: array<u32>;
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
parse_str(
|
||||
"
|
||||
@group(0) @binding(0)
|
||||
var<storage,read_write> foo: array<u32>;
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_alias() {
|
||||
parse_str(
|
||||
"
|
||||
alias Vec4 = vec4<f32>;
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue