diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/glsl/src/syntax.rs | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/glsl/src/syntax.rs')
-rw-r--r-- | third_party/rust/glsl/src/syntax.rs | 1354 |
1 files changed, 1354 insertions, 0 deletions
diff --git a/third_party/rust/glsl/src/syntax.rs b/third_party/rust/glsl/src/syntax.rs new file mode 100644 index 0000000000..0ddf1e7b76 --- /dev/null +++ b/third_party/rust/glsl/src/syntax.rs @@ -0,0 +1,1354 @@ +//! 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`]: syntax::Statement +//! [`TranslationUnit`]: syntax::TranslationUnit +//! [`Expr`]: syntax::Expr +//! [`FunctionDefinition`]: 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)] +pub struct NonEmpty<T>(pub Vec<T>); + +impl<T> NonEmpty<T> { + /// Construct a non-empty from an iterator. + /// + /// # Errors + /// + /// `None` if the iterator yields no value. + pub fn 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. + pub fn push(&mut self, 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. + pub fn pop(&mut self) -> Option<T> { + if self.0.len() == 1 { + None + } else { + self.0.pop() + } + } +} + +impl<T> IntoIterator for NonEmpty<T> { + type IntoIter = <Vec<T> as IntoIterator>::IntoIter; + type Item = T; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, T> IntoIterator for &'a NonEmpty<T> { + type IntoIter = <&'a Vec<T> as IntoIterator>::IntoIter; + type Item = &'a T; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a, T> IntoIterator for &'a mut NonEmpty<T> { + type IntoIter = <&'a mut Vec<T> as IntoIterator>::IntoIter; + type Item = &'a mut T; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl<T> Extend<T> for NonEmpty<T> { + fn extend<I>(&mut self, iter: I) + where + I: IntoIterator<Item = T>, + { + self.0.extend(iter); + } +} + +/// A path literal. +#[derive(Clone, Debug, PartialEq)] +pub enum Path { + /// Specified with angle brackets. + Absolute(String), + /// Specified with double quotes. + Relative(String), +} + +/// Error that might occur when creating a new [`Identifier`]. +#[derive(Debug)] +pub enum IdentifierError { + StartsWithDigit, + ContainsNonASCIIAlphaNum, +} + +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)] +pub struct 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. + pub fn 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()) == Some(false) { + // check the first letter is not a digit + Err(IdentifierError::StartsWithDigit) + } else if 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. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl<'a> From<&'a str> for Identifier { + fn from(s: &str) -> Self { + Identifier(s.to_owned()) + } +} + +impl From<String> for Identifier { + fn from(s: String) -> Self { + Identifier(s) + } +} + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// Any type name. +#[derive(Clone, Debug, PartialEq)] +pub struct 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. + pub fn 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. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl<'a> From<&'a str> for TypeName { + fn from(s: &str) -> Self { + TypeName(s.to_owned()) + } +} + +impl From<String> for TypeName { + fn from(s: String) -> Self { + TypeName(s) + } +} + +impl fmt::Display for TypeName { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// Type specifier (non-array). +#[derive(Clone, Debug, PartialEq)] +pub enum TypeSpecifierNonArray { + // transparent types + Void, + Bool, + Int, + UInt, + Float, + Double, + Vec2, + Vec3, + Vec4, + DVec2, + DVec3, + DVec4, + BVec2, + BVec3, + BVec4, + IVec2, + IVec3, + IVec4, + UVec2, + UVec3, + UVec4, + Mat2, + Mat3, + Mat4, + Mat23, + Mat24, + Mat32, + Mat34, + Mat42, + Mat43, + DMat2, + DMat3, + DMat4, + DMat23, + DMat24, + DMat32, + DMat34, + DMat42, + DMat43, + // floating point opaque types + Sampler1D, + Image1D, + Sampler2D, + Image2D, + Sampler3D, + Image3D, + SamplerCube, + ImageCube, + Sampler2DRect, + Image2DRect, + Sampler1DArray, + Image1DArray, + Sampler2DArray, + Image2DArray, + SamplerBuffer, + ImageBuffer, + Sampler2DMS, + Image2DMS, + Sampler2DMSArray, + Image2DMSArray, + SamplerCubeArray, + ImageCubeArray, + Sampler1DShadow, + Sampler2DShadow, + Sampler2DRectShadow, + Sampler1DArrayShadow, + Sampler2DArrayShadow, + SamplerCubeShadow, + SamplerCubeArrayShadow, + // signed integer opaque types + ISampler1D, + IImage1D, + ISampler2D, + IImage2D, + ISampler3D, + IImage3D, + ISamplerCube, + IImageCube, + ISampler2DRect, + IImage2DRect, + ISampler1DArray, + IImage1DArray, + ISampler2DArray, + IImage2DArray, + ISamplerBuffer, + IImageBuffer, + ISampler2DMS, + IImage2DMS, + ISampler2DMSArray, + IImage2DMSArray, + ISamplerCubeArray, + IImageCubeArray, + // unsigned integer opaque types + AtomicUInt, + USampler1D, + UImage1D, + USampler2D, + UImage2D, + USampler3D, + UImage3D, + USamplerCube, + UImageCube, + USampler2DRect, + UImage2DRect, + USampler1DArray, + UImage1DArray, + USampler2DArray, + UImage2DArray, + USamplerBuffer, + UImageBuffer, + USampler2DMS, + UImage2DMS, + USampler2DMSArray, + UImage2DMSArray, + USamplerCubeArray, + UImageCubeArray, + Struct(StructSpecifier), + TypeName(TypeName), +} + +/// Type specifier. +#[derive(Clone, Debug, PartialEq)] +pub struct TypeSpecifier { + pub ty: TypeSpecifierNonArray, + pub array_specifier: Option<ArraySpecifier>, +} + +impl TypeSpecifier { + pub fn new(ty: TypeSpecifierNonArray) -> Self { + TypeSpecifier { + ty, + array_specifier: None, + } + } +} + +impl From<TypeSpecifierNonArray> for TypeSpecifier { + fn from(ty: TypeSpecifierNonArray) -> Self { + TypeSpecifier::new(ty) + } +} + +/// Struct specifier. Used to create new, user-defined types. +#[derive(Clone, Debug, PartialEq)] +pub struct StructSpecifier { + pub name: Option<TypeName>, + pub fields: NonEmpty<StructFieldSpecifier>, +} + +/// Struct field specifier. Used to add fields to struct specifiers. +#[derive(Clone, Debug, PartialEq)] +pub struct StructFieldSpecifier { + pub qualifier: Option<TypeQualifier>, + pub ty: TypeSpecifier, + pub identifiers: NonEmpty<ArrayedIdentifier>, // several identifiers of the same type +} + +impl StructFieldSpecifier { + /// Create a struct field. + pub fn new<A, T>(identifier: A, ty: T) -> Self + where + A: Into<ArrayedIdentifier>, + T: Into<TypeSpecifier>, + { + StructFieldSpecifier { + qualifier: None, + ty: ty.into(), + identifiers: NonEmpty(vec![identifier.into()]), + } + } + + /// Create a list of struct fields that all have the same type. + pub fn 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)] +pub struct ArrayedIdentifier { + pub ident: Identifier, + pub array_spec: Option<ArraySpecifier>, +} + +impl ArrayedIdentifier { + pub fn new<I, AS>(ident: I, array_spec: AS) -> Self + where + I: Into<Identifier>, + AS: Into<Option<ArraySpecifier>>, + { + ArrayedIdentifier { + ident: ident.into(), + array_spec: array_spec.into(), + } + } +} + +impl<'a> From<&'a str> for ArrayedIdentifier { + fn from(ident: &str) -> Self { + ArrayedIdentifier { + ident: Identifier(ident.to_owned()), + array_spec: None, + } + } +} + +impl From<Identifier> for ArrayedIdentifier { + fn from(ident: Identifier) -> Self { + ArrayedIdentifier { + ident, + array_spec: None, + } + } +} + +/// Type qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct TypeQualifier { + pub qualifiers: NonEmpty<TypeQualifierSpec>, +} + +/// Type qualifier spec. +#[derive(Clone, Debug, PartialEq)] +pub enum TypeQualifierSpec { + Storage(StorageQualifier), + Layout(LayoutQualifier), + Precision(PrecisionQualifier), + Interpolation(InterpolationQualifier), + Invariant, + Precise, +} + +/// Storage qualifier. +#[derive(Clone, Debug, PartialEq)] +pub enum StorageQualifier { + Const, + InOut, + In, + Out, + Centroid, + Patch, + Sample, + Uniform, + Attribute, + Varying, + Buffer, + Shared, + Coherent, + Volatile, + Restrict, + ReadOnly, + WriteOnly, + Subroutine(Vec<TypeName>), +} + +/// Layout qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct LayoutQualifier { + pub ids: NonEmpty<LayoutQualifierSpec>, +} + +/// Layout qualifier spec. +#[derive(Clone, Debug, PartialEq)] +pub enum LayoutQualifierSpec { + Identifier(Identifier, Option<Box<Expr>>), + Shared, +} + +/// Precision qualifier. +#[derive(Clone, Debug, PartialEq)] +pub enum PrecisionQualifier { + High, + Medium, + Low, +} + +/// Interpolation qualifier. +#[derive(Clone, Debug, PartialEq)] +pub enum InterpolationQualifier { + Smooth, + Flat, + NoPerspective, +} + +/// Fully specified type. +#[derive(Clone, Debug, PartialEq)] +pub struct FullySpecifiedType { + pub qualifier: Option<TypeQualifier>, + pub ty: TypeSpecifier, +} + +impl FullySpecifiedType { + pub fn new(ty: TypeSpecifierNonArray) -> Self { + FullySpecifiedType { + qualifier: None, + ty: TypeSpecifier { + ty, + array_specifier: None, + }, + } + } +} + +impl From<TypeSpecifierNonArray> for FullySpecifiedType { + fn from(ty: TypeSpecifierNonArray) -> Self { + FullySpecifiedType::new(ty) + } +} + +/// Dimensionality of an arary. +#[derive(Clone, Debug, PartialEq)] +pub enum ArraySpecifier { + Unsized, + ExplicitlySized(Box<Expr>), +} + +/// A declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum Declaration { + FunctionPrototype(FunctionPrototype), + InitDeclaratorList(InitDeclaratorList), + Precision(PrecisionQualifier, TypeSpecifier), + Block(Block), + Global(TypeQualifier, Vec<Identifier>), +} + +/// A general purpose block, containing fields and possibly a list of declared identifiers. Semantic +/// is given with the storage qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct Block { + pub qualifier: TypeQualifier, + pub name: Identifier, + pub fields: Vec<StructFieldSpecifier>, + pub identifier: Option<ArrayedIdentifier>, +} + +/// Function identifier. +#[derive(Clone, Debug, PartialEq)] +pub enum FunIdentifier { + Identifier(Identifier), + Expr(Box<Expr>), +} + +impl FunIdentifier { + pub(crate) fn into_expr(self) -> Option<Expr> { + match self { + FunIdentifier::Identifier(..) => None, + FunIdentifier::Expr(expr) => Some(*expr), + } + } +} + +/// Function prototype. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionPrototype { + pub ty: FullySpecifiedType, + pub name: Identifier, + pub parameters: Vec<FunctionParameterDeclaration>, +} + +/// Function parameter declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum FunctionParameterDeclaration { + Named(Option<TypeQualifier>, FunctionParameterDeclarator), + Unnamed(Option<TypeQualifier>, TypeSpecifier), +} + +impl FunctionParameterDeclaration { + /// Create a named function argument. + pub fn new_named<I, T>(ident: I, ty: T) -> Self + where + I: Into<ArrayedIdentifier>, + T: Into<TypeSpecifier>, + { + let declator = FunctionParameterDeclarator { + ty: ty.into(), + ident: ident.into(), + }; + + FunctionParameterDeclaration::Named(None, declator) + } + + /// Create an unnamed function argument (mostly useful for interfaces / function prototypes). + pub fn new_unnamed<T>(ty: T) -> Self + where + T: Into<TypeSpecifier>, + { + FunctionParameterDeclaration::Unnamed(None, ty.into()) + } +} + +/// Function parameter declarator. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionParameterDeclarator { + pub ty: TypeSpecifier, + pub ident: ArrayedIdentifier, +} + +/// Init declarator list. +#[derive(Clone, Debug, PartialEq)] +pub struct InitDeclaratorList { + pub head: SingleDeclaration, + pub tail: Vec<SingleDeclarationNoType>, +} + +/// Single declaration. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclaration { + pub ty: FullySpecifiedType, + pub name: Option<Identifier>, + pub array_specifier: Option<ArraySpecifier>, + pub initializer: Option<Initializer>, +} + +/// A single declaration with implicit, already-defined type. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclarationNoType { + pub ident: ArrayedIdentifier, + pub initializer: Option<Initializer>, +} + +/// Initializer. +#[derive(Clone, Debug, PartialEq)] +pub enum Initializer { + Simple(Box<Expr>), + List(NonEmpty<Initializer>), +} + +impl From<Expr> for Initializer { + fn from(e: Expr) -> Self { + Initializer::Simple(Box::new(e)) + } +} + +/// 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)] +pub enum 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>), +} + +impl From<i32> for Expr { + fn from(x: i32) -> Expr { + Expr::IntConst(x) + } +} + +impl From<u32> for Expr { + fn from(x: u32) -> Expr { + Expr::UIntConst(x) + } +} + +impl From<bool> for Expr { + fn from(x: bool) -> Expr { + Expr::BoolConst(x) + } +} + +impl From<f32> for Expr { + fn from(x: f32) -> Expr { + Expr::FloatConst(x) + } +} + +impl From<f64> for Expr { + fn from(x: f64) -> Expr { + Expr::DoubleConst(x) + } +} + +/// All unary operators that exist in GLSL. +#[derive(Clone, Debug, PartialEq)] +pub enum UnaryOp { + Inc, + Dec, + Add, + Minus, + Not, + Complement, +} + +/// All binary operators that exist in GLSL. +#[derive(Clone, Debug, PartialEq)] +pub enum BinaryOp { + Or, + Xor, + And, + BitOr, + BitXor, + BitAnd, + Equal, + NonEqual, + LT, + GT, + LTE, + GTE, + LShift, + RShift, + Add, + Sub, + Mult, + Div, + Mod, +} + +/// All possible operators for assigning expressions. +#[derive(Clone, Debug, PartialEq)] +pub enum AssignmentOp { + Equal, + Mult, + Div, + Mod, + Add, + Sub, + LShift, + RShift, + And, + Xor, + Or, +} + +/// Starting rule. +#[derive(Clone, Debug, PartialEq)] +pub struct TranslationUnit(pub NonEmpty<ExternalDeclaration>); + +/// A shader stage. +pub type 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. + pub fn 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>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for TranslationUnit { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntoIterator for TranslationUnit { + type IntoIter = <NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a TranslationUnit { + type IntoIter = <&'a NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = &'a ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter() + } +} + +impl<'a> IntoIterator for &'a mut TranslationUnit { + type IntoIter = <&'a mut NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = &'a mut ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&mut self.0).into_iter() + } +} + +/// External declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum ExternalDeclaration { + Preprocessor(Preprocessor), + FunctionDefinition(FunctionDefinition), + Declaration(Declaration), +} + +impl ExternalDeclaration { + /// Create a new function. + pub fn 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. + pub fn 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(); + + if fields.is_empty() { + None + } else { + Some(ExternalDeclaration::Declaration( + Declaration::InitDeclaratorList(InitDeclaratorList { + head: SingleDeclaration { + ty: FullySpecifiedType { + qualifier: None, + ty: TypeSpecifier { + ty: TypeSpecifierNonArray::Struct(StructSpecifier { + name: Some(name.into()), + fields: NonEmpty(fields.into_iter().collect()), + }), + array_specifier: None, + }, + }, + name: None, + array_specifier: None, + initializer: None, + }, + tail: vec![], + }), + )) + } + } +} + +/// Function definition. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionDefinition { + pub prototype: FunctionPrototype, + pub statement: CompoundStatement, +} + +/// Compound statement (with no new scope). +#[derive(Clone, Debug, PartialEq)] +pub struct CompoundStatement { + pub statement_list: Vec<Statement>, +} + +impl FromIterator<Statement> for CompoundStatement { + fn from_iter<T>(iter: T) -> Self + where + T: IntoIterator<Item = Statement>, + { + CompoundStatement { + statement_list: iter.into_iter().collect(), + } + } +} + +/// Statement. +#[derive(Clone, Debug, PartialEq)] +pub enum Statement { + Compound(Box<CompoundStatement>), + Simple(Box<SimpleStatement>), +} + +impl Statement { + /// Create a case-label sequence of nested statements. + pub fn new_case<C, S>(case: C, statements: S) -> Self + where + C: Into<CaseLabel>, + S: IntoIterator<Item = Statement>, + { + let case_stmt = Statement::Simple(Box::new(SimpleStatement::CaseLabel(case.into()))); + + Statement::Compound(Box::new(CompoundStatement { + statement_list: once(case_stmt).chain(statements.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` + pub fn 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(), + }), + ))) + } +} + +/// Simple statement. +#[derive(Clone, Debug, PartialEq)] +pub enum SimpleStatement { + Declaration(Declaration), + Expression(ExprStatement), + Selection(SelectionStatement), + Switch(SwitchStatement), + CaseLabel(CaseLabel), + Iteration(IterationStatement), + Jump(JumpStatement), +} + +impl SimpleStatement { + /// Create a new expression statement. + pub fn new_expr<E>(expr: E) -> Self + where + E: Into<Expr>, + { + SimpleStatement::Expression(Some(expr.into())) + } + + /// Create a new selection statement (if / else). + pub fn new_if_else<If, True, False>(ife: If, truee: True, falsee: False) -> Self + where + If: Into<Expr>, + True: Into<Statement>, + False: Into<Statement>, + { + SimpleStatement::Selection(SelectionStatement { + cond: Box::new(ife.into()), + rest: SelectionRestStatement::Else(Box::new(truee.into()), Box::new(falsee.into())), + }) + } + + /// 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. + pub fn 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. + pub fn 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. + pub fn 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()), + )) + } +} + +/// Expression statement. +pub type ExprStatement = Option<Expr>; + +/// Selection statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SelectionStatement { + pub cond: Box<Expr>, + pub rest: SelectionRestStatement, +} + +/// Condition. +#[derive(Clone, Debug, PartialEq)] +pub enum Condition { + Expr(Box<Expr>), + Assignment(FullySpecifiedType, Identifier, Initializer), +} + +impl From<Expr> for Condition { + fn from(expr: Expr) -> Self { + Condition::Expr(Box::new(expr)) + } +} + +/// Selection rest statement. +#[derive(Clone, Debug, PartialEq)] +pub enum 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>), +} + +/// Switch statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SwitchStatement { + pub head: Box<Expr>, + pub body: Vec<Statement>, +} + +/// Case label statement. +#[derive(Clone, Debug, PartialEq)] +pub enum CaseLabel { + Case(Box<Expr>), + Def, +} + +/// Iteration statement. +#[derive(Clone, Debug, PartialEq)] +pub enum IterationStatement { + While(Condition, Box<Statement>), + DoWhile(Box<Statement>, Box<Expr>), + For(ForInitStatement, ForRestStatement, Box<Statement>), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub enum ForInitStatement { + Expression(Option<Expr>), + Declaration(Box<Declaration>), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub struct ForRestStatement { + pub condition: Option<Condition>, + pub post_expr: Option<Box<Expr>>, +} + +/// Jump statement. +#[derive(Clone, Debug, PartialEq)] +pub enum JumpStatement { + Continue, + Break, + Return(Option<Box<Expr>>), + Discard, +} + +/// 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)] +pub enum 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)] +pub enum PreprocessorDefine { + ObjectLike { + ident: Identifier, + value: String, + }, + + FunctionLike { + ident: Identifier, + args: Vec<Identifier>, + value: String, + }, +} + +/// An #else preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorElseIf { + pub condition: String, +} + +/// An #error preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorError { + pub message: String, +} + +/// An #if preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorIf { + pub condition: String, +} + +/// An #ifdef preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorIfDef { + pub ident: Identifier, +} + +/// A #ifndef preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorIfNDef { + pub ident: Identifier, +} + +/// An #include name annotation. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorInclude { + pub path: Path, +} + +/// A #line preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorLine { + pub line: u32, + pub source_string_number: Option<u32>, +} + +/// A #pragma preprocessor directive. +/// Holds compiler-specific command. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorPragma { + pub command: String, +} + +/// A #undef preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorUndef { + pub name: Identifier, +} + +/// A #version preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorVersion { + pub version: u16, + pub profile: Option<PreprocessorVersionProfile>, +} + +/// A #version profile annotation. +#[derive(Clone, Debug, PartialEq)] +pub enum PreprocessorVersionProfile { + Core, + Compatibility, + ES, +} + +/// An #extension preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorExtension { + pub name: PreprocessorExtensionName, + pub behavior: Option<PreprocessorExtensionBehavior>, +} + +/// An #extension name annotation. +#[derive(Clone, Debug, PartialEq)] +pub enum PreprocessorExtensionName { + /// All extensions you could ever imagine in your whole lifetime (how crazy is that!). + All, + /// A specific extension. + Specific(String), +} + +/// An #extension behavior annotation. +#[derive(Clone, Debug, PartialEq)] +pub enum PreprocessorExtensionBehavior { + Require, + Enable, + Warn, + Disable, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn create_new_identifier() { + assert!(Identifier::new("foo_bar").is_ok()); + assert!(Identifier::new("3foo_bar").is_err()); + assert!(Identifier::new("FooBar").is_ok()); + assert!(Identifier::new("_FooBar").is_err()); + assert!(Identifier::new("foo3").is_ok()); + assert!(Identifier::new("foo3_").is_ok()); + assert!(Identifier::new("fδo3_").is_err()); + } + + #[test] + fn create_new_type_name() { + assert!(TypeName::new("foo_bar").is_ok()); + assert!(TypeName::new("FooBar").is_ok()); + assert!(TypeName::new("foo3").is_ok()); + assert!(TypeName::new("foo3_").is_ok()); + + assert!(TypeName::new("_FooBar").is_err()); + assert!(TypeName::new("3foo_bar").is_err()); + assert!(TypeName::new("fδo3_").is_err()); + } + + // bool predicate(float x) { + // } + #[test] + fn declare_new_fn() { + let _ = ExternalDeclaration::new_fn( + TypeSpecifierNonArray::Bool, + "predicate", + vec![FunctionParameterDeclaration::new_named( + "x", + TypeSpecifierNonArray::Float, + )], + vec![], + ); + } + + // struct Point2D { + // float x; + // float y; + // }; + #[test] + fn declare_struct() { + let point = ExternalDeclaration::new_struct( + "Point2D", + vec![ + StructFieldSpecifier::new("x", TypeSpecifierNonArray::Double), + StructFieldSpecifier::new("y", TypeSpecifierNonArray::Double), + ], + ); + + assert!(point.is_some()); + } + + // struct Point2D {}; + #[test] + fn declare_bad_struct() { + let point = ExternalDeclaration::new_struct("Point2D", vec![]); + assert!(point.is_none()); + } + + #[test] + fn initializer_from_expr() { + let _: Initializer = Expr::from(false).into(); + } +} |