//! A GLSL450/GLSL460 transpiler that takes a syntax tree and writes it as a plain raw GLSL //! [`String`]. //! //! # Foreword //! //! This module exports several functions that just transform a part of a syntax tree into its raw //! GLSL [`String`] representation. //! //! > Important note: this module – and actually, any [`transpiler`] module – is not responsible in //! > optimizing the syntax tree nor semantically check its validity. This is done in other stages //! > of the compilation process. //! //! In order to achieve that purpose, you could: //! //! - For each elements in the AST, return a [`String`] or [`Cow`]. //! - Insert the string representation via a formatter. //! //! The second solution is better because it lets the user handle the memory the way they want: //! they might just use a dynamic buffer that implements [`Write`] or simply pass a `&mut` //! [`String`]. It’s up to you. //! //! # How to use this module //! //! First, head over to the [`syntax`] module. That module defines the AST items defined by GLSL. This //! very module provides you with functions like `show_*` taking the AST item and writing it to a //! [`Write`] object. You’re likely to be interested in [`show_translation_unit`] to start with. //! //! [`Cow`]: std::borrow::Cow //! [`Write`]: std::fmt::Write //! [`show_translation_unit`]: crate::transpiler::glsl::show_translation_unit //! [`syntax`]: crate::syntax //! [`transpiler`]: crate::transpiler use std::fmt::Write; use crate::syntax; // Precedence information for transpiling parentheses properly trait HasPrecedence { fn precedence(&self) -> u32; } impl HasPrecedence for syntax::Expr { fn precedence(&self) -> u32 { match self { // 0 isn't a valid precedence, but we use this to represent atomic expressions Self::Variable(_) | Self::IntConst(_) | Self::UIntConst(_) | Self::BoolConst(_) | Self::FloatConst(_) | Self::DoubleConst(_) => 0, // Precedence operator expression is precedence of operator Self::Unary(op, _) => op.precedence(), Self::Binary(op, _, _) => op.precedence(), Self::Ternary(_, _, _) => 15, Self::Assignment(_, op, _) => op.precedence(), Self::Bracket(_, _) | Self::FunCall(_, _) | Self::Dot(_, _) | Self::PostInc(_) | Self::PostDec(_) => 2, Self::Comma(_, _) => 17, } } } impl HasPrecedence for syntax::UnaryOp { fn precedence(&self) -> u32 { 3 } } impl HasPrecedence for syntax::BinaryOp { fn precedence(&self) -> u32 { match self { Self::Mult | Self::Div | Self::Mod => 4, Self::Add | Self::Sub => 5, Self::LShift | Self::RShift => 6, Self::LT | Self::GT | Self::LTE | Self::GTE => 7, Self::Equal | Self::NonEqual => 8, Self::BitAnd => 9, Self::BitXor => 10, Self::BitOr => 11, Self::And => 12, Self::Xor => 13, Self::Or => 14, } } } impl HasPrecedence for syntax::AssignmentOp { fn precedence(&self) -> u32 { 16 } } pub fn show_identifier(f: &mut F, i: &syntax::Identifier) where F: Write, { let _ = f.write_str(&i.0); } pub fn show_type_name(f: &mut F, t: &syntax::TypeName) where F: Write, { let _ = f.write_str(&t.0); } pub fn show_type_specifier_non_array(f: &mut F, t: &syntax::TypeSpecifierNonArray) where F: Write, { match *t { syntax::TypeSpecifierNonArray::Void => { let _ = f.write_str("void"); } syntax::TypeSpecifierNonArray::Bool => { let _ = f.write_str("bool"); } syntax::TypeSpecifierNonArray::Int => { let _ = f.write_str("int"); } syntax::TypeSpecifierNonArray::UInt => { let _ = f.write_str("uint"); } syntax::TypeSpecifierNonArray::Float => { let _ = f.write_str("float"); } syntax::TypeSpecifierNonArray::Double => { let _ = f.write_str("double"); } syntax::TypeSpecifierNonArray::Vec2 => { let _ = f.write_str("vec2"); } syntax::TypeSpecifierNonArray::Vec3 => { let _ = f.write_str("vec3"); } syntax::TypeSpecifierNonArray::Vec4 => { let _ = f.write_str("vec4"); } syntax::TypeSpecifierNonArray::DVec2 => { let _ = f.write_str("dvec2"); } syntax::TypeSpecifierNonArray::DVec3 => { let _ = f.write_str("dvec3"); } syntax::TypeSpecifierNonArray::DVec4 => { let _ = f.write_str("dvec4"); } syntax::TypeSpecifierNonArray::BVec2 => { let _ = f.write_str("bvec2"); } syntax::TypeSpecifierNonArray::BVec3 => { let _ = f.write_str("bvec3"); } syntax::TypeSpecifierNonArray::BVec4 => { let _ = f.write_str("bvec4"); } syntax::TypeSpecifierNonArray::IVec2 => { let _ = f.write_str("ivec2"); } syntax::TypeSpecifierNonArray::IVec3 => { let _ = f.write_str("ivec3"); } syntax::TypeSpecifierNonArray::IVec4 => { let _ = f.write_str("ivec4"); } syntax::TypeSpecifierNonArray::UVec2 => { let _ = f.write_str("uvec2"); } syntax::TypeSpecifierNonArray::UVec3 => { let _ = f.write_str("uvec3"); } syntax::TypeSpecifierNonArray::UVec4 => { let _ = f.write_str("uvec4"); } syntax::TypeSpecifierNonArray::Mat2 => { let _ = f.write_str("mat2"); } syntax::TypeSpecifierNonArray::Mat3 => { let _ = f.write_str("mat3"); } syntax::TypeSpecifierNonArray::Mat4 => { let _ = f.write_str("mat4"); } syntax::TypeSpecifierNonArray::Mat23 => { let _ = f.write_str("mat2x3"); } syntax::TypeSpecifierNonArray::Mat24 => { let _ = f.write_str("mat2x4"); } syntax::TypeSpecifierNonArray::Mat32 => { let _ = f.write_str("mat3x2"); } syntax::TypeSpecifierNonArray::Mat34 => { let _ = f.write_str("mat3x4"); } syntax::TypeSpecifierNonArray::Mat42 => { let _ = f.write_str("mat4x2"); } syntax::TypeSpecifierNonArray::Mat43 => { let _ = f.write_str("mat4x3"); } syntax::TypeSpecifierNonArray::DMat2 => { let _ = f.write_str("dmat2"); } syntax::TypeSpecifierNonArray::DMat3 => { let _ = f.write_str("dmat3"); } syntax::TypeSpecifierNonArray::DMat4 => { let _ = f.write_str("dmat4"); } syntax::TypeSpecifierNonArray::DMat23 => { let _ = f.write_str("dmat2x3"); } syntax::TypeSpecifierNonArray::DMat24 => { let _ = f.write_str("dmat2x4"); } syntax::TypeSpecifierNonArray::DMat32 => { let _ = f.write_str("dmat3x2"); } syntax::TypeSpecifierNonArray::DMat34 => { let _ = f.write_str("dmat3x4"); } syntax::TypeSpecifierNonArray::DMat42 => { let _ = f.write_str("dmat4x2"); } syntax::TypeSpecifierNonArray::DMat43 => { let _ = f.write_str("dmat4x3"); } syntax::TypeSpecifierNonArray::Sampler1D => { let _ = f.write_str("sampler1D"); } syntax::TypeSpecifierNonArray::Image1D => { let _ = f.write_str("image1D"); } syntax::TypeSpecifierNonArray::Sampler2D => { let _ = f.write_str("sampler2D"); } syntax::TypeSpecifierNonArray::Image2D => { let _ = f.write_str("image2D"); } syntax::TypeSpecifierNonArray::Sampler3D => { let _ = f.write_str("sampler3D"); } syntax::TypeSpecifierNonArray::Image3D => { let _ = f.write_str("image3D"); } syntax::TypeSpecifierNonArray::SamplerCube => { let _ = f.write_str("samplerCube"); } syntax::TypeSpecifierNonArray::ImageCube => { let _ = f.write_str("imageCube"); } syntax::TypeSpecifierNonArray::Sampler2DRect => { let _ = f.write_str("sampler2DRect"); } syntax::TypeSpecifierNonArray::Image2DRect => { let _ = f.write_str("image2DRect"); } syntax::TypeSpecifierNonArray::Sampler1DArray => { let _ = f.write_str("sampler1DArray"); } syntax::TypeSpecifierNonArray::Image1DArray => { let _ = f.write_str("image1DArray"); } syntax::TypeSpecifierNonArray::Sampler2DArray => { let _ = f.write_str("sampler2DArray"); } syntax::TypeSpecifierNonArray::Image2DArray => { let _ = f.write_str("image2DArray"); } syntax::TypeSpecifierNonArray::SamplerBuffer => { let _ = f.write_str("samplerBuffer"); } syntax::TypeSpecifierNonArray::ImageBuffer => { let _ = f.write_str("imageBuffer"); } syntax::TypeSpecifierNonArray::Sampler2DMS => { let _ = f.write_str("sampler2DMS"); } syntax::TypeSpecifierNonArray::Image2DMS => { let _ = f.write_str("image2DMS"); } syntax::TypeSpecifierNonArray::Sampler2DMSArray => { let _ = f.write_str("sampler2DMSArray"); } syntax::TypeSpecifierNonArray::Image2DMSArray => { let _ = f.write_str("image2DMSArray"); } syntax::TypeSpecifierNonArray::SamplerCubeArray => { let _ = f.write_str("samplerCubeArray"); } syntax::TypeSpecifierNonArray::ImageCubeArray => { let _ = f.write_str("imageCubeArray"); } syntax::TypeSpecifierNonArray::Sampler1DShadow => { let _ = f.write_str("sampler1DShadow"); } syntax::TypeSpecifierNonArray::Sampler2DShadow => { let _ = f.write_str("sampler2DShadow"); } syntax::TypeSpecifierNonArray::Sampler2DRectShadow => { let _ = f.write_str("sampler2DRectShadow"); } syntax::TypeSpecifierNonArray::Sampler1DArrayShadow => { let _ = f.write_str("sampler1DArrayShadow"); } syntax::TypeSpecifierNonArray::Sampler2DArrayShadow => { let _ = f.write_str("sampler2DArrayShadow"); } syntax::TypeSpecifierNonArray::SamplerCubeShadow => { let _ = f.write_str("samplerCubeShadow"); } syntax::TypeSpecifierNonArray::SamplerCubeArrayShadow => { let _ = f.write_str("samplerCubeArrayShadow"); } syntax::TypeSpecifierNonArray::ISampler1D => { let _ = f.write_str("isampler1D"); } syntax::TypeSpecifierNonArray::IImage1D => { let _ = f.write_str("iimage1D"); } syntax::TypeSpecifierNonArray::ISampler2D => { let _ = f.write_str("isampler2D"); } syntax::TypeSpecifierNonArray::IImage2D => { let _ = f.write_str("iimage2D"); } syntax::TypeSpecifierNonArray::ISampler3D => { let _ = f.write_str("isampler3D"); } syntax::TypeSpecifierNonArray::IImage3D => { let _ = f.write_str("iimage3D"); } syntax::TypeSpecifierNonArray::ISamplerCube => { let _ = f.write_str("isamplerCube"); } syntax::TypeSpecifierNonArray::IImageCube => { let _ = f.write_str("iimageCube"); } syntax::TypeSpecifierNonArray::ISampler2DRect => { let _ = f.write_str("isampler2DRect"); } syntax::TypeSpecifierNonArray::IImage2DRect => { let _ = f.write_str("iimage2DRect"); } syntax::TypeSpecifierNonArray::ISampler1DArray => { let _ = f.write_str("isampler1DArray"); } syntax::TypeSpecifierNonArray::IImage1DArray => { let _ = f.write_str("iimage1DArray"); } syntax::TypeSpecifierNonArray::ISampler2DArray => { let _ = f.write_str("isampler2DArray"); } syntax::TypeSpecifierNonArray::IImage2DArray => { let _ = f.write_str("iimage2DArray"); } syntax::TypeSpecifierNonArray::ISamplerBuffer => { let _ = f.write_str("isamplerBuffer"); } syntax::TypeSpecifierNonArray::IImageBuffer => { let _ = f.write_str("iimageBuffer"); } syntax::TypeSpecifierNonArray::ISampler2DMS => { let _ = f.write_str("isampler2MS"); } syntax::TypeSpecifierNonArray::IImage2DMS => { let _ = f.write_str("iimage2DMS"); } syntax::TypeSpecifierNonArray::ISampler2DMSArray => { let _ = f.write_str("isampler2DMSArray"); } syntax::TypeSpecifierNonArray::IImage2DMSArray => { let _ = f.write_str("iimage2DMSArray"); } syntax::TypeSpecifierNonArray::ISamplerCubeArray => { let _ = f.write_str("isamplerCubeArray"); } syntax::TypeSpecifierNonArray::IImageCubeArray => { let _ = f.write_str("iimageCubeArray"); } syntax::TypeSpecifierNonArray::AtomicUInt => { let _ = f.write_str("atomic_uint"); } syntax::TypeSpecifierNonArray::USampler1D => { let _ = f.write_str("usampler1D"); } syntax::TypeSpecifierNonArray::UImage1D => { let _ = f.write_str("uimage1D"); } syntax::TypeSpecifierNonArray::USampler2D => { let _ = f.write_str("usampler2D"); } syntax::TypeSpecifierNonArray::UImage2D => { let _ = f.write_str("uimage2D"); } syntax::TypeSpecifierNonArray::USampler3D => { let _ = f.write_str("usampler3D"); } syntax::TypeSpecifierNonArray::UImage3D => { let _ = f.write_str("uimage3D"); } syntax::TypeSpecifierNonArray::USamplerCube => { let _ = f.write_str("usamplerCube"); } syntax::TypeSpecifierNonArray::UImageCube => { let _ = f.write_str("uimageCube"); } syntax::TypeSpecifierNonArray::USampler2DRect => { let _ = f.write_str("usampler2DRect"); } syntax::TypeSpecifierNonArray::UImage2DRect => { let _ = f.write_str("uimage2DRect"); } syntax::TypeSpecifierNonArray::USampler1DArray => { let _ = f.write_str("usampler1DArray"); } syntax::TypeSpecifierNonArray::UImage1DArray => { let _ = f.write_str("uimage1DArray"); } syntax::TypeSpecifierNonArray::USampler2DArray => { let _ = f.write_str("usampler2DArray"); } syntax::TypeSpecifierNonArray::UImage2DArray => { let _ = f.write_str("uimage2DArray"); } syntax::TypeSpecifierNonArray::USamplerBuffer => { let _ = f.write_str("usamplerBuffer"); } syntax::TypeSpecifierNonArray::UImageBuffer => { let _ = f.write_str("uimageBuffer"); } syntax::TypeSpecifierNonArray::USampler2DMS => { let _ = f.write_str("usampler2DMS"); } syntax::TypeSpecifierNonArray::UImage2DMS => { let _ = f.write_str("uimage2DMS"); } syntax::TypeSpecifierNonArray::USampler2DMSArray => { let _ = f.write_str("usamplerDMSArray"); } syntax::TypeSpecifierNonArray::UImage2DMSArray => { let _ = f.write_str("uimage2DMSArray"); } syntax::TypeSpecifierNonArray::USamplerCubeArray => { let _ = f.write_str("usamplerCubeArray"); } syntax::TypeSpecifierNonArray::UImageCubeArray => { let _ = f.write_str("uimageCubeArray"); } syntax::TypeSpecifierNonArray::Struct(ref s) => show_struct_non_declaration(f, s), syntax::TypeSpecifierNonArray::TypeName(ref tn) => show_type_name(f, tn), } } pub fn show_type_specifier(f: &mut F, t: &syntax::TypeSpecifier) where F: Write, { show_type_specifier_non_array(f, &t.ty); if let Some(ref arr_spec) = t.array_specifier { show_array_spec(f, arr_spec); } } pub fn show_fully_specified_type(f: &mut F, t: &syntax::FullySpecifiedType) where F: Write, { if let Some(ref qual) = t.qualifier { show_type_qualifier(f, &qual); let _ = f.write_str(" "); } show_type_specifier(f, &t.ty); } pub fn show_struct_non_declaration(f: &mut F, s: &syntax::StructSpecifier) where F: Write, { let _ = f.write_str("struct "); if let Some(ref name) = s.name { let _ = write!(f, "{} ", name); } let _ = f.write_str("{\n"); for field in &s.fields.0 { show_struct_field(f, field); } let _ = f.write_str("}"); } pub fn show_struct(f: &mut F, s: &syntax::StructSpecifier) where F: Write, { show_struct_non_declaration(f, s); let _ = f.write_str(";\n"); } pub fn show_struct_field(f: &mut F, field: &syntax::StructFieldSpecifier) where F: Write, { if let Some(ref qual) = field.qualifier { show_type_qualifier(f, &qual); let _ = f.write_str(" "); } show_type_specifier(f, &field.ty); let _ = f.write_str(" "); // there’s at least one identifier let mut identifiers = field.identifiers.0.iter(); let identifier = identifiers.next().unwrap(); show_arrayed_identifier(f, identifier); // write the rest of the identifiers for identifier in identifiers { let _ = f.write_str(", "); show_arrayed_identifier(f, identifier); } let _ = f.write_str(";\n"); } pub fn show_array_spec(f: &mut F, a: &syntax::ArraySpecifier) where F: Write, { for dimension in &a.dimensions { match *dimension { syntax::ArraySpecifierDimension::Unsized => { let _ = f.write_str("[]"); } syntax::ArraySpecifierDimension::ExplicitlySized(ref e) => { let _ = f.write_str("["); show_expr(f, &e); let _ = f.write_str("]"); } } } } pub fn show_arrayed_identifier(f: &mut F, a: &syntax::ArrayedIdentifier) where F: Write, { let _ = write!(f, "{}", a.ident); if let Some(ref arr_spec) = a.array_spec { show_array_spec(f, arr_spec); } } pub fn show_type_qualifier(f: &mut F, q: &syntax::TypeQualifier) where F: Write, { let mut qualifiers = q.qualifiers.0.iter(); let first = qualifiers.next().unwrap(); show_type_qualifier_spec(f, first); for qual_spec in qualifiers { let _ = f.write_str(" "); show_type_qualifier_spec(f, qual_spec) } } pub fn show_type_qualifier_spec(f: &mut F, q: &syntax::TypeQualifierSpec) where F: Write, { match *q { syntax::TypeQualifierSpec::Storage(ref s) => show_storage_qualifier(f, &s), syntax::TypeQualifierSpec::Layout(ref l) => show_layout_qualifier(f, &l), syntax::TypeQualifierSpec::Precision(ref p) => show_precision_qualifier(f, &p), syntax::TypeQualifierSpec::Interpolation(ref i) => show_interpolation_qualifier(f, &i), syntax::TypeQualifierSpec::Invariant => { let _ = f.write_str("invariant"); } syntax::TypeQualifierSpec::Precise => { let _ = f.write_str("precise"); } } } pub fn show_storage_qualifier(f: &mut F, q: &syntax::StorageQualifier) where F: Write, { match *q { syntax::StorageQualifier::Const => { let _ = f.write_str("const"); } syntax::StorageQualifier::InOut => { let _ = f.write_str("inout"); } syntax::StorageQualifier::In => { let _ = f.write_str("in"); } syntax::StorageQualifier::Out => { let _ = f.write_str("out"); } syntax::StorageQualifier::Centroid => { let _ = f.write_str("centroid"); } syntax::StorageQualifier::Patch => { let _ = f.write_str("patch"); } syntax::StorageQualifier::Sample => { let _ = f.write_str("sample"); } syntax::StorageQualifier::Uniform => { let _ = f.write_str("uniform"); } syntax::StorageQualifier::Attribute => { let _ = f.write_str("attribute"); } syntax::StorageQualifier::Varying => { let _ = f.write_str("varying"); } syntax::StorageQualifier::Buffer => { let _ = f.write_str("buffer"); } syntax::StorageQualifier::Shared => { let _ = f.write_str("shared"); } syntax::StorageQualifier::Coherent => { let _ = f.write_str("coherent"); } syntax::StorageQualifier::Volatile => { let _ = f.write_str("volatile"); } syntax::StorageQualifier::Restrict => { let _ = f.write_str("restrict"); } syntax::StorageQualifier::ReadOnly => { let _ = f.write_str("readonly"); } syntax::StorageQualifier::WriteOnly => { let _ = f.write_str("writeonly"); } syntax::StorageQualifier::Subroutine(ref n) => show_subroutine(f, &n), } } pub fn show_subroutine(f: &mut F, types: &Vec) where F: Write, { let _ = f.write_str("subroutine"); if !types.is_empty() { let _ = f.write_str("("); let mut types_iter = types.iter(); let first = types_iter.next().unwrap(); show_type_name(f, first); for type_name in types_iter { let _ = f.write_str(", "); show_type_name(f, type_name); } let _ = f.write_str(")"); } } pub fn show_layout_qualifier(f: &mut F, l: &syntax::LayoutQualifier) where F: Write, { let mut qualifiers = l.ids.0.iter(); let first = qualifiers.next().unwrap(); let _ = f.write_str("layout ("); show_layout_qualifier_spec(f, first); for qual_spec in qualifiers { let _ = f.write_str(", "); show_layout_qualifier_spec(f, qual_spec); } let _ = f.write_str(")"); } pub fn show_layout_qualifier_spec(f: &mut F, l: &syntax::LayoutQualifierSpec) where F: Write, { match *l { syntax::LayoutQualifierSpec::Identifier(ref i, Some(ref e)) => { let _ = write!(f, "{} = ", i); show_expr(f, &e); } syntax::LayoutQualifierSpec::Identifier(ref i, None) => show_identifier(f, &i), syntax::LayoutQualifierSpec::Shared => { let _ = f.write_str("shared"); } } } pub fn show_precision_qualifier(f: &mut F, p: &syntax::PrecisionQualifier) where F: Write, { match *p { syntax::PrecisionQualifier::High => { let _ = f.write_str("highp"); } syntax::PrecisionQualifier::Medium => { let _ = f.write_str("mediump"); } syntax::PrecisionQualifier::Low => { let _ = f.write_str("low"); } } } pub fn show_interpolation_qualifier(f: &mut F, i: &syntax::InterpolationQualifier) where F: Write, { match *i { syntax::InterpolationQualifier::Smooth => { let _ = f.write_str("smooth"); } syntax::InterpolationQualifier::Flat => { let _ = f.write_str("flat"); } syntax::InterpolationQualifier::NoPerspective => { let _ = f.write_str("noperspective"); } } } pub fn show_float(f: &mut F, x: f32) where F: Write, { if x.fract() == 0. { let _ = write!(f, "{}.", x); } else { let _ = write!(f, "{}", x); } } pub fn show_double(f: &mut F, x: f64) where F: Write, { if x.fract() == 0. { let _ = write!(f, "{}.lf", x); } else { let _ = write!(f, "{}lf", x); } } pub fn show_expr(f: &mut F, expr: &syntax::Expr) where F: Write, { match *expr { syntax::Expr::Variable(ref i) => show_identifier(f, &i), syntax::Expr::IntConst(ref x) => { let _ = write!(f, "{}", x); } syntax::Expr::UIntConst(ref x) => { let _ = write!(f, "{}u", x); } syntax::Expr::BoolConst(ref x) => { let _ = write!(f, "{}", x); } syntax::Expr::FloatConst(ref x) => show_float(f, *x), syntax::Expr::DoubleConst(ref x) => show_double(f, *x), syntax::Expr::Unary(ref op, ref e) => { // Note: all unary ops are right-to-left associative show_unary_op(f, &op); if e.precedence() > op.precedence() { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } else if let syntax::Expr::Unary(eop, _) = &**e { // Prevent double-unary plus/minus turning into inc/dec if eop == op && (*eop == syntax::UnaryOp::Add || *eop == syntax::UnaryOp::Minus) { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } else { show_expr(f, &e); } } else { show_expr(f, &e); } } syntax::Expr::Binary(ref op, ref l, ref r) => { // Note: all binary ops are left-to-right associative (<= for left part) if l.precedence() <= op.precedence() { show_expr(f, &l); } else { let _ = f.write_str("("); show_expr(f, &l); let _ = f.write_str(")"); } show_binary_op(f, &op); if r.precedence() < op.precedence() { show_expr(f, &r); } else { let _ = f.write_str("("); show_expr(f, &r); let _ = f.write_str(")"); } } syntax::Expr::Ternary(ref c, ref s, ref e) => { // Note: ternary is right-to-left associative (<= for right part) if c.precedence() < expr.precedence() { show_expr(f, &c); } else { let _ = f.write_str("("); show_expr(f, &c); let _ = f.write_str(")"); } let _ = f.write_str(" ? "); show_expr(f, &s); let _ = f.write_str(" : "); if e.precedence() <= expr.precedence() { show_expr(f, &e); } else { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } } syntax::Expr::Assignment(ref v, ref op, ref e) => { // Note: all assignment ops are right-to-left associative if v.precedence() < op.precedence() { show_expr(f, &v); } else { let _ = f.write_str("("); show_expr(f, &v); let _ = f.write_str(")"); } let _ = f.write_str(" "); show_assignment_op(f, &op); let _ = f.write_str(" "); if e.precedence() <= op.precedence() { show_expr(f, &e); } else { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } } syntax::Expr::Bracket(ref e, ref a) => { // Note: bracket is left-to-right associative if e.precedence() <= expr.precedence() { show_expr(f, &e); } else { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } show_array_spec(f, &a); } syntax::Expr::FunCall(ref fun, ref args) => { show_function_identifier(f, &fun); let _ = f.write_str("("); if !args.is_empty() { let mut args_iter = args.iter(); let first = args_iter.next().unwrap(); show_expr(f, first); for e in args_iter { let _ = f.write_str(", "); show_expr(f, e); } } let _ = f.write_str(")"); } syntax::Expr::Dot(ref e, ref i) => { // Note: dot is left-to-right associative if e.precedence() <= expr.precedence() { show_expr(f, &e); } else { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } let _ = f.write_str("."); show_identifier(f, &i); } syntax::Expr::PostInc(ref e) => { // Note: post-increment is right-to-left associative if e.precedence() < expr.precedence() { show_expr(f, &e); } else { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } let _ = f.write_str("++"); } syntax::Expr::PostDec(ref e) => { // Note: post-decrement is right-to-left associative if e.precedence() < expr.precedence() { show_expr(f, &e); } else { let _ = f.write_str("("); show_expr(f, &e); let _ = f.write_str(")"); } let _ = f.write_str("--"); } syntax::Expr::Comma(ref a, ref b) => { // Note: comma is left-to-right associative if a.precedence() <= expr.precedence() { show_expr(f, &a); } else { let _ = f.write_str("("); show_expr(f, &a); let _ = f.write_str(")"); } let _ = f.write_str(", "); if b.precedence() < expr.precedence() { show_expr(f, &b); } else { let _ = f.write_str("("); show_expr(f, &b); let _ = f.write_str(")"); } } } } pub fn show_path(f: &mut F, path: &syntax::Path) where F: Write, { match path { syntax::Path::Absolute(s) => { let _ = write!(f, "<{}>", s); } syntax::Path::Relative(s) => { let _ = write!(f, "\"{}\"", s); } } } pub fn show_unary_op(f: &mut F, op: &syntax::UnaryOp) where F: Write, { match *op { syntax::UnaryOp::Inc => { let _ = f.write_str("++"); } syntax::UnaryOp::Dec => { let _ = f.write_str("--"); } syntax::UnaryOp::Add => { let _ = f.write_str("+"); } syntax::UnaryOp::Minus => { let _ = f.write_str("-"); } syntax::UnaryOp::Not => { let _ = f.write_str("!"); } syntax::UnaryOp::Complement => { let _ = f.write_str("~"); } } } pub fn show_binary_op(f: &mut F, op: &syntax::BinaryOp) where F: Write, { match *op { syntax::BinaryOp::Or => { let _ = f.write_str("||"); } syntax::BinaryOp::Xor => { let _ = f.write_str("^^"); } syntax::BinaryOp::And => { let _ = f.write_str("&&"); } syntax::BinaryOp::BitOr => { let _ = f.write_str("|"); } syntax::BinaryOp::BitXor => { let _ = f.write_str("^"); } syntax::BinaryOp::BitAnd => { let _ = f.write_str("&"); } syntax::BinaryOp::Equal => { let _ = f.write_str("=="); } syntax::BinaryOp::NonEqual => { let _ = f.write_str("!="); } syntax::BinaryOp::LT => { let _ = f.write_str("<"); } syntax::BinaryOp::GT => { let _ = f.write_str(">"); } syntax::BinaryOp::LTE => { let _ = f.write_str("<="); } syntax::BinaryOp::GTE => { let _ = f.write_str(">="); } syntax::BinaryOp::LShift => { let _ = f.write_str("<<"); } syntax::BinaryOp::RShift => { let _ = f.write_str(">>"); } syntax::BinaryOp::Add => { let _ = f.write_str("+"); } syntax::BinaryOp::Sub => { let _ = f.write_str("-"); } syntax::BinaryOp::Mult => { let _ = f.write_str("*"); } syntax::BinaryOp::Div => { let _ = f.write_str("/"); } syntax::BinaryOp::Mod => { let _ = f.write_str("%"); } } } pub fn show_assignment_op(f: &mut F, op: &syntax::AssignmentOp) where F: Write, { match *op { syntax::AssignmentOp::Equal => { let _ = f.write_str("="); } syntax::AssignmentOp::Mult => { let _ = f.write_str("*="); } syntax::AssignmentOp::Div => { let _ = f.write_str("/="); } syntax::AssignmentOp::Mod => { let _ = f.write_str("%="); } syntax::AssignmentOp::Add => { let _ = f.write_str("+="); } syntax::AssignmentOp::Sub => { let _ = f.write_str("-="); } syntax::AssignmentOp::LShift => { let _ = f.write_str("<<="); } syntax::AssignmentOp::RShift => { let _ = f.write_str(">>="); } syntax::AssignmentOp::And => { let _ = f.write_str("&="); } syntax::AssignmentOp::Xor => { let _ = f.write_str("^="); } syntax::AssignmentOp::Or => { let _ = f.write_str("|="); } } } pub fn show_function_identifier(f: &mut F, i: &syntax::FunIdentifier) where F: Write, { match *i { syntax::FunIdentifier::Identifier(ref n) => show_identifier(f, &n), syntax::FunIdentifier::Expr(ref e) => show_expr(f, &*e), } } pub fn show_declaration(f: &mut F, d: &syntax::Declaration) where F: Write, { match *d { syntax::Declaration::FunctionPrototype(ref proto) => { show_function_prototype(f, &proto); let _ = f.write_str(";\n"); } syntax::Declaration::InitDeclaratorList(ref list) => { show_init_declarator_list(f, &list); let _ = f.write_str(";\n"); } syntax::Declaration::Precision(ref qual, ref ty) => { show_precision_qualifier(f, &qual); show_type_specifier(f, &ty); let _ = f.write_str(";\n"); } syntax::Declaration::Block(ref block) => { show_block(f, &block); let _ = f.write_str(";\n"); } syntax::Declaration::Global(ref qual, ref identifiers) => { show_type_qualifier(f, &qual); if !identifiers.is_empty() { let mut iter = identifiers.iter(); let first = iter.next().unwrap(); show_identifier(f, first); for identifier in iter { let _ = write!(f, ", {}", identifier); } } let _ = f.write_str(";\n"); } } } pub fn show_function_prototype(f: &mut F, fp: &syntax::FunctionPrototype) where F: Write, { show_fully_specified_type(f, &fp.ty); let _ = f.write_str(" "); show_identifier(f, &fp.name); let _ = f.write_str("("); if !fp.parameters.is_empty() { let mut iter = fp.parameters.iter(); let first = iter.next().unwrap(); show_function_parameter_declaration(f, first); for param in iter { let _ = f.write_str(", "); show_function_parameter_declaration(f, param); } } let _ = f.write_str(")"); } pub fn show_function_parameter_declaration(f: &mut F, p: &syntax::FunctionParameterDeclaration) where F: Write, { match *p { syntax::FunctionParameterDeclaration::Named(ref qual, ref fpd) => { if let Some(ref q) = *qual { show_type_qualifier(f, q); let _ = f.write_str(" "); } show_function_parameter_declarator(f, fpd); } syntax::FunctionParameterDeclaration::Unnamed(ref qual, ref ty) => { if let Some(ref q) = *qual { show_type_qualifier(f, q); let _ = f.write_str(" "); } show_type_specifier(f, ty); } } } pub fn show_function_parameter_declarator(f: &mut F, p: &syntax::FunctionParameterDeclarator) where F: Write, { show_type_specifier(f, &p.ty); let _ = f.write_str(" "); show_arrayed_identifier(f, &p.ident); } pub fn show_init_declarator_list(f: &mut F, i: &syntax::InitDeclaratorList) where F: Write, { show_single_declaration(f, &i.head); for decl in &i.tail { let _ = f.write_str(", "); show_single_declaration_no_type(f, decl); } } pub fn show_single_declaration(f: &mut F, d: &syntax::SingleDeclaration) where F: Write, { show_fully_specified_type(f, &d.ty); if let Some(ref name) = d.name { let _ = f.write_str(" "); show_identifier(f, name); } if let Some(ref arr_spec) = d.array_specifier { show_array_spec(f, arr_spec); } if let Some(ref initializer) = d.initializer { let _ = f.write_str(" = "); show_initializer(f, initializer); } } pub fn show_single_declaration_no_type(f: &mut F, d: &syntax::SingleDeclarationNoType) where F: Write, { show_arrayed_identifier(f, &d.ident); if let Some(ref initializer) = d.initializer { let _ = f.write_str(" = "); show_initializer(f, initializer); } } pub fn show_initializer(f: &mut F, i: &syntax::Initializer) where F: Write, { match *i { syntax::Initializer::Simple(ref e) => show_expr(f, e), syntax::Initializer::List(ref list) => { let mut iter = list.0.iter(); let first = iter.next().unwrap(); let _ = f.write_str("{ "); show_initializer(f, first); for ini in iter { let _ = f.write_str(", "); show_initializer(f, ini); } let _ = f.write_str(" }"); } } } pub fn show_block(f: &mut F, b: &syntax::Block) where F: Write, { show_type_qualifier(f, &b.qualifier); let _ = f.write_str(" "); show_identifier(f, &b.name); let _ = f.write_str(" {"); for field in &b.fields { show_struct_field(f, field); let _ = f.write_str("\n"); } let _ = f.write_str("}"); if let Some(ref ident) = b.identifier { show_arrayed_identifier(f, ident); } } pub fn show_function_definition(f: &mut F, fd: &syntax::FunctionDefinition) where F: Write, { show_function_prototype(f, &fd.prototype); let _ = f.write_str(" "); show_compound_statement(f, &fd.statement); } pub fn show_compound_statement(f: &mut F, cst: &syntax::CompoundStatement) where F: Write, { let _ = f.write_str("{\n"); for st in &cst.statement_list { show_statement(f, st); } let _ = f.write_str("}\n"); } pub fn show_statement(f: &mut F, st: &syntax::Statement) where F: Write, { match *st { syntax::Statement::Compound(ref cst) => show_compound_statement(f, cst), syntax::Statement::Simple(ref sst) => show_simple_statement(f, sst), } } pub fn show_simple_statement(f: &mut F, sst: &syntax::SimpleStatement) where F: Write, { match *sst { syntax::SimpleStatement::Declaration(ref d) => show_declaration(f, d), syntax::SimpleStatement::Expression(ref e) => show_expression_statement(f, e), syntax::SimpleStatement::Selection(ref s) => show_selection_statement(f, s), syntax::SimpleStatement::Switch(ref s) => show_switch_statement(f, s), syntax::SimpleStatement::CaseLabel(ref cl) => show_case_label(f, cl), syntax::SimpleStatement::Iteration(ref i) => show_iteration_statement(f, i), syntax::SimpleStatement::Jump(ref j) => show_jump_statement(f, j), } } pub fn show_expression_statement(f: &mut F, est: &syntax::ExprStatement) where F: Write, { if let Some(ref e) = *est { show_expr(f, e); } let _ = f.write_str(";\n"); } pub fn show_selection_statement(f: &mut F, sst: &syntax::SelectionStatement) where F: Write, { let _ = f.write_str("if ("); show_expr(f, &sst.cond); let _ = f.write_str(") {\n"); show_selection_rest_statement(f, &sst.rest); } pub fn show_selection_rest_statement(f: &mut F, sst: &syntax::SelectionRestStatement) where F: Write, { match *sst { syntax::SelectionRestStatement::Statement(ref if_st) => { show_statement(f, if_st); let _ = f.write_str("}\n"); } syntax::SelectionRestStatement::Else(ref if_st, ref else_st) => { show_statement(f, if_st); let _ = f.write_str("} else "); show_statement(f, else_st); } } } pub fn show_switch_statement(f: &mut F, sst: &syntax::SwitchStatement) where F: Write, { let _ = f.write_str("switch ("); show_expr(f, &sst.head); let _ = f.write_str(") {\n"); for st in &sst.body { show_statement(f, st); } let _ = f.write_str("}\n"); } pub fn show_case_label(f: &mut F, cl: &syntax::CaseLabel) where F: Write, { match *cl { syntax::CaseLabel::Case(ref e) => { let _ = f.write_str("case "); show_expr(f, e); let _ = f.write_str(":\n"); } syntax::CaseLabel::Def => { let _ = f.write_str("default:\n"); } } } pub fn show_iteration_statement(f: &mut F, ist: &syntax::IterationStatement) where F: Write, { match *ist { syntax::IterationStatement::While(ref cond, ref body) => { let _ = f.write_str("while ("); show_condition(f, cond); let _ = f.write_str(") "); show_statement(f, body); } syntax::IterationStatement::DoWhile(ref body, ref cond) => { let _ = f.write_str("do "); show_statement(f, body); let _ = f.write_str(" while ("); show_expr(f, cond); let _ = f.write_str(")\n"); } syntax::IterationStatement::For(ref init, ref rest, ref body) => { let _ = f.write_str("for ("); show_for_init_statement(f, init); show_for_rest_statement(f, rest); let _ = f.write_str(") "); show_statement(f, body); } } } pub fn show_condition(f: &mut F, c: &syntax::Condition) where F: Write, { match *c { syntax::Condition::Expr(ref e) => show_expr(f, e), syntax::Condition::Assignment(ref ty, ref name, ref initializer) => { show_fully_specified_type(f, ty); let _ = f.write_str(" "); show_identifier(f, name); let _ = f.write_str(" = "); show_initializer(f, initializer); } } } pub fn show_for_init_statement(f: &mut F, i: &syntax::ForInitStatement) where F: Write, { match *i { syntax::ForInitStatement::Expression(ref expr) => { if let Some(ref e) = *expr { show_expr(f, e); } } syntax::ForInitStatement::Declaration(ref d) => show_declaration(f, d), } } pub fn show_for_rest_statement(f: &mut F, r: &syntax::ForRestStatement) where F: Write, { if let Some(ref cond) = r.condition { show_condition(f, cond); } let _ = f.write_str("; "); if let Some(ref e) = r.post_expr { show_expr(f, e); } } pub fn show_jump_statement(f: &mut F, j: &syntax::JumpStatement) where F: Write, { match *j { syntax::JumpStatement::Continue => { let _ = f.write_str("continue;\n"); } syntax::JumpStatement::Break => { let _ = f.write_str("break;\n"); } syntax::JumpStatement::Discard => { let _ = f.write_str("discard;\n"); } syntax::JumpStatement::Return(ref e) => { let _ = f.write_str("return "); if let Some(e) = e { show_expr(f, e); } let _ = f.write_str(";\n"); } } } pub fn show_preprocessor(f: &mut F, pp: &syntax::Preprocessor) where F: Write, { match *pp { syntax::Preprocessor::Define(ref pd) => show_preprocessor_define(f, pd), syntax::Preprocessor::Else => show_preprocessor_else(f), syntax::Preprocessor::ElseIf(ref pei) => show_preprocessor_elseif(f, pei), syntax::Preprocessor::EndIf => show_preprocessor_endif(f), syntax::Preprocessor::Error(ref pe) => show_preprocessor_error(f, pe), syntax::Preprocessor::If(ref pi) => show_preprocessor_if(f, pi), syntax::Preprocessor::IfDef(ref pid) => show_preprocessor_ifdef(f, pid), syntax::Preprocessor::IfNDef(ref pind) => show_preprocessor_ifndef(f, pind), syntax::Preprocessor::Include(ref pi) => show_preprocessor_include(f, pi), syntax::Preprocessor::Line(ref pl) => show_preprocessor_line(f, pl), syntax::Preprocessor::Pragma(ref pp) => show_preprocessor_pragma(f, pp), syntax::Preprocessor::Undef(ref pu) => show_preprocessor_undef(f, pu), syntax::Preprocessor::Version(ref pv) => show_preprocessor_version(f, pv), syntax::Preprocessor::Extension(ref pe) => show_preprocessor_extension(f, pe), } } pub fn show_preprocessor_define(f: &mut F, pd: &syntax::PreprocessorDefine) where F: Write, { match *pd { syntax::PreprocessorDefine::ObjectLike { ref ident, ref value, } => { let _ = write!(f, "#define {} {}\n", ident, value); } syntax::PreprocessorDefine::FunctionLike { ref ident, ref args, ref value, } => { let _ = write!(f, "#define {}(", ident); if !args.is_empty() { let _ = write!(f, "{}", &args[0]); for arg in &args[1..args.len()] { let _ = write!(f, ", {}", arg); } } let _ = write!(f, ") {}\n", value); } } } pub fn show_preprocessor_else(f: &mut F) where F: Write, { let _ = f.write_str("#else\n"); } pub fn show_preprocessor_elseif(f: &mut F, pei: &syntax::PreprocessorElseIf) where F: Write, { let _ = write!(f, "#elseif {}\n", pei.condition); } pub fn show_preprocessor_error(f: &mut F, pe: &syntax::PreprocessorError) where F: Write, { let _ = writeln!(f, "#error {}", pe.message); } pub fn show_preprocessor_endif(f: &mut F) where F: Write, { let _ = f.write_str("#endif\n"); } pub fn show_preprocessor_if(f: &mut F, pi: &syntax::PreprocessorIf) where F: Write, { let _ = write!(f, "#if {}\n", pi.condition); } pub fn show_preprocessor_ifdef(f: &mut F, pid: &syntax::PreprocessorIfDef) where F: Write, { let _ = f.write_str("#ifdef "); show_identifier(f, &pid.ident); let _ = f.write_str("\n"); } pub fn show_preprocessor_ifndef(f: &mut F, pind: &syntax::PreprocessorIfNDef) where F: Write, { let _ = f.write_str("#ifndef "); show_identifier(f, &pind.ident); let _ = f.write_str("\n"); } pub fn show_preprocessor_include(f: &mut F, pi: &syntax::PreprocessorInclude) where F: Write, { let _ = f.write_str("#include "); show_path(f, &pi.path); let _ = f.write_str("\n"); } pub fn show_preprocessor_line(f: &mut F, pl: &syntax::PreprocessorLine) where F: Write, { let _ = write!(f, "#line {}", pl.line); if let Some(source_string_number) = pl.source_string_number { let _ = write!(f, " {}", source_string_number); } let _ = f.write_str("\n"); } pub fn show_preprocessor_pragma(f: &mut F, pp: &syntax::PreprocessorPragma) where F: Write, { let _ = writeln!(f, "#pragma {}", pp.command); } pub fn show_preprocessor_undef(f: &mut F, pud: &syntax::PreprocessorUndef) where F: Write, { let _ = f.write_str("#undef "); show_identifier(f, &pud.name); let _ = f.write_str("\n"); } pub fn show_preprocessor_version(f: &mut F, pv: &syntax::PreprocessorVersion) where F: Write, { let _ = write!(f, "#version {}", pv.version); if let Some(ref profile) = pv.profile { match *profile { syntax::PreprocessorVersionProfile::Core => { let _ = f.write_str(" core"); } syntax::PreprocessorVersionProfile::Compatibility => { let _ = f.write_str(" compatibility"); } syntax::PreprocessorVersionProfile::ES => { let _ = f.write_str(" es"); } } } let _ = f.write_str("\n"); } pub fn show_preprocessor_extension(f: &mut F, pe: &syntax::PreprocessorExtension) where F: Write, { let _ = f.write_str("#extension "); match pe.name { syntax::PreprocessorExtensionName::All => { let _ = f.write_str("all"); } syntax::PreprocessorExtensionName::Specific(ref n) => { let _ = f.write_str(n); } } if let Some(ref behavior) = pe.behavior { match *behavior { syntax::PreprocessorExtensionBehavior::Require => { let _ = f.write_str(" : require"); } syntax::PreprocessorExtensionBehavior::Enable => { let _ = f.write_str(" : enable"); } syntax::PreprocessorExtensionBehavior::Warn => { let _ = f.write_str(" : warn"); } syntax::PreprocessorExtensionBehavior::Disable => { let _ = f.write_str(" : disable"); } } } let _ = f.write_str("\n"); } pub fn show_external_declaration(f: &mut F, ed: &syntax::ExternalDeclaration) where F: Write, { match *ed { syntax::ExternalDeclaration::Preprocessor(ref pp) => show_preprocessor(f, pp), syntax::ExternalDeclaration::FunctionDefinition(ref fd) => show_function_definition(f, fd), syntax::ExternalDeclaration::Declaration(ref d) => show_declaration(f, d), } } pub fn show_translation_unit(f: &mut F, tu: &syntax::TranslationUnit) where F: Write, { for ed in &(tu.0).0 { show_external_declaration(f, ed); } } #[cfg(test)] mod tests { use super::*; use crate::parsers::expr; fn to_string(e: &syntax::Expr) -> String { let mut s = String::new(); show_expr(&mut s, e); s } #[test] fn unary_parentheses() { assert_eq!(to_string(&expr("-a").unwrap().1), "-a"); assert_eq!(to_string(&expr("-(a + b)").unwrap().1), "-(a+b)"); assert_eq!(to_string(&expr("-a.x").unwrap().1), "-a.x"); assert_eq!(to_string(&expr("-(-a)").unwrap().1), "-(-a)"); assert_eq!(to_string(&expr("+(+a)").unwrap().1), "+(+a)"); assert_eq!(to_string(&expr("~~a").unwrap().1), "~~a"); assert_eq!(to_string(&expr("--a").unwrap().1), "--a"); assert_eq!(to_string(&expr("++a").unwrap().1), "++a"); assert_eq!(to_string(&expr("+-a").unwrap().1), "+-a"); } #[test] fn binary_parentheses() { assert_eq!(to_string(&expr("a + b").unwrap().1), "a+b"); assert_eq!(to_string(&expr("a * b + c").unwrap().1), "a*b+c"); assert_eq!(to_string(&expr("(a + b) * c").unwrap().1), "(a+b)*c"); assert_eq!(to_string(&expr("a + (b * c)").unwrap().1), "a+b*c"); assert_eq!(to_string(&expr("a * (b + c)").unwrap().1), "a*(b+c)"); assert_eq!(to_string(&expr("(a * b) * c").unwrap().1), "a*b*c"); assert_eq!(to_string(&expr("a * (b * c)").unwrap().1), "a*(b*c)"); assert_eq!(to_string(&expr("a&&b&&c").unwrap().1), "a&&b&&c"); assert_eq!( to_string(&expr("n - p > 0. && u.y < n && u.y > p").unwrap().1), "n-p>0.&&u.yp" ); } #[test] fn ternary_parentheses() { assert_eq!( to_string(&expr("a ? b : c ? d : e").unwrap().1), "a ? b : c ? d : e" ); assert_eq!( to_string(&expr("(a ? b : c) ? d : e").unwrap().1), "(a ? b : c) ? d : e" ); } #[test] fn assignment_parentheses() { assert_eq!(to_string(&expr("a = b = c").unwrap().1), "a = b = c"); assert_eq!(to_string(&expr("(a = b) = c").unwrap().1), "(a = b) = c"); } #[test] fn dot_parentheses() { assert_eq!(to_string(&expr("a.x").unwrap().1), "a.x"); assert_eq!(to_string(&expr("(a + b).x").unwrap().1), "(a+b).x"); } #[test] fn test_parentheses() { use crate::parsers::function_definition; const SRC: &'static str = r#"vec2 main() { float n = 0.; float p = 0.; float u = vec2(0., 0.); if (n-p>0.&&u.yp) { } return u; } "#; // Ideally we would use SRC as the expected, but there's a bug in block braces generation const DST: &'static str = r#"vec2 main() { float n = 0.; float p = 0.; float u = vec2(0., 0.); if (n-p>0.&&u.yp) { { } } return u; } "#; let mut s = String::new(); show_function_definition(&mut s, &function_definition(SRC).unwrap().1); assert_eq!(s, DST); } #[test] fn roundtrip_glsl_complex_expr() { let zero = syntax::Expr::DoubleConst(0.); let ray = syntax::Expr::Variable("ray".into()); let raydir = syntax::Expr::Dot(Box::new(ray), "dir".into()); let vec4 = syntax::Expr::FunCall( syntax::FunIdentifier::Identifier("vec4".into()), vec![raydir, zero], ); let view = syntax::Expr::Variable("view".into()); let iview = syntax::Expr::FunCall( syntax::FunIdentifier::Identifier("inverse".into()), vec![view], ); let mul = syntax::Expr::Binary(syntax::BinaryOp::Mult, Box::new(iview), Box::new(vec4)); let xyz = syntax::Expr::Dot(Box::new(mul), "xyz".into()); let input = syntax::Expr::FunCall( syntax::FunIdentifier::Identifier("normalize".into()), vec![xyz], ); let mut output = String::new(); show_expr(&mut output, &input); let _ = output.write_str(";"); let back = expr(&output); assert_eq!(back, Ok((";", input)), "intermediate source '{}'", output); } }