//! GLSL abstract syntax tree and grammar. //! //! This module exports all the grammar syntax that defines GLSL. You’ll be handling ASTs //! representing your GLSL source. //! //! The most external form of a GLSL parsed AST is [`TranslationUnit`] (a shader). Some parts of the //! tree are *boxed*. This is due to two facts: //! //! - Recursion is used, hence we need a way to give our types a static size. //! - Because of some very deep variants, runtime size would explode if no indirection weren’t //! in place. //! //! The types are commented so feel free to inspect each of theme. As a starter, you should read //! the documentation of [`Expr`], [`FunctionDefinition`], [`Statement`] and [`TranslationUnit`]. //! //! [`Statement`]: crate::syntax::Statement //! [`TranslationUnit`]: crate::syntax::TranslationUnit //! [`Expr`]: crate::syntax::Expr //! [`FunctionDefinition`]: crate::syntax::FunctionDefinition
use std::fmt; use std::iter::{once, FromIterator}; use std::ops::{Deref, DerefMut};
/// A non-empty [`Vec`]. It has at least one element. #[derive(Clone, Debug, PartialEq)] pubstruct NonEmpty<T>(pub Vec<T>);
impl<T> NonEmpty<T> { /// Construct a non-empty from an iterator. /// /// # Errors /// /// `None` if the iterator yields no value. pubfn from_non_empty_iter<I>(iter: I) -> Option<Self> where
I: IntoIterator<Item = T>,
{ let vec: Vec<_> = iter.into_iter().collect();
if vec.is_empty() {
None
} else {
Some(NonEmpty(vec))
}
}
/// Move a new item at the end of the non-empty. pubfn push(&mutself, item: T) { self.0.push(item);
}
/// Move out the last element of the non-empty. /// /// # Errors /// /// This function returns `None` if called on a non-empty that contains a single element. pubfn pop(&mutself) -> Option<T> { ifself.0.len() == 1 {
None
} else { self.0.pop()
}
}
}
impl<T> IntoIterator for NonEmpty<T> { type IntoIter = <Vec<T> as IntoIterator>::IntoIter; type Item = T;
impl<T> Extend<T> for NonEmpty<T> { fn extend<I>(&mutself, iter: I) where
I: IntoIterator<Item = T>,
{ self.0.extend(iter);
}
}
/// A path literal. #[derive(Clone, Debug, PartialEq)] pubenum Path { /// Specified with angle brackets.
Absolute(String), /// Specified with double quotes.
Relative(String),
}
/// Error that might occur when creating a new [`Identifier`]. #[derive(Debug)] pubenum IdentifierError {
StartsWithDigit,
ContainsNonASCIIAlphaNum,
}
impl std::error::Error for IdentifierError {}
impl fmt::Display for IdentifierError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self {
IdentifierError::StartsWithDigit => f.write_str("starts starts with a digit"),
IdentifierError::ContainsNonASCIIAlphaNum => {
f.write_str("contains at least one non-alphanumeric ASCII character")
}
}
}
}
/// A generic identifier. #[derive(Clone, Debug, PartialEq)] pubstruct Identifier(pub String);
impl Identifier { /// Create a new [`Identifier`]. /// /// # Errors /// /// This function will fail if the identifier starts with a digit or contains non-alphanumeric /// ASCII characters. pubfn new<N>(name: N) -> Result<Self, IdentifierError> where
N: Into<String>,
{ let name = name.into();
if name
.chars()
.next()
.map(|c| c.is_ascii_alphabetic() || c == '_')
== Some(false)
{ // check the first letter is not a digit
Err(IdentifierError::StartsWithDigit)
} elseif name.contains(|c: char| !(c.is_ascii_alphanumeric() || c == '_')) { // check we only have ASCII alphanumeric characters
Err(IdentifierError::ContainsNonASCIIAlphaNum)
} else {
Ok(Identifier(name))
}
}
/// Get the string representation of the identifier. pubfn as_str(&self) -> &str {
&self.0
}
}
/// Any type name. #[derive(Clone, Debug, PartialEq)] pubstruct TypeName(pub String);
impl TypeName { /// Create a new [`TypeName`]. /// /// # Errors /// /// This function will fail if the type name starts with a digit or contains non-alphanumeric /// ASCII characters. pubfn new<N>(name: N) -> Result<Self, IdentifierError> where
N: Into<String>,
{ // build as identifier and unwrap into type name let Identifier(tn) = Identifier::new(name)?;
Ok(TypeName(tn))
}
/// Get the string representation of the type name. pubfn as_str(&self) -> &str {
&self.0
}
}
/// Struct field specifier. Used to add fields to struct specifiers. #[derive(Clone, Debug, PartialEq)] pubstruct StructFieldSpecifier { pub qualifier: Option<TypeQualifier>, pub ty: TypeSpecifier, pub identifiers: NonEmpty<ArrayedIdentifier>, // several identifiers of the same type
}
/// Create a list of struct fields that all have the same type. pubfn new_many<I>(identifiers: I, ty: TypeSpecifier) -> Self where
I: IntoIterator<Item = ArrayedIdentifier>,
{
StructFieldSpecifier {
qualifier: None,
ty,
identifiers: NonEmpty(identifiers.into_iter().collect()),
}
}
}
/// An identifier with an optional array specifier. #[derive(Clone, Debug, PartialEq)] pubstruct ArrayedIdentifier { pub ident: Identifier, pub array_spec: Option<ArraySpecifier>,
}
/// Dimensionality of an array. #[derive(Clone, Debug, PartialEq)] pubstruct ArraySpecifier { /// List of all the dimensions – possibly unsized or explicitly-sized. pub dimensions: NonEmpty<ArraySpecifierDimension>,
}
/// A general purpose block, containing fields and possibly a list of declared identifiers. Semantic /// is given with the storage qualifier. #[derive(Clone, Debug, PartialEq)] pubstruct Block { pub qualifier: TypeQualifier, pub name: Identifier, pub fields: Vec<StructFieldSpecifier>, pub identifier: Option<ArrayedIdentifier>,
}
/// The most general form of an expression. As you can see if you read the variant list, in GLSL, an /// assignment is an expression. This is a bit silly but think of an assignment as a statement first /// then an expression which evaluates to what the statement “returns”. /// /// An expression is either an assignment or a list (comma) of assignments. #[derive(Clone, Debug, PartialEq)] pubenum Expr { /// A variable expression, using an identifier.
Variable(Identifier), /// Integral constant expression.
IntConst(i32), /// Unsigned integral constant expression.
UIntConst(u32), /// Boolean constant expression.
BoolConst(bool), /// Single precision floating expression.
FloatConst(f32), /// Double precision floating expression.
DoubleConst(f64), /// A unary expression, gathering a single expression and a unary operator.
Unary(UnaryOp, Box<Expr>), /// A binary expression, gathering two expressions and a binary operator.
Binary(BinaryOp, Box<Expr>, Box<Expr>), /// A ternary conditional expression, gathering three expressions.
Ternary(Box<Expr>, Box<Expr>, Box<Expr>), /// An assignment is also an expression. Gathers an expression that defines what to assign to, an /// assignment operator and the value to associate with.
Assignment(Box<Expr>, AssignmentOp, Box<Expr>), /// Add an array specifier to an expression.
Bracket(Box<Expr>, ArraySpecifier), /// A functional call. It has a function identifier and a list of expressions (arguments).
FunCall(FunIdentifier, Vec<Expr>), /// An expression associated with a field selection (struct).
Dot(Box<Expr>, Identifier), /// Post-incrementation of an expression.
PostInc(Box<Expr>), /// Post-decrementation of an expression.
PostDec(Box<Expr>), /// An expression that contains several, separated with comma.
Comma(Box<Expr>, Box<Expr>),
}
/// A shader stage. pubtype ShaderStage = TranslationUnit;
impl TranslationUnit { /// Construct a translation unit from an iterator representing a _non-empty_ sequence of /// [`ExternalDeclaration`]. /// /// # Errors /// /// `None` if the iterator yields no value. pubfn from_non_empty_iter<I>(iter: I) -> Option<Self> where
I: IntoIterator<Item = ExternalDeclaration>,
{
NonEmpty::from_non_empty_iter(iter).map(TranslationUnit)
}
}
impl Deref for TranslationUnit { type Target = NonEmpty<ExternalDeclaration>;
impl<'a> IntoIterator for &'a TranslationUnit { type IntoIter = <&'a NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; type Item = &'a ExternalDeclaration;
impl ExternalDeclaration { /// Create a new function. pubfn new_fn<T, N, A, S>(ret_ty: T, name: N, args: A, body: S) -> Self where
T: Into<FullySpecifiedType>,
N: Into<Identifier>,
A: IntoIterator<Item = FunctionParameterDeclaration>,
S: IntoIterator<Item = Statement>,
{
ExternalDeclaration::FunctionDefinition(FunctionDefinition {
prototype: FunctionPrototype {
ty: ret_ty.into(),
name: name.into(),
parameters: args.into_iter().collect(),
},
statement: CompoundStatement {
statement_list: body.into_iter().collect(),
},
})
}
/// Create a new structure. /// /// # Errors /// /// - [`None`] if no fields are provided. GLSL forbids having empty structs. pubfn new_struct<N, F>(name: N, fields: F) -> Option<Self> where
N: Into<TypeName>,
F: IntoIterator<Item = StructFieldSpecifier>,
{ let fields: Vec<_> = fields.into_iter().collect();
/// Declare a new variable. /// /// `ty` is the type of the variable, `name` the name of the binding to create, /// `array_specifier` an optional argument to make your binding an array and /// `initializer` pubfn declare_var<T, N, A, I>(ty: T, name: N, array_specifier: A, initializer: I) -> Self where
T: Into<FullySpecifiedType>,
N: Into<Identifier>,
A: Into<Option<ArraySpecifier>>,
I: Into<Option<Initializer>>,
{
Statement::Simple(Box::new(SimpleStatement::Declaration(
Declaration::InitDeclaratorList(InitDeclaratorList {
head: SingleDeclaration {
ty: ty.into(),
name: Some(name.into()),
array_specifier: array_specifier.into(),
initializer: initializer.into(),
},
tail: Vec::new(),
}),
)))
}
}
/// Create a new switch statement. /// /// A switch statement is always composed of a [`SimpleStatement::Switch`] block, that contains it /// all, and has as body a compound list of case statements. pubfn new_switch<H, B>(head: H, body: B) -> Self where
H: Into<Expr>,
B: IntoIterator<Item = Statement>,
{
SimpleStatement::Switch(SwitchStatement {
head: Box::new(head.into()),
body: body.into_iter().collect(),
})
}
/// Create a new while statement. pubfn new_while<C, S>(cond: C, body: S) -> Self where
C: Into<Condition>,
S: Into<Statement>,
{
SimpleStatement::Iteration(IterationStatement::While(
cond.into(), Box::new(body.into()),
))
}
/// Create a new do-while statement. pubfn new_do_while<C, S>(body: S, cond: C) -> Self where
S: Into<Statement>,
C: Into<Expr>,
{
SimpleStatement::Iteration(IterationStatement::DoWhile( Box::new(body.into()), Box::new(cond.into()),
))
}
}
/// Selection rest statement. #[derive(Clone, Debug, PartialEq)] pubenum SelectionRestStatement { /// Body of the if.
Statement(Box<Statement>), /// The first argument is the body of the if, the rest is the next statement. Else(Box<Statement>, Box<Statement>),
}
/// Some basic preprocessor directives. /// /// As it’s important to carry them around the AST because they cannot be substituted in a normal /// preprocessor (they’re used by GPU’s compilers), those preprocessor directives are available for /// inspection. #[derive(Clone, Debug, PartialEq)] pubenum Preprocessor {
Define(PreprocessorDefine), Else,
ElseIf(PreprocessorElseIf),
EndIf,
Error(PreprocessorError), If(PreprocessorIf),
IfDef(PreprocessorIfDef),
IfNDef(PreprocessorIfNDef),
Include(PreprocessorInclude),
Line(PreprocessorLine),
Pragma(PreprocessorPragma),
Undef(PreprocessorUndef),
Version(PreprocessorVersion),
Extension(PreprocessorExtension),
}
/// A #define preprocessor directive. /// /// Allows any expression but only Integer and Float literals make sense #[derive(Clone, Debug, PartialEq)] pubenum PreprocessorDefine {
ObjectLike {
ident: Identifier,
value: String,
},
/// An #extension name annotation. #[derive(Clone, Debug, PartialEq)] pubenum PreprocessorExtensionName { /// All extensions you could ever imagine in your whole lifetime (how crazy is that!).
All, /// A specific extension.
Specific(String),
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.