use crate::parsers::*; use crate::syntax; #[test] fn parse_uniline_comment() { assert_eq!(comment("// lol"), Ok(("", " lol"))); assert_eq!(comment("// lol\nfoo"), Ok(("foo", " lol"))); assert_eq!(comment("// lol\\\nfoo"), Ok(("", " lol\\\nfoo"))); assert_eq!( comment("// lol \\\n foo\n"), Ok(("", " lol \\\n foo")) ); } #[test] fn parse_multiline_comment() { assert_eq!(comment("/* lol\nfoo\n*/bar"), Ok(("bar", " lol\nfoo\n"))); } #[test] fn parse_unsigned_suffix() { assert_eq!(unsigned_suffix("u"), Ok(("", 'u'))); assert_eq!(unsigned_suffix("U"), Ok(("", 'U'))); } #[test] fn parse_nonzero_digits() { assert_eq!(nonzero_digits("3"), Ok(("", "3"))); assert_eq!(nonzero_digits("12345953"), Ok(("", "12345953"))); } #[test] fn parse_decimal_lit() { assert_eq!(decimal_lit("3"), Ok(("", Ok(3)))); assert_eq!(decimal_lit("3"), Ok(("", Ok(3)))); assert_eq!(decimal_lit("13"), Ok(("", Ok(13)))); assert_eq!(decimal_lit("42"), Ok(("", Ok(42)))); assert_eq!(decimal_lit("123456"), Ok(("", Ok(123456)))); } #[test] fn parse_octal_lit() { assert_eq!(octal_lit("0"), Ok(("", Ok(0o0)))); assert_eq!(octal_lit("03 "), Ok((" ", Ok(0o3)))); assert_eq!(octal_lit("012 "), Ok((" ", Ok(0o12)))); assert_eq!(octal_lit("07654321 "), Ok((" ", Ok(0o7654321)))); } #[test] fn parse_hexadecimal_lit() { assert_eq!(hexadecimal_lit("0x3 "), Ok((" ", Ok(0x3)))); assert_eq!(hexadecimal_lit("0x0123789"), Ok(("", Ok(0x0123789)))); assert_eq!(hexadecimal_lit("0xABCDEF"), Ok(("", Ok(0xabcdef)))); assert_eq!(hexadecimal_lit("0xabcdef"), Ok(("", Ok(0xabcdef)))); } #[test] fn parse_integral_lit() { assert_eq!(integral_lit("0"), Ok(("", 0))); assert_eq!(integral_lit("3"), Ok(("", 3))); assert_eq!(integral_lit("3 "), Ok((" ", 3))); assert_eq!(integral_lit("03 "), Ok((" ", 3))); assert_eq!(integral_lit("076556 "), Ok((" ", 0o76556))); assert_eq!(integral_lit("012 "), Ok((" ", 0o12))); assert_eq!(integral_lit("0x3 "), Ok((" ", 0x3))); assert_eq!(integral_lit("0x9ABCDEF"), Ok(("", 0x9ABCDEF))); assert_eq!(integral_lit("0x9ABCDEF"), Ok(("", 0x9ABCDEF))); assert_eq!(integral_lit("0x9abcdef"), Ok(("", 0x9abcdef))); assert_eq!(integral_lit("0x9abcdef"), Ok(("", 0x9abcdef))); assert_eq!(integral_lit("0xffffffff"), Ok(("", 0xffffffffu32 as i32))); } #[test] fn parse_integral_neg_lit() { assert_eq!(integral_lit("-3"), Ok(("", -3))); assert_eq!(integral_lit("-3 "), Ok((" ", -3))); assert_eq!(integral_lit("-03 "), Ok((" ", -3))); assert_eq!(integral_lit("-076556 "), Ok((" ", -0o76556))); assert_eq!(integral_lit("-012 "), Ok((" ", -0o12))); assert_eq!(integral_lit("-0x3 "), Ok((" ", -0x3))); assert_eq!(integral_lit("-0x9ABCDEF"), Ok(("", -0x9ABCDEF))); assert_eq!(integral_lit("-0x9ABCDEF"), Ok(("", -0x9ABCDEF))); assert_eq!(integral_lit("-0x9abcdef"), Ok(("", -0x9abcdef))); assert_eq!(integral_lit("-0x9abcdef"), Ok(("", -0x9abcdef))); } #[test] fn parse_unsigned_lit() { assert_eq!(unsigned_lit("0xffffffffU"), Ok(("", 0xffffffff as u32))); assert_eq!(unsigned_lit("-1u"), Ok(("", 0xffffffff as u32))); assert!(unsigned_lit("0xfffffffffU").is_err()); } #[test] fn parse_float_lit() { assert_eq!(float_lit("0.;"), Ok((";", 0.))); assert_eq!(float_lit(".0;"), Ok((";", 0.))); assert_eq!(float_lit(".035 "), Ok((" ", 0.035))); assert_eq!(float_lit("0. "), Ok((" ", 0.))); assert_eq!(float_lit("0.035 "), Ok((" ", 0.035))); assert_eq!(float_lit(".035f"), Ok(("", 0.035))); assert_eq!(float_lit("0.f"), Ok(("", 0.))); assert_eq!(float_lit("314.f"), Ok(("", 314.))); assert_eq!(float_lit("0.035f"), Ok(("", 0.035))); assert_eq!(float_lit(".035F"), Ok(("", 0.035))); assert_eq!(float_lit("0.F"), Ok(("", 0.))); assert_eq!(float_lit("0.035F"), Ok(("", 0.035))); assert_eq!(float_lit("1.03e+34 "), Ok((" ", 1.03e+34))); assert_eq!(float_lit("1.03E+34 "), Ok((" ", 1.03E+34))); assert_eq!(float_lit("1.03e-34 "), Ok((" ", 1.03e-34))); assert_eq!(float_lit("1.03E-34 "), Ok((" ", 1.03E-34))); assert_eq!(float_lit("1.03e+34f"), Ok(("", 1.03e+34))); assert_eq!(float_lit("1.03E+34f"), Ok(("", 1.03E+34))); assert_eq!(float_lit("1.03e-34f"), Ok(("", 1.03e-34))); assert_eq!(float_lit("1.03E-34f"), Ok(("", 1.03E-34))); assert_eq!(float_lit("1.03e+34F"), Ok(("", 1.03e+34))); assert_eq!(float_lit("1.03E+34F"), Ok(("", 1.03E+34))); assert_eq!(float_lit("1.03e-34F"), Ok(("", 1.03e-34))); assert_eq!(float_lit("1.03E-34F"), Ok(("", 1.03E-34))); } #[test] fn parse_float_neg_lit() { assert_eq!(float_lit("-.035 "), Ok((" ", -0.035))); assert_eq!(float_lit("-0. "), Ok((" ", -0.))); assert_eq!(float_lit("-0.035 "), Ok((" ", -0.035))); assert_eq!(float_lit("-.035f"), Ok(("", -0.035))); assert_eq!(float_lit("-0.f"), Ok(("", -0.))); assert_eq!(float_lit("-0.035f"), Ok(("", -0.035))); assert_eq!(float_lit("-.035F"), Ok(("", -0.035))); assert_eq!(float_lit("-0.F"), Ok(("", -0.))); assert_eq!(float_lit("-0.035F"), Ok(("", -0.035))); assert_eq!(float_lit("-1.03e+34 "), Ok((" ", -1.03e+34))); assert_eq!(float_lit("-1.03E+34 "), Ok((" ", -1.03E+34))); assert_eq!(float_lit("-1.03e-34 "), Ok((" ", -1.03e-34))); assert_eq!(float_lit("-1.03E-34 "), Ok((" ", -1.03E-34))); assert_eq!(float_lit("-1.03e+34f"), Ok(("", -1.03e+34))); assert_eq!(float_lit("-1.03E+34f"), Ok(("", -1.03E+34))); assert_eq!(float_lit("-1.03e-34f"), Ok(("", -1.03e-34))); assert_eq!(float_lit("-1.03E-34f"), Ok(("", -1.03E-34))); assert_eq!(float_lit("-1.03e+34F"), Ok(("", -1.03e+34))); assert_eq!(float_lit("-1.03E+34F"), Ok(("", -1.03E+34))); assert_eq!(float_lit("-1.03e-34F"), Ok(("", -1.03e-34))); assert_eq!(float_lit("-1.03E-34F"), Ok(("", -1.03E-34))); } #[test] fn parse_double_lit() { assert_eq!(double_lit("0.;"), Ok((";", 0.))); assert_eq!(double_lit(".0;"), Ok((";", 0.))); assert_eq!(double_lit(".035 "), Ok((" ", 0.035))); assert_eq!(double_lit("0. "), Ok((" ", 0.))); assert_eq!(double_lit("0.035 "), Ok((" ", 0.035))); assert_eq!(double_lit("0.lf"), Ok(("", 0.))); assert_eq!(double_lit("0.035lf"), Ok(("", 0.035))); assert_eq!(double_lit(".035lf"), Ok(("", 0.035))); assert_eq!(double_lit(".035LF"), Ok(("", 0.035))); assert_eq!(double_lit("0.LF"), Ok(("", 0.))); assert_eq!(double_lit("0.035LF"), Ok(("", 0.035))); assert_eq!(double_lit("1.03e+34lf"), Ok(("", 1.03e+34))); assert_eq!(double_lit("1.03E+34lf"), Ok(("", 1.03E+34))); assert_eq!(double_lit("1.03e-34lf"), Ok(("", 1.03e-34))); assert_eq!(double_lit("1.03E-34lf"), Ok(("", 1.03E-34))); assert_eq!(double_lit("1.03e+34LF"), Ok(("", 1.03e+34))); assert_eq!(double_lit("1.03E+34LF"), Ok(("", 1.03E+34))); assert_eq!(double_lit("1.03e-34LF"), Ok(("", 1.03e-34))); assert_eq!(double_lit("1.03E-34LF"), Ok(("", 1.03E-34))); } #[test] fn parse_double_neg_lit() { assert_eq!(double_lit("-0.;"), Ok((";", -0.))); assert_eq!(double_lit("-.0;"), Ok((";", -0.))); assert_eq!(double_lit("-.035 "), Ok((" ", -0.035))); assert_eq!(double_lit("-0. "), Ok((" ", -0.))); assert_eq!(double_lit("-0.035 "), Ok((" ", -0.035))); assert_eq!(double_lit("-0.lf"), Ok(("", -0.))); assert_eq!(double_lit("-0.035lf"), Ok(("", -0.035))); assert_eq!(double_lit("-.035lf"), Ok(("", -0.035))); assert_eq!(double_lit("-.035LF"), Ok(("", -0.035))); assert_eq!(double_lit("-0.LF"), Ok(("", -0.))); assert_eq!(double_lit("-0.035LF"), Ok(("", -0.035))); assert_eq!(double_lit("-1.03e+34lf"), Ok(("", -1.03e+34))); assert_eq!(double_lit("-1.03E+34lf"), Ok(("", -1.03E+34))); assert_eq!(double_lit("-1.03e-34lf"), Ok(("", -1.03e-34))); assert_eq!(double_lit("-1.03E-34lf"), Ok(("", -1.03E-34))); assert_eq!(double_lit("-1.03e+34LF"), Ok(("", -1.03e+34))); assert_eq!(double_lit("-1.03E+34LF"), Ok(("", -1.03E+34))); assert_eq!(double_lit("-1.03e-34LF"), Ok(("", -1.03e-34))); assert_eq!(double_lit("-1.03E-34LF"), Ok(("", -1.03E-34))); } #[test] fn parse_bool_lit() { assert_eq!(bool_lit("false"), Ok(("", false))); assert_eq!(bool_lit("true"), Ok(("", true))); } #[test] fn parse_identifier() { assert_eq!(identifier("a"), Ok(("", "a".into()))); assert_eq!(identifier("ab_cd"), Ok(("", "ab_cd".into()))); assert_eq!(identifier("Ab_cd"), Ok(("", "Ab_cd".into()))); assert_eq!(identifier("Ab_c8d"), Ok(("", "Ab_c8d".into()))); assert_eq!(identifier("Ab_c8d9"), Ok(("", "Ab_c8d9".into()))); } #[test] fn parse_unary_op_add() { assert_eq!(unary_op("+ "), Ok((" ", syntax::UnaryOp::Add))); } #[test] fn parse_unary_op_minus() { assert_eq!(unary_op("- "), Ok((" ", syntax::UnaryOp::Minus))); } #[test] fn parse_unary_op_not() { assert_eq!(unary_op("!"), Ok(("", syntax::UnaryOp::Not))); } #[test] fn parse_unary_op_complement() { assert_eq!(unary_op("~"), Ok(("", syntax::UnaryOp::Complement))); } #[test] fn parse_unary_op_inc() { assert_eq!(unary_op("++"), Ok(("", syntax::UnaryOp::Inc))); } #[test] fn parse_unary_op_dec() { assert_eq!(unary_op("--"), Ok(("", syntax::UnaryOp::Dec))); } #[test] fn parse_array_specifier_dimension_unsized() { assert_eq!( array_specifier_dimension("[]"), Ok(("", syntax::ArraySpecifierDimension::Unsized)) ); assert_eq!( array_specifier_dimension("[ ]"), Ok(("", syntax::ArraySpecifierDimension::Unsized)) ); assert_eq!( array_specifier_dimension("[\n]"), Ok(("", syntax::ArraySpecifierDimension::Unsized)) ); } #[test] fn parse_array_specifier_dimension_sized() { let ix = syntax::Expr::IntConst(0); assert_eq!( array_specifier_dimension("[0]"), Ok(( "", syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(ix.clone())) )) ); assert_eq!( array_specifier_dimension("[\n0 \t]"), Ok(( "", syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(ix)) )) ); } #[test] fn parse_array_specifier_unsized() { assert_eq!( array_specifier("[]"), Ok(( "", syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]) } )) ) } #[test] fn parse_array_specifier_sized() { let ix = syntax::Expr::IntConst(123); assert_eq!( array_specifier("[123]"), Ok(( "", syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( Box::new(ix) )]) } )) ) } #[test] fn parse_array_specifier_sized_multiple() { let a = syntax::Expr::IntConst(2); let b = syntax::Expr::IntConst(100); let d = syntax::Expr::IntConst(5); assert_eq!( array_specifier("[2][100][][5]"), Ok(( "", syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![ syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(a)), syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(b)), syntax::ArraySpecifierDimension::Unsized, syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(d)), ]) } )) ) } #[test] fn parse_precise_qualifier() { assert_eq!(precise_qualifier("precise "), Ok((" ", ()))); } #[test] fn parse_invariant_qualifier() { assert_eq!(invariant_qualifier("invariant "), Ok((" ", ()))); } #[test] fn parse_interpolation_qualifier() { assert_eq!( interpolation_qualifier("smooth "), Ok((" ", syntax::InterpolationQualifier::Smooth)) ); assert_eq!( interpolation_qualifier("flat "), Ok((" ", syntax::InterpolationQualifier::Flat)) ); assert_eq!( interpolation_qualifier("noperspective "), Ok((" ", syntax::InterpolationQualifier::NoPerspective)) ); } #[test] fn parse_precision_qualifier() { assert_eq!( precision_qualifier("highp "), Ok((" ", syntax::PrecisionQualifier::High)) ); assert_eq!( precision_qualifier("mediump "), Ok((" ", syntax::PrecisionQualifier::Medium)) ); assert_eq!( precision_qualifier("lowp "), Ok((" ", syntax::PrecisionQualifier::Low)) ); } #[test] fn parse_storage_qualifier() { assert_eq!( storage_qualifier("const "), Ok((" ", syntax::StorageQualifier::Const)) ); assert_eq!( storage_qualifier("inout "), Ok((" ", syntax::StorageQualifier::InOut)) ); assert_eq!( storage_qualifier("in "), Ok((" ", syntax::StorageQualifier::In)) ); assert_eq!( storage_qualifier("out "), Ok((" ", syntax::StorageQualifier::Out)) ); assert_eq!( storage_qualifier("centroid "), Ok((" ", syntax::StorageQualifier::Centroid)) ); assert_eq!( storage_qualifier("patch "), Ok((" ", syntax::StorageQualifier::Patch)) ); assert_eq!( storage_qualifier("sample "), Ok((" ", syntax::StorageQualifier::Sample)) ); assert_eq!( storage_qualifier("uniform "), Ok((" ", syntax::StorageQualifier::Uniform)) ); assert_eq!( storage_qualifier("attribute "), Ok((" ", syntax::StorageQualifier::Attribute)) ); assert_eq!( storage_qualifier("varying "), Ok((" ", syntax::StorageQualifier::Varying)) ); assert_eq!( storage_qualifier("buffer "), Ok((" ", syntax::StorageQualifier::Buffer)) ); assert_eq!( storage_qualifier("shared "), Ok((" ", syntax::StorageQualifier::Shared)) ); assert_eq!( storage_qualifier("coherent "), Ok((" ", syntax::StorageQualifier::Coherent)) ); assert_eq!( storage_qualifier("volatile "), Ok((" ", syntax::StorageQualifier::Volatile)) ); assert_eq!( storage_qualifier("restrict "), Ok((" ", syntax::StorageQualifier::Restrict)) ); assert_eq!( storage_qualifier("readonly "), Ok((" ", syntax::StorageQualifier::ReadOnly)) ); assert_eq!( storage_qualifier("writeonly "), Ok((" ", syntax::StorageQualifier::WriteOnly)) ); assert_eq!( storage_qualifier("subroutine a"), Ok((" a", syntax::StorageQualifier::Subroutine(vec![]))) ); let a = syntax::TypeName("vec3".to_owned()); let b = syntax::TypeName("float".to_owned()); let c = syntax::TypeName("dmat43".to_owned()); let types = vec![a, b, c]; assert_eq!( storage_qualifier("subroutine ( vec3 , float \\\n, dmat43)"), Ok(("", syntax::StorageQualifier::Subroutine(types))) ); } #[test] fn parse_layout_qualifier_std430() { let expected = syntax::LayoutQualifier { ids: syntax::NonEmpty(vec![syntax::LayoutQualifierSpec::Identifier( "std430".into(), None, )]), }; assert_eq!( layout_qualifier("layout (std430)"), Ok(("", expected.clone())) ); assert_eq!( layout_qualifier("layout (std430 )"), Ok(("", expected.clone())) ); assert_eq!( layout_qualifier("layout \n\t ( std430 )"), Ok(("", expected.clone())) ); assert_eq!(layout_qualifier("layout(std430)"), Ok(("", expected))); } #[test] fn parse_layout_qualifier_shared() { let expected = syntax::LayoutQualifier { ids: syntax::NonEmpty(vec![syntax::LayoutQualifierSpec::Shared]), }; assert_eq!( layout_qualifier("layout (shared)"), Ok(("", expected.clone())) ); assert_eq!( layout_qualifier("layout ( shared )"), Ok(("", expected.clone())) ); assert_eq!(layout_qualifier("layout(shared)"), Ok(("", expected))); } #[test] fn parse_layout_qualifier_list() { let id_0 = syntax::LayoutQualifierSpec::Shared; let id_1 = syntax::LayoutQualifierSpec::Identifier("std140".into(), None); let id_2 = syntax::LayoutQualifierSpec::Identifier( "max_vertices".into(), Some(Box::new(syntax::Expr::IntConst(3))), ); let expected = syntax::LayoutQualifier { ids: syntax::NonEmpty(vec![id_0, id_1, id_2]), }; assert_eq!( layout_qualifier("layout (shared, std140, max_vertices = 3)"), Ok(("", expected.clone())) ); assert_eq!( layout_qualifier("layout(shared,std140,max_vertices=3)"), Ok(("", expected.clone())) ); assert_eq!( layout_qualifier("layout\n\n\t ( shared , std140, max_vertices= 3)"), Ok(("", expected.clone())) ); } #[test] fn parse_type_qualifier() { let storage_qual = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Const); let id_0 = syntax::LayoutQualifierSpec::Shared; let id_1 = syntax::LayoutQualifierSpec::Identifier("std140".into(), None); let id_2 = syntax::LayoutQualifierSpec::Identifier( "max_vertices".into(), Some(Box::new(syntax::Expr::IntConst(3))), ); let layout_qual = syntax::TypeQualifierSpec::Layout(syntax::LayoutQualifier { ids: syntax::NonEmpty(vec![id_0, id_1, id_2]), }); let expected = syntax::TypeQualifier { qualifiers: syntax::NonEmpty(vec![storage_qual, layout_qual]), }; assert_eq!( type_qualifier("const layout (shared, std140, max_vertices = 3)"), Ok(("", expected.clone())) ); assert_eq!( type_qualifier("const layout(shared,std140,max_vertices=3)"), Ok(("", expected)) ); } #[test] fn parse_struct_field_specifier() { let expected = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec4, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["foo".into()]), }; assert_eq!( struct_field_specifier("vec4 foo;"), Ok(("", expected.clone())) ); assert_eq!( struct_field_specifier("vec4 foo ; "), Ok((" ", expected.clone())) ); } #[test] fn parse_struct_field_specifier_type_name() { let expected = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::TypeName("S0238_3".into()), array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["x".into()]), }; assert_eq!( struct_field_specifier("S0238_3 x;"), Ok(("", expected.clone())) ); assert_eq!( struct_field_specifier("S0238_3 x ;"), Ok(("", expected.clone())) ); } #[test] fn parse_struct_field_specifier_several() { let expected = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec4, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["foo".into(), "bar".into(), "zoo".into()]), }; assert_eq!( struct_field_specifier("vec4 foo, bar, zoo;"), Ok(("", expected.clone())) ); assert_eq!( struct_field_specifier("vec4 foo , bar , zoo ;"), Ok(("", expected.clone())) ); } #[test] fn parse_struct_specifier_one_field() { let field = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec4, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["foo".into()]), }; let expected = syntax::StructSpecifier { name: Some("TestStruct".into()), fields: syntax::NonEmpty(vec![field]), }; assert_eq!( struct_specifier("struct TestStruct { vec4 foo; }"), Ok(("", expected.clone())) ); assert_eq!( struct_specifier("struct TestStruct \n \n\n {\n vec4 foo ;}"), Ok(("", expected)) ); } #[test] fn parse_struct_specifier_multi_fields() { let a = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec4, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["foo".into()]), }; let b = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["bar".into()]), }; let c = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::UInt, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["zoo".into()]), }; let d = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::BVec3, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["foo_BAR_zoo3497_34".into()]), }; let e = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::TypeName("S0238_3".into()), array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["x".into()]), }; let expected = syntax::StructSpecifier { name: Some("_TestStruct_934i".into()), fields: syntax::NonEmpty(vec![a, b, c, d, e]), }; assert_eq!( struct_specifier( "struct _TestStruct_934i { vec4 foo; float bar; uint zoo; bvec3 foo_BAR_zoo3497_34; S0238_3 x; }" ), Ok(("", expected.clone())) ); assert_eq!( struct_specifier( "struct _TestStruct_934i{vec4 foo;float bar;uint zoo;bvec3 foo_BAR_zoo3497_34;S0238_3 x;}" ), Ok(("", expected.clone())) ); assert_eq!(struct_specifier("struct _TestStruct_934i\n { vec4\nfoo ; \n\t float\n\t\t bar ; \nuint zoo; \n bvec3 foo_BAR_zoo3497_34\n\n\t\n\t\n ; S0238_3 x;}"), Ok(("", expected))); } #[test] fn parse_type_specifier_non_array() { assert_eq!( type_specifier_non_array("bool"), Ok(("", syntax::TypeSpecifierNonArray::Bool)) ); assert_eq!( type_specifier_non_array("int"), Ok(("", syntax::TypeSpecifierNonArray::Int)) ); assert_eq!( type_specifier_non_array("uint"), Ok(("", syntax::TypeSpecifierNonArray::UInt)) ); assert_eq!( type_specifier_non_array("float"), Ok(("", syntax::TypeSpecifierNonArray::Float)) ); assert_eq!( type_specifier_non_array("double"), Ok(("", syntax::TypeSpecifierNonArray::Double)) ); assert_eq!( type_specifier_non_array("vec2"), Ok(("", syntax::TypeSpecifierNonArray::Vec2)) ); assert_eq!( type_specifier_non_array("vec3"), Ok(("", syntax::TypeSpecifierNonArray::Vec3)) ); assert_eq!( type_specifier_non_array("vec4"), Ok(("", syntax::TypeSpecifierNonArray::Vec4)) ); assert_eq!( type_specifier_non_array("dvec2"), Ok(("", syntax::TypeSpecifierNonArray::DVec2)) ); assert_eq!( type_specifier_non_array("dvec3"), Ok(("", syntax::TypeSpecifierNonArray::DVec3)) ); assert_eq!( type_specifier_non_array("dvec4"), Ok(("", syntax::TypeSpecifierNonArray::DVec4)) ); assert_eq!( type_specifier_non_array("bvec2"), Ok(("", syntax::TypeSpecifierNonArray::BVec2)) ); assert_eq!( type_specifier_non_array("bvec3"), Ok(("", syntax::TypeSpecifierNonArray::BVec3)) ); assert_eq!( type_specifier_non_array("bvec4"), Ok(("", syntax::TypeSpecifierNonArray::BVec4)) ); assert_eq!( type_specifier_non_array("ivec2"), Ok(("", syntax::TypeSpecifierNonArray::IVec2)) ); assert_eq!( type_specifier_non_array("ivec3"), Ok(("", syntax::TypeSpecifierNonArray::IVec3)) ); assert_eq!( type_specifier_non_array("ivec4"), Ok(("", syntax::TypeSpecifierNonArray::IVec4)) ); assert_eq!( type_specifier_non_array("uvec2"), Ok(("", syntax::TypeSpecifierNonArray::UVec2)) ); assert_eq!( type_specifier_non_array("uvec3"), Ok(("", syntax::TypeSpecifierNonArray::UVec3)) ); assert_eq!( type_specifier_non_array("uvec4"), Ok(("", syntax::TypeSpecifierNonArray::UVec4)) ); assert_eq!( type_specifier_non_array("mat2"), Ok(("", syntax::TypeSpecifierNonArray::Mat2)) ); assert_eq!( type_specifier_non_array("mat3"), Ok(("", syntax::TypeSpecifierNonArray::Mat3)) ); assert_eq!( type_specifier_non_array("mat4"), Ok(("", syntax::TypeSpecifierNonArray::Mat4)) ); assert_eq!( type_specifier_non_array("mat2x2"), Ok(("", syntax::TypeSpecifierNonArray::Mat2)) ); assert_eq!( type_specifier_non_array("mat2x3"), Ok(("", syntax::TypeSpecifierNonArray::Mat23)) ); assert_eq!( type_specifier_non_array("mat2x4"), Ok(("", syntax::TypeSpecifierNonArray::Mat24)) ); assert_eq!( type_specifier_non_array("mat3x2"), Ok(("", syntax::TypeSpecifierNonArray::Mat32)) ); assert_eq!( type_specifier_non_array("mat3x3"), Ok(("", syntax::TypeSpecifierNonArray::Mat3)) ); assert_eq!( type_specifier_non_array("mat3x4"), Ok(("", syntax::TypeSpecifierNonArray::Mat34)) ); assert_eq!( type_specifier_non_array("mat4x2"), Ok(("", syntax::TypeSpecifierNonArray::Mat42)) ); assert_eq!( type_specifier_non_array("mat4x3"), Ok(("", syntax::TypeSpecifierNonArray::Mat43)) ); assert_eq!( type_specifier_non_array("mat4x4"), Ok(("", syntax::TypeSpecifierNonArray::Mat4)) ); assert_eq!( type_specifier_non_array("dmat2"), Ok(("", syntax::TypeSpecifierNonArray::DMat2)) ); assert_eq!( type_specifier_non_array("dmat3"), Ok(("", syntax::TypeSpecifierNonArray::DMat3)) ); assert_eq!( type_specifier_non_array("dmat4"), Ok(("", syntax::TypeSpecifierNonArray::DMat4)) ); assert_eq!( type_specifier_non_array("dmat2x2"), Ok(("", syntax::TypeSpecifierNonArray::DMat2)) ); assert_eq!( type_specifier_non_array("dmat2x3"), Ok(("", syntax::TypeSpecifierNonArray::DMat23)) ); assert_eq!( type_specifier_non_array("dmat2x4"), Ok(("", syntax::TypeSpecifierNonArray::DMat24)) ); assert_eq!( type_specifier_non_array("dmat3x2"), Ok(("", syntax::TypeSpecifierNonArray::DMat32)) ); assert_eq!( type_specifier_non_array("dmat3x3"), Ok(("", syntax::TypeSpecifierNonArray::DMat3)) ); assert_eq!( type_specifier_non_array("dmat3x4"), Ok(("", syntax::TypeSpecifierNonArray::DMat34)) ); assert_eq!( type_specifier_non_array("dmat4x2"), Ok(("", syntax::TypeSpecifierNonArray::DMat42)) ); assert_eq!( type_specifier_non_array("dmat4x3"), Ok(("", syntax::TypeSpecifierNonArray::DMat43)) ); assert_eq!( type_specifier_non_array("dmat4x4"), Ok(("", syntax::TypeSpecifierNonArray::DMat4)) ); assert_eq!( type_specifier_non_array("sampler1D"), Ok(("", syntax::TypeSpecifierNonArray::Sampler1D)) ); assert_eq!( type_specifier_non_array("image1D"), Ok(("", syntax::TypeSpecifierNonArray::Image1D)) ); assert_eq!( type_specifier_non_array("sampler2D"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2D)) ); assert_eq!( type_specifier_non_array("image2D"), Ok(("", syntax::TypeSpecifierNonArray::Image2D)) ); assert_eq!( type_specifier_non_array("sampler3D"), Ok(("", syntax::TypeSpecifierNonArray::Sampler3D)) ); assert_eq!( type_specifier_non_array("image3D"), Ok(("", syntax::TypeSpecifierNonArray::Image3D)) ); assert_eq!( type_specifier_non_array("samplerCube"), Ok(("", syntax::TypeSpecifierNonArray::SamplerCube)) ); assert_eq!( type_specifier_non_array("imageCube"), Ok(("", syntax::TypeSpecifierNonArray::ImageCube)) ); assert_eq!( type_specifier_non_array("sampler2DRect"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2DRect)) ); assert_eq!( type_specifier_non_array("image2DRect"), Ok(("", syntax::TypeSpecifierNonArray::Image2DRect)) ); assert_eq!( type_specifier_non_array("sampler1DArray"), Ok(("", syntax::TypeSpecifierNonArray::Sampler1DArray)) ); assert_eq!( type_specifier_non_array("image1DArray"), Ok(("", syntax::TypeSpecifierNonArray::Image1DArray)) ); assert_eq!( type_specifier_non_array("sampler2DArray"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2DArray)) ); assert_eq!( type_specifier_non_array("image2DArray"), Ok(("", syntax::TypeSpecifierNonArray::Image2DArray)) ); assert_eq!( type_specifier_non_array("samplerBuffer"), Ok(("", syntax::TypeSpecifierNonArray::SamplerBuffer)) ); assert_eq!( type_specifier_non_array("imageBuffer"), Ok(("", syntax::TypeSpecifierNonArray::ImageBuffer)) ); assert_eq!( type_specifier_non_array("sampler2DMS"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2DMS)) ); assert_eq!( type_specifier_non_array("image2DMS"), Ok(("", syntax::TypeSpecifierNonArray::Image2DMS)) ); assert_eq!( type_specifier_non_array("sampler2DMSArray"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2DMSArray)) ); assert_eq!( type_specifier_non_array("image2DMSArray"), Ok(("", syntax::TypeSpecifierNonArray::Image2DMSArray)) ); assert_eq!( type_specifier_non_array("samplerCubeArray"), Ok(("", syntax::TypeSpecifierNonArray::SamplerCubeArray)) ); assert_eq!( type_specifier_non_array("imageCubeArray"), Ok(("", syntax::TypeSpecifierNonArray::ImageCubeArray)) ); assert_eq!( type_specifier_non_array("sampler1DShadow"), Ok(("", syntax::TypeSpecifierNonArray::Sampler1DShadow)) ); assert_eq!( type_specifier_non_array("sampler2DShadow"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2DShadow)) ); assert_eq!( type_specifier_non_array("sampler2DRectShadow"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2DRectShadow)) ); assert_eq!( type_specifier_non_array("sampler1DArrayShadow"), Ok(("", syntax::TypeSpecifierNonArray::Sampler1DArrayShadow)) ); assert_eq!( type_specifier_non_array("sampler2DArrayShadow"), Ok(("", syntax::TypeSpecifierNonArray::Sampler2DArrayShadow)) ); assert_eq!( type_specifier_non_array("samplerCubeShadow"), Ok(("", syntax::TypeSpecifierNonArray::SamplerCubeShadow)) ); assert_eq!( type_specifier_non_array("samplerCubeArrayShadow"), Ok(("", syntax::TypeSpecifierNonArray::SamplerCubeArrayShadow)) ); assert_eq!( type_specifier_non_array("isampler1D"), Ok(("", syntax::TypeSpecifierNonArray::ISampler1D)) ); assert_eq!( type_specifier_non_array("iimage1D"), Ok(("", syntax::TypeSpecifierNonArray::IImage1D)) ); assert_eq!( type_specifier_non_array("isampler2D"), Ok(("", syntax::TypeSpecifierNonArray::ISampler2D)) ); assert_eq!( type_specifier_non_array("iimage2D"), Ok(("", syntax::TypeSpecifierNonArray::IImage2D)) ); assert_eq!( type_specifier_non_array("isampler3D"), Ok(("", syntax::TypeSpecifierNonArray::ISampler3D)) ); assert_eq!( type_specifier_non_array("iimage3D"), Ok(("", syntax::TypeSpecifierNonArray::IImage3D)) ); assert_eq!( type_specifier_non_array("isamplerCube"), Ok(("", syntax::TypeSpecifierNonArray::ISamplerCube)) ); assert_eq!( type_specifier_non_array("iimageCube"), Ok(("", syntax::TypeSpecifierNonArray::IImageCube)) ); assert_eq!( type_specifier_non_array("isampler2DRect"), Ok(("", syntax::TypeSpecifierNonArray::ISampler2DRect)) ); assert_eq!( type_specifier_non_array("iimage2DRect"), Ok(("", syntax::TypeSpecifierNonArray::IImage2DRect)) ); assert_eq!( type_specifier_non_array("isampler1DArray"), Ok(("", syntax::TypeSpecifierNonArray::ISampler1DArray)) ); assert_eq!( type_specifier_non_array("iimage1DArray"), Ok(("", syntax::TypeSpecifierNonArray::IImage1DArray)) ); assert_eq!( type_specifier_non_array("isampler2DArray"), Ok(("", syntax::TypeSpecifierNonArray::ISampler2DArray)) ); assert_eq!( type_specifier_non_array("iimage2DArray"), Ok(("", syntax::TypeSpecifierNonArray::IImage2DArray)) ); assert_eq!( type_specifier_non_array("isamplerBuffer"), Ok(("", syntax::TypeSpecifierNonArray::ISamplerBuffer)) ); assert_eq!( type_specifier_non_array("iimageBuffer"), Ok(("", syntax::TypeSpecifierNonArray::IImageBuffer)) ); assert_eq!( type_specifier_non_array("isampler2DMS"), Ok(("", syntax::TypeSpecifierNonArray::ISampler2DMS)) ); assert_eq!( type_specifier_non_array("iimage2DMS"), Ok(("", syntax::TypeSpecifierNonArray::IImage2DMS)) ); assert_eq!( type_specifier_non_array("isampler2DMSArray"), Ok(("", syntax::TypeSpecifierNonArray::ISampler2DMSArray)) ); assert_eq!( type_specifier_non_array("iimage2DMSArray"), Ok(("", syntax::TypeSpecifierNonArray::IImage2DMSArray)) ); assert_eq!( type_specifier_non_array("isamplerCubeArray"), Ok(("", syntax::TypeSpecifierNonArray::ISamplerCubeArray)) ); assert_eq!( type_specifier_non_array("iimageCubeArray"), Ok(("", syntax::TypeSpecifierNonArray::IImageCubeArray)) ); assert_eq!( type_specifier_non_array("atomic_uint"), Ok(("", syntax::TypeSpecifierNonArray::AtomicUInt)) ); assert_eq!( type_specifier_non_array("usampler1D"), Ok(("", syntax::TypeSpecifierNonArray::USampler1D)) ); assert_eq!( type_specifier_non_array("uimage1D"), Ok(("", syntax::TypeSpecifierNonArray::UImage1D)) ); assert_eq!( type_specifier_non_array("usampler2D"), Ok(("", syntax::TypeSpecifierNonArray::USampler2D)) ); assert_eq!( type_specifier_non_array("uimage2D"), Ok(("", syntax::TypeSpecifierNonArray::UImage2D)) ); assert_eq!( type_specifier_non_array("usampler3D"), Ok(("", syntax::TypeSpecifierNonArray::USampler3D)) ); assert_eq!( type_specifier_non_array("uimage3D"), Ok(("", syntax::TypeSpecifierNonArray::UImage3D)) ); assert_eq!( type_specifier_non_array("usamplerCube"), Ok(("", syntax::TypeSpecifierNonArray::USamplerCube)) ); assert_eq!( type_specifier_non_array("uimageCube"), Ok(("", syntax::TypeSpecifierNonArray::UImageCube)) ); assert_eq!( type_specifier_non_array("usampler2DRect"), Ok(("", syntax::TypeSpecifierNonArray::USampler2DRect)) ); assert_eq!( type_specifier_non_array("uimage2DRect"), Ok(("", syntax::TypeSpecifierNonArray::UImage2DRect)) ); assert_eq!( type_specifier_non_array("usampler1DArray"), Ok(("", syntax::TypeSpecifierNonArray::USampler1DArray)) ); assert_eq!( type_specifier_non_array("uimage1DArray"), Ok(("", syntax::TypeSpecifierNonArray::UImage1DArray)) ); assert_eq!( type_specifier_non_array("usampler2DArray"), Ok(("", syntax::TypeSpecifierNonArray::USampler2DArray)) ); assert_eq!( type_specifier_non_array("uimage2DArray"), Ok(("", syntax::TypeSpecifierNonArray::UImage2DArray)) ); assert_eq!( type_specifier_non_array("usamplerBuffer"), Ok(("", syntax::TypeSpecifierNonArray::USamplerBuffer)) ); assert_eq!( type_specifier_non_array("uimageBuffer"), Ok(("", syntax::TypeSpecifierNonArray::UImageBuffer)) ); assert_eq!( type_specifier_non_array("usampler2DMS"), Ok(("", syntax::TypeSpecifierNonArray::USampler2DMS)) ); assert_eq!( type_specifier_non_array("uimage2DMS"), Ok(("", syntax::TypeSpecifierNonArray::UImage2DMS)) ); assert_eq!( type_specifier_non_array("usampler2DMSArray"), Ok(("", syntax::TypeSpecifierNonArray::USampler2DMSArray)) ); assert_eq!( type_specifier_non_array("uimage2DMSArray"), Ok(("", syntax::TypeSpecifierNonArray::UImage2DMSArray)) ); assert_eq!( type_specifier_non_array("usamplerCubeArray"), Ok(("", syntax::TypeSpecifierNonArray::USamplerCubeArray)) ); assert_eq!( type_specifier_non_array("uimageCubeArray"), Ok(("", syntax::TypeSpecifierNonArray::UImageCubeArray)) ); assert_eq!( type_specifier_non_array("ReturnType"), Ok(( "", syntax::TypeSpecifierNonArray::TypeName(syntax::TypeName::new("ReturnType").unwrap()) )) ); } #[test] fn parse_type_specifier() { assert_eq!( type_specifier("uint;"), Ok(( ";", syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::UInt, array_specifier: None } )) ); assert_eq!( type_specifier("iimage2DMSArray[35];"), Ok(( ";", syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::IImage2DMSArray, array_specifier: Some(syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( Box::new(syntax::Expr::IntConst(35)) )]) }) } )) ); } #[test] fn parse_fully_specified_type() { let ty = syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::IImage2DMSArray, array_specifier: None, }; let expected = syntax::FullySpecifiedType { qualifier: None, ty, }; assert_eq!( fully_specified_type("iimage2DMSArray;"), Ok((";", expected.clone())) ); } #[test] fn parse_fully_specified_type_with_qualifier() { let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Subroutine(vec![ "vec2".into(), "S032_29k".into(), ])); let qual = syntax::TypeQualifier { qualifiers: syntax::NonEmpty(vec![qual_spec]), }; let ty = syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::IImage2DMSArray, array_specifier: None, }; let expected = syntax::FullySpecifiedType { qualifier: Some(qual), ty, }; assert_eq!( fully_specified_type("subroutine (vec2, S032_29k) iimage2DMSArray;"), Ok((";", expected.clone())) ); assert_eq!( fully_specified_type("subroutine ( vec2\t\n \t , \n S032_29k )\n iimage2DMSArray ;"), Ok((" ;", expected.clone())) ); assert_eq!( fully_specified_type("subroutine(vec2,S032_29k)iimage2DMSArray;"), Ok((";", expected)) ); } #[test] fn parse_primary_expr_intconst() { assert_eq!(primary_expr("0 "), Ok((" ", syntax::Expr::IntConst(0)))); assert_eq!(primary_expr("1 "), Ok((" ", syntax::Expr::IntConst(1)))); } #[test] fn parse_primary_expr_uintconst() { assert_eq!(primary_expr("0u "), Ok((" ", syntax::Expr::UIntConst(0)))); assert_eq!(primary_expr("1u "), Ok((" ", syntax::Expr::UIntConst(1)))); } #[test] fn parse_primary_expr_floatconst() { assert_eq!( primary_expr("0.f "), Ok((" ", syntax::Expr::FloatConst(0.))) ); assert_eq!( primary_expr("1.f "), Ok((" ", syntax::Expr::FloatConst(1.))) ); assert_eq!( primary_expr("0.F "), Ok((" ", syntax::Expr::FloatConst(0.))) ); assert_eq!( primary_expr("1.F "), Ok((" ", syntax::Expr::FloatConst(1.))) ); } #[test] fn parse_primary_expr_doubleconst() { assert_eq!(primary_expr("0. "), Ok((" ", syntax::Expr::FloatConst(0.)))); assert_eq!(primary_expr("1. "), Ok((" ", syntax::Expr::FloatConst(1.)))); assert_eq!( primary_expr("0.lf "), Ok((" ", syntax::Expr::DoubleConst(0.))) ); assert_eq!( primary_expr("1.lf "), Ok((" ", syntax::Expr::DoubleConst(1.))) ); assert_eq!( primary_expr("0.LF "), Ok((" ", syntax::Expr::DoubleConst(0.))) ); assert_eq!( primary_expr("1.LF "), Ok((" ", syntax::Expr::DoubleConst(1.))) ); } #[test] fn parse_primary_expr_boolconst() { assert_eq!( primary_expr("false"), Ok(("", syntax::Expr::BoolConst(false.to_owned()))) ); assert_eq!( primary_expr("true"), Ok(("", syntax::Expr::BoolConst(true.to_owned()))) ); } #[test] fn parse_primary_expr_parens() { assert_eq!(primary_expr("(0)"), Ok(("", syntax::Expr::IntConst(0)))); assert_eq!(primary_expr("( 0 )"), Ok(("", syntax::Expr::IntConst(0)))); assert_eq!( primary_expr("( .0 )"), Ok(("", syntax::Expr::FloatConst(0.))) ); assert_eq!( primary_expr("( (.0) )"), Ok(("", syntax::Expr::FloatConst(0.))) ); assert_eq!( primary_expr("(true) "), Ok((" ", syntax::Expr::BoolConst(true))) ); } #[test] fn parse_postfix_function_call_no_args() { let fun = syntax::FunIdentifier::Identifier("vec3".into()); let args = Vec::new(); let expected = syntax::Expr::FunCall(fun, args); assert_eq!(postfix_expr("vec3();"), Ok((";", expected.clone()))); assert_eq!(postfix_expr("vec3 ( ) ;"), Ok((" ;", expected.clone()))); assert_eq!(postfix_expr("vec3 (\nvoid\n) ;"), Ok((" ;", expected))); } #[test] fn parse_postfix_function_call_one_arg() { let fun = syntax::FunIdentifier::Identifier("foo".into()); let args = vec![syntax::Expr::IntConst(0)]; let expected = syntax::Expr::FunCall(fun, args); assert_eq!(postfix_expr("foo(0);"), Ok((";", expected.clone()))); assert_eq!(postfix_expr("foo ( 0 ) ;"), Ok((" ;", expected.clone()))); assert_eq!(postfix_expr("foo (\n0\t\n) ;"), Ok((" ;", expected))); } #[test] fn parse_postfix_function_call_multi_arg() { let fun = syntax::FunIdentifier::Identifier("foo".into()); let args = vec![ syntax::Expr::IntConst(0), syntax::Expr::BoolConst(false), syntax::Expr::Variable("bar".into()), ]; let expected = syntax::Expr::FunCall(fun, args); assert_eq!( postfix_expr("foo(0, false, bar);"), Ok((";", expected.clone())) ); assert_eq!( postfix_expr("foo ( 0\t, false ,\t\tbar) ;"), Ok((" ;", expected)) ); } #[test] fn parse_postfix_expr_bracket() { let id = syntax::Expr::Variable("foo".into()); let array_spec = syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( Box::new(syntax::Expr::IntConst(7354)), )]), }; let expected = syntax::Expr::Bracket(Box::new(id), array_spec); assert_eq!(postfix_expr("foo[7354];"), Ok((";", expected.clone()))); assert_eq!(postfix_expr("foo[\n 7354 ] ;"), Ok((";", expected))); } #[test] fn parse_postfix_expr_dot() { let foo = Box::new(syntax::Expr::Variable("foo".into())); let expected = syntax::Expr::Dot(foo, "bar".into()); assert_eq!(postfix_expr("foo.bar;"), Ok((";", expected.clone()))); assert_eq!(postfix_expr("(foo).bar;"), Ok((";", expected))); } #[test] fn parse_postfix_expr_dot_several() { let foo = Box::new(syntax::Expr::Variable("foo".into())); let expected = syntax::Expr::Dot(Box::new(syntax::Expr::Dot(foo, "bar".into())), "zoo".into()); assert_eq!(postfix_expr("foo.bar.zoo;"), Ok((";", expected.clone()))); assert_eq!(postfix_expr("(foo).bar.zoo;"), Ok((";", expected.clone()))); assert_eq!(postfix_expr("(foo.bar).zoo;"), Ok((";", expected))); } #[test] fn parse_postfix_postinc() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::PostInc(Box::new(foo)); assert_eq!(postfix_expr("foo++;"), Ok((";", expected.clone()))); } #[test] fn parse_postfix_postdec() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::PostDec(Box::new(foo)); assert_eq!(postfix_expr("foo--;"), Ok((";", expected.clone()))); } #[test] fn parse_unary_add() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::Unary(syntax::UnaryOp::Add, Box::new(foo)); assert_eq!(unary_expr("+foo;"), Ok((";", expected.clone()))); } #[test] fn parse_unary_minus() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::Unary(syntax::UnaryOp::Minus, Box::new(foo)); assert_eq!(unary_expr("-foo;"), Ok((";", expected.clone()))); } #[test] fn parse_unary_not() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::Unary(syntax::UnaryOp::Not, Box::new(foo)); assert_eq!(unary_expr("!foo;"), Ok((";", expected))); } #[test] fn parse_unary_complement() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::Unary(syntax::UnaryOp::Complement, Box::new(foo)); assert_eq!(unary_expr("~foo;"), Ok((";", expected.clone()))); } #[test] fn parse_unary_inc() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::Unary(syntax::UnaryOp::Inc, Box::new(foo)); assert_eq!(unary_expr("++foo;"), Ok((";", expected.clone()))); } #[test] fn parse_unary_dec() { let foo = syntax::Expr::Variable("foo".into()); let expected = syntax::Expr::Unary(syntax::UnaryOp::Dec, Box::new(foo)); assert_eq!(unary_expr("--foo;"), Ok((";", expected.clone()))); } #[test] fn parse_expr_float() { assert_eq!(expr("314.;"), Ok((";", syntax::Expr::FloatConst(314.)))); assert_eq!(expr("314.f;"), Ok((";", syntax::Expr::FloatConst(314.)))); assert_eq!(expr("314.LF;"), Ok((";", syntax::Expr::DoubleConst(314.)))); } #[test] fn parse_expr_add_2() { let one = Box::new(syntax::Expr::IntConst(1)); let expected = syntax::Expr::Binary(syntax::BinaryOp::Add, one.clone(), one); assert_eq!(expr("1 + 1;"), Ok((";", expected.clone()))); assert_eq!(expr("1+1;"), Ok((";", expected.clone()))); assert_eq!(expr("(1 + 1);"), Ok((";", expected))); } #[test] fn parse_expr_add_3() { let one = Box::new(syntax::Expr::UIntConst(1)); let two = Box::new(syntax::Expr::UIntConst(2)); let three = Box::new(syntax::Expr::UIntConst(3)); let expected = syntax::Expr::Binary( syntax::BinaryOp::Add, Box::new(syntax::Expr::Binary(syntax::BinaryOp::Add, one, two)), three, ); assert_eq!(expr("1u + 2u + 3u"), Ok(("", expected.clone()))); assert_eq!(expr("1u + 2u + 3u "), Ok((" ", expected.clone()))); assert_eq!(expr("1u+2u+3u"), Ok(("", expected.clone()))); assert_eq!(expr("((1u + 2u) + 3u)"), Ok(("", expected))); } #[test] fn parse_expr_add_mult_3() { let one = Box::new(syntax::Expr::UIntConst(1)); let two = Box::new(syntax::Expr::UIntConst(2)); let three = Box::new(syntax::Expr::UIntConst(3)); let expected = syntax::Expr::Binary( syntax::BinaryOp::Add, Box::new(syntax::Expr::Binary(syntax::BinaryOp::Mult, one, two)), three, ); assert_eq!(expr("1u * 2u + 3u ;"), Ok((" ;", expected.clone()))); assert_eq!(expr("1u*2u+3u;"), Ok((";", expected.clone()))); assert_eq!(expr("(1u * 2u) + 3u;"), Ok((";", expected))); } #[test] fn parse_expr_add_sub_mult_div() { let one = Box::new(syntax::Expr::IntConst(1)); let two = Box::new(syntax::Expr::IntConst(2)); let three = Box::new(syntax::Expr::IntConst(3)); let four = Box::new(syntax::Expr::IntConst(4)); let five = Box::new(syntax::Expr::IntConst(5)); let six = Box::new(syntax::Expr::IntConst(6)); let expected = syntax::Expr::Binary( syntax::BinaryOp::Add, Box::new(syntax::Expr::Binary( syntax::BinaryOp::Mult, one, Box::new(syntax::Expr::Binary(syntax::BinaryOp::Add, two, three)), )), Box::new(syntax::Expr::Binary( syntax::BinaryOp::Div, four, Box::new(syntax::Expr::Binary(syntax::BinaryOp::Add, five, six)), )), ); assert_eq!( expr("1 * (2 + 3) + 4 / (5 + 6);"), Ok((";", expected.clone())) ); } #[test] fn parse_complex_expr() { let input = "normalize((inverse(view) * vec4(ray.dir, 0.)).xyz);"; let zero = syntax::Expr::FloatConst(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 normalize = syntax::Expr::FunCall( syntax::FunIdentifier::Identifier("normalize".into()), vec![xyz], ); let expected = normalize; assert_eq!(expr(&input[..]), Ok((";", expected))); } #[test] fn parse_function_identifier_typename() { let expected = syntax::FunIdentifier::Identifier("foo".into()); assert_eq!(function_identifier("foo("), Ok(("(", expected.clone()))); assert_eq!(function_identifier("foo\n\t("), Ok(("(", expected.clone()))); assert_eq!(function_identifier("foo\n ("), Ok(("(", expected))); } #[test] fn parse_function_identifier_cast() { let expected = syntax::FunIdentifier::Identifier("vec3".into()); assert_eq!(function_identifier("vec3("), Ok(("(", expected.clone()))); assert_eq!(function_identifier("vec3 ("), Ok(("(", expected.clone()))); assert_eq!(function_identifier("vec3\t\n\n \t ("), Ok(("(", expected))); } #[test] fn parse_function_identifier_cast_array_unsized() { let expected = syntax::FunIdentifier::Expr(Box::new(syntax::Expr::Bracket( Box::new(syntax::Expr::Variable("vec3".into())), syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), }, ))); assert_eq!(function_identifier("vec3[]("), Ok(("(", expected.clone()))); assert_eq!(function_identifier("vec3 [\t\n]("), Ok(("(", expected))); } #[test] fn parse_function_identifier_cast_array_sized() { let expected = syntax::FunIdentifier::Expr(Box::new(syntax::Expr::Bracket( Box::new(syntax::Expr::Variable("vec3".into())), syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( Box::new(syntax::Expr::IntConst(12)), )]), }, ))); assert_eq!( function_identifier("vec3[12]("), Ok(("(", expected.clone())) ); assert_eq!(function_identifier("vec3 [\t 12\n]("), Ok(("(", expected))); } #[test] fn parse_void() { assert_eq!(void("void "), Ok((" ", ()))); } #[test] fn parse_assignment_op_equal() { assert_eq!(assignment_op("= "), Ok((" ", syntax::AssignmentOp::Equal))); } #[test] fn parse_assignment_op_mult() { assert_eq!(assignment_op("*= "), Ok((" ", syntax::AssignmentOp::Mult))); } #[test] fn parse_assignment_op_div() { assert_eq!(assignment_op("/= "), Ok((" ", syntax::AssignmentOp::Div))); } #[test] fn parse_assignment_op_mod() { assert_eq!(assignment_op("%= "), Ok((" ", syntax::AssignmentOp::Mod))); } #[test] fn parse_assignment_op_add() { assert_eq!(assignment_op("+= "), Ok((" ", syntax::AssignmentOp::Add))); } #[test] fn parse_assignment_op_sub() { assert_eq!(assignment_op("-= "), Ok((" ", syntax::AssignmentOp::Sub))); } #[test] fn parse_assignment_op_lshift() { assert_eq!( assignment_op("<<= "), Ok((" ", syntax::AssignmentOp::LShift)) ); } #[test] fn parse_assignment_op_rshift() { assert_eq!( assignment_op(">>= "), Ok((" ", syntax::AssignmentOp::RShift)) ); } #[test] fn parse_assignment_op_and() { assert_eq!(assignment_op("&= "), Ok((" ", syntax::AssignmentOp::And))); } #[test] fn parse_assignment_op_xor() { assert_eq!(assignment_op("^= "), Ok((" ", syntax::AssignmentOp::Xor))); } #[test] fn parse_assignment_op_or() { assert_eq!(assignment_op("|= "), Ok((" ", syntax::AssignmentOp::Or))); } #[test] fn parse_expr_statement() { let expected = Some(syntax::Expr::Assignment( Box::new(syntax::Expr::Variable("foo".into())), syntax::AssignmentOp::Equal, Box::new(syntax::Expr::FloatConst(314.)), )); assert_eq!(expr_statement("foo = 314.f;"), Ok(("", expected.clone()))); assert_eq!(expr_statement("foo=314.f;"), Ok(("", expected.clone()))); assert_eq!(expr_statement("foo\n\t= \n314.f;"), Ok(("", expected))); } #[test] fn parse_declaration_function_prototype() { let rt = syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec3, array_specifier: None, }, }; let arg0_ty = syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec2, array_specifier: None, }; let arg0 = syntax::FunctionParameterDeclaration::Unnamed(None, arg0_ty); let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Out); let qual = syntax::TypeQualifier { qualifiers: syntax::NonEmpty(vec![qual_spec]), }; let arg1 = syntax::FunctionParameterDeclaration::Named( Some(qual), syntax::FunctionParameterDeclarator { ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }, ident: "the_arg".into(), }, ); let fp = syntax::FunctionPrototype { ty: rt, name: "foo".into(), parameters: vec![arg0, arg1], }; let expected = syntax::Declaration::FunctionPrototype(fp); assert_eq!( declaration("vec3 foo(vec2, out float the_arg);"), Ok(("", expected.clone())) ); assert_eq!( declaration("vec3 \nfoo ( vec2\n, out float \n\tthe_arg )\n;"), Ok(("", expected.clone())) ); assert_eq!( declaration("vec3 foo(vec2,out float the_arg);"), Ok(("", expected)) ); } #[test] fn parse_declaration_init_declarator_list_single() { let ty = syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Int, array_specifier: None, }, }; let sd = syntax::SingleDeclaration { ty, name: Some("foo".into()), array_specifier: None, initializer: Some(syntax::Initializer::Simple(Box::new( syntax::Expr::IntConst(34), ))), }; let idl = syntax::InitDeclaratorList { head: sd, tail: Vec::new(), }; let expected = syntax::Declaration::InitDeclaratorList(idl); assert_eq!(declaration("int foo = 34;"), Ok(("", expected.clone()))); assert_eq!(declaration("int foo=34;"), Ok(("", expected.clone()))); assert_eq!(declaration("int \t \nfoo =\t34 ;"), Ok(("", expected))); } #[test] fn parse_declaration_init_declarator_list_complex() { let ty = syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Int, array_specifier: None, }, }; let sd = syntax::SingleDeclaration { ty, name: Some("foo".into()), array_specifier: None, initializer: Some(syntax::Initializer::Simple(Box::new( syntax::Expr::IntConst(34), ))), }; let sdnt = syntax::SingleDeclarationNoType { ident: "bar".into(), initializer: Some(syntax::Initializer::Simple(Box::new( syntax::Expr::IntConst(12), ))), }; let expected = syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { head: sd, tail: vec![sdnt], }); assert_eq!( declaration("int foo = 34, bar = 12;"), Ok(("", expected.clone())) ); assert_eq!( declaration("int foo=34,bar=12;"), Ok(("", expected.clone())) ); assert_eq!( declaration("int \t \nfoo =\t34 \n,\tbar= 12\n ;"), Ok(("", expected)) ); } #[test] fn parse_declaration_precision_low() { let qual = syntax::PrecisionQualifier::Low; let ty = syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }; let expected = syntax::Declaration::Precision(qual, ty); assert_eq!(declaration("precision lowp float;"), Ok(("", expected))); } #[test] fn parse_declaration_precision_medium() { let qual = syntax::PrecisionQualifier::Medium; let ty = syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }; let expected = syntax::Declaration::Precision(qual, ty); assert_eq!(declaration("precision mediump float;"), Ok(("", expected))); } #[test] fn parse_declaration_precision_high() { let qual = syntax::PrecisionQualifier::High; let ty = syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }; let expected = syntax::Declaration::Precision(qual, ty); assert_eq!(declaration("precision highp float;"), Ok(("", expected))); } #[test] fn parse_declaration_uniform_block() { let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Uniform); let qual = syntax::TypeQualifier { qualifiers: syntax::NonEmpty(vec![qual_spec]), }; let f0 = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["a".into()]), }; let f1 = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec3, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["b".into()]), }; let f2 = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::TypeName("foo".into()), array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["c".into(), "d".into()]), }; let expected = syntax::Declaration::Block(syntax::Block { qualifier: qual, name: "UniformBlockTest".into(), fields: vec![f0, f1, f2], identifier: None, }); assert_eq!( declaration("uniform UniformBlockTest { float a; vec3 b; foo c, d; };"), Ok(("", expected.clone())) ); assert_eq!(declaration("uniform \nUniformBlockTest\n {\n \t float a \n; \nvec3 b\n; foo \nc\n, \nd\n;\n }\n\t\n\t\t \t;"), Ok(("", expected))); } #[test] fn parse_declaration_buffer_block() { let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Buffer); let qual = syntax::TypeQualifier { qualifiers: syntax::NonEmpty(vec![qual_spec]), }; let f0 = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["a".into()]), }; let f1 = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec3, array_specifier: None, }, identifiers: syntax::NonEmpty(vec![syntax::ArrayedIdentifier::new( "b", Some(syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), }), )]), }; let f2 = syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::TypeName("foo".into()), array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["c".into(), "d".into()]), }; let expected = syntax::Declaration::Block(syntax::Block { qualifier: qual, name: "UniformBlockTest".into(), fields: vec![f0, f1, f2], identifier: None, }); assert_eq!( declaration("buffer UniformBlockTest { float a; vec3 b[]; foo c, d; };"), Ok(("", expected.clone())) ); assert_eq!(declaration("buffer \nUniformBlockTest\n {\n \t float a \n; \nvec3 b [ ]\n; foo \nc\n, \nd\n;\n }\n\t\n\t\t \t;"), Ok(("", expected))); } #[test] fn parse_selection_statement_if() { let cond = syntax::Expr::Binary( syntax::BinaryOp::LT, Box::new(syntax::Expr::Variable("foo".into())), Box::new(syntax::Expr::IntConst(10)), ); let ret = Box::new(syntax::Expr::BoolConst(false)); let st = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( syntax::JumpStatement::Return(Some(ret)), ))); let body = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { statement_list: vec![st], })); let rest = syntax::SelectionRestStatement::Statement(Box::new(body)); let expected = syntax::SelectionStatement { cond: Box::new(cond), rest, }; assert_eq!( selection_statement("if (foo < 10) { return false; }K"), Ok(("K", expected.clone())) ); assert_eq!( selection_statement("if \n(foo<10\n) \t{return false;}K"), Ok(("K", expected)) ); } #[test] fn parse_selection_statement_if_else() { let cond = syntax::Expr::Binary( syntax::BinaryOp::LT, Box::new(syntax::Expr::Variable("foo".into())), Box::new(syntax::Expr::IntConst(10)), ); let if_ret = Box::new(syntax::Expr::FloatConst(0.)); let if_st = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( syntax::JumpStatement::Return(Some(if_ret)), ))); let if_body = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { statement_list: vec![if_st], })); let else_ret = Box::new(syntax::Expr::Variable("foo".into())); let else_st = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( syntax::JumpStatement::Return(Some(else_ret)), ))); let else_body = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { statement_list: vec![else_st], })); let rest = syntax::SelectionRestStatement::Else(Box::new(if_body), Box::new(else_body)); let expected = syntax::SelectionStatement { cond: Box::new(cond), rest, }; assert_eq!( selection_statement("if (foo < 10) { return 0.f; } else { return foo; }"), Ok(("", expected.clone())) ); assert_eq!( selection_statement("if \n(foo<10\n) \t{return 0.f\t;\n\n}\n else{\n\t return foo ;}"), Ok(("", expected)) ); } #[test] fn parse_switch_statement_empty() { let head = Box::new(syntax::Expr::Variable("foo".into())); let expected = syntax::SwitchStatement { head, body: Vec::new(), }; assert_eq!( switch_statement("switch (foo) {}"), Ok(("", expected.clone())) ); assert_eq!( switch_statement("switch(foo){}"), Ok(("", expected.clone())) ); assert_eq!( switch_statement("switch\n\n ( foo \t \n) { \n\n }"), Ok(("", expected)) ); } #[test] fn parse_switch_statement_cases() { let head = Box::new(syntax::Expr::Variable("foo".into())); let case0 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::CaseLabel( syntax::CaseLabel::Case(Box::new(syntax::Expr::IntConst(0))), ))); let case1 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::CaseLabel( syntax::CaseLabel::Case(Box::new(syntax::Expr::IntConst(1))), ))); let ret = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::UIntConst(12)))), ))); let expected = syntax::SwitchStatement { head, body: vec![case0, case1, ret], }; assert_eq!( switch_statement("switch (foo) { case 0: case 1: return 12u; }"), Ok(("", expected.clone())) ); } #[test] fn parse_case_label_def() { assert_eq!(case_label("default:"), Ok(("", syntax::CaseLabel::Def))); assert_eq!(case_label("default :"), Ok(("", syntax::CaseLabel::Def))); } #[test] fn parse_case_label() { let expected = syntax::CaseLabel::Case(Box::new(syntax::Expr::IntConst(3))); assert_eq!(case_label("case 3:"), Ok(("", expected.clone()))); assert_eq!(case_label("case\n\t 3 :"), Ok(("", expected))); } #[test] fn parse_iteration_statement_while_empty() { let cond = syntax::Condition::Expr(Box::new(syntax::Expr::Binary( syntax::BinaryOp::GTE, Box::new(syntax::Expr::Variable("a".into())), Box::new(syntax::Expr::Variable("b".into())), ))); let st = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { statement_list: Vec::new(), })); let expected = syntax::IterationStatement::While(cond, Box::new(st)); assert_eq!( iteration_statement("while (a >= b) {}"), Ok(("", expected.clone())) ); assert_eq!( iteration_statement("while(a>=b){}"), Ok(("", expected.clone())) ); assert_eq!( iteration_statement("while ( a >=\n\tb )\t { \n}"), Ok(("", expected)) ); } #[test] fn parse_iteration_statement_do_while_empty() { let st = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { statement_list: Vec::new(), })); let cond = Box::new(syntax::Expr::Binary( syntax::BinaryOp::GTE, Box::new(syntax::Expr::Variable("a".into())), Box::new(syntax::Expr::Variable("b".into())), )); let expected = syntax::IterationStatement::DoWhile(Box::new(st), cond); assert_eq!( iteration_statement("do {} while (a >= b);"), Ok(("", expected.clone())) ); assert_eq!( iteration_statement("do{}while(a>=b);"), Ok(("", expected.clone())) ); assert_eq!( iteration_statement("do \n {\n} while ( a >=\n\tb )\t \n;"), Ok(("", expected)) ); } #[test] fn parse_iteration_statement_for_empty() { let init = syntax::ForInitStatement::Declaration(Box::new( syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { head: syntax::SingleDeclaration { ty: syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Float, array_specifier: None, }, }, name: Some("i".into()), array_specifier: None, initializer: Some(syntax::Initializer::Simple(Box::new( syntax::Expr::FloatConst(0.), ))), }, tail: Vec::new(), }), )); let rest = syntax::ForRestStatement { condition: Some(syntax::Condition::Expr(Box::new(syntax::Expr::Binary( syntax::BinaryOp::LTE, Box::new(syntax::Expr::Variable("i".into())), Box::new(syntax::Expr::FloatConst(10.)), )))), post_expr: Some(Box::new(syntax::Expr::Unary( syntax::UnaryOp::Inc, Box::new(syntax::Expr::Variable("i".into())), ))), }; let st = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { statement_list: Vec::new(), })); let expected = syntax::IterationStatement::For(init, rest, Box::new(st)); assert_eq!( iteration_statement("for (float i = 0.f; i <= 10.f; ++i) {}"), Ok(("", expected.clone())) ); assert_eq!( iteration_statement("for(float i=0.f;i<=10.f;++i){}"), Ok(("", expected.clone())) ); assert_eq!( iteration_statement("for\n\t ( \t\n\nfloat \ni \t=\n0.f\n;\ni\t<= 10.f; \n++i\n)\n{\n}"), Ok(("", expected)) ); } #[test] fn parse_jump_continue() { assert_eq!( jump_statement("continue;"), Ok(("", syntax::JumpStatement::Continue)) ); } #[test] fn parse_jump_break() { assert_eq!( jump_statement("break;"), Ok(("", syntax::JumpStatement::Break)) ); } #[test] fn parse_jump_return() { let expected = syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::IntConst(3)))); assert_eq!(jump_statement("return 3;"), Ok(("", expected))); } #[test] fn parse_jump_empty_return() { let expected = syntax::SimpleStatement::Jump(syntax::JumpStatement::Return(None)); assert_eq!(simple_statement("return;"), Ok(("", expected))); } #[test] fn parse_jump_discard() { assert_eq!( jump_statement("discard;"), Ok(("", syntax::JumpStatement::Discard)) ); } #[test] fn parse_simple_statement_return() { let e = syntax::Expr::BoolConst(false); let expected = syntax::SimpleStatement::Jump(syntax::JumpStatement::Return(Some(Box::new(e)))); assert_eq!(simple_statement("return false;"), Ok(("", expected))); } #[test] fn parse_compound_statement_empty() { let expected = syntax::CompoundStatement { statement_list: Vec::new(), }; assert_eq!(compound_statement("{}"), Ok(("", expected))); } #[test] fn parse_compound_statement() { let st0 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Selection( syntax::SelectionStatement { cond: Box::new(syntax::Expr::BoolConst(true)), rest: syntax::SelectionRestStatement::Statement(Box::new(syntax::Statement::Compound( Box::new(syntax::CompoundStatement { statement_list: Vec::new(), }), ))), }, ))); let st1 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Declaration( syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { head: syntax::SingleDeclaration { ty: syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::ISampler3D, array_specifier: None, }, }, name: Some("x".into()), array_specifier: None, initializer: None, }, tail: Vec::new(), }), ))); let st2 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::IntConst(42)))), ))); let expected = syntax::CompoundStatement { statement_list: vec![st0, st1, st2], }; assert_eq!( compound_statement("{ if (true) {} isampler3D x; return 42 ; }"), Ok(("", expected.clone())) ); assert_eq!( compound_statement("{if(true){}isampler3D x;return 42;}"), Ok(("", expected)) ); } #[test] fn parse_function_definition() { let rt = syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::IImage2DArray, array_specifier: None, }, }; let fp = syntax::FunctionPrototype { ty: rt, name: "foo".into(), parameters: Vec::new(), }; let st0 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::Variable("bar".into())))), ))); let expected = syntax::FunctionDefinition { prototype: fp, statement: syntax::CompoundStatement { statement_list: vec![st0], }, }; assert_eq!( function_definition("iimage2DArray foo() { return bar; }"), Ok(("", expected.clone())) ); assert_eq!( function_definition("iimage2DArray \tfoo\n()\n \n{\n return \nbar\n;}"), Ok(("", expected.clone())) ); assert_eq!( function_definition("iimage2DArray foo(){return bar;}"), Ok(("", expected)) ); } #[test] fn parse_buffer_block_0() { let src = include_str!("../data/tests/buffer_block_0.glsl"); let main_fn = syntax::ExternalDeclaration::FunctionDefinition(syntax::FunctionDefinition { prototype: syntax::FunctionPrototype { ty: syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Void, array_specifier: None, }, }, name: "main".into(), parameters: Vec::new(), }, statement: syntax::CompoundStatement { statement_list: Vec::new(), }, }); let buffer_block = syntax::ExternalDeclaration::Declaration(syntax::Declaration::Block(syntax::Block { qualifier: syntax::TypeQualifier { qualifiers: syntax::NonEmpty(vec![syntax::TypeQualifierSpec::Storage( syntax::StorageQualifier::Buffer, )]), }, name: "Foo".into(), fields: vec![syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::TypeName("char".into()), array_specifier: None, }, identifiers: syntax::NonEmpty(vec![syntax::ArrayedIdentifier::new( "tiles", Some(syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), }), )]), }], identifier: Some("main_tiles".into()), })); let expected = syntax::TranslationUnit(syntax::NonEmpty(vec![buffer_block, main_fn])); assert_eq!(translation_unit(src), Ok(("", expected))); } #[test] fn parse_layout_buffer_block_0() { let src = include_str!("../data/tests/layout_buffer_block_0.glsl"); let layout = syntax::LayoutQualifier { ids: syntax::NonEmpty(vec![ syntax::LayoutQualifierSpec::Identifier( "set".into(), Some(Box::new(syntax::Expr::IntConst(0))), ), syntax::LayoutQualifierSpec::Identifier( "binding".into(), Some(Box::new(syntax::Expr::IntConst(0))), ), ]), }; let type_qual = syntax::TypeQualifier { qualifiers: syntax::NonEmpty(vec![ syntax::TypeQualifierSpec::Layout(layout), syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Buffer), ]), }; let block = syntax::ExternalDeclaration::Declaration(syntax::Declaration::Block(syntax::Block { qualifier: type_qual, name: "Foo".into(), fields: vec![syntax::StructFieldSpecifier { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::TypeName("char".into()), array_specifier: None, }, identifiers: syntax::NonEmpty(vec!["a".into()]), }], identifier: Some("foo".into()), })); let expected = syntax::TranslationUnit(syntax::NonEmpty(vec![block])); assert_eq!(translation_unit(src), Ok(("", expected))); } #[test] fn parse_pp_space0() { assert_eq!(pp_space0(" \\\n "), Ok(("", " \\\n "))); assert_eq!(pp_space0(""), Ok(("", ""))); } #[test] fn parse_pp_version_number() { assert_eq!(pp_version_number("450"), Ok(("", 450))); } #[test] fn parse_pp_version_profile() { assert_eq!( pp_version_profile("core"), Ok(("", syntax::PreprocessorVersionProfile::Core)) ); assert_eq!( pp_version_profile("compatibility"), Ok(("", syntax::PreprocessorVersionProfile::Compatibility)) ); assert_eq!( pp_version_profile("es"), Ok(("", syntax::PreprocessorVersionProfile::ES)) ); } #[test] fn parse_pp_version() { assert_eq!( preprocessor("#version 450\n"), Ok(( "", syntax::Preprocessor::Version(syntax::PreprocessorVersion { version: 450, profile: None, }) )) ); assert_eq!( preprocessor("#version 450 core\n"), Ok(( "", syntax::Preprocessor::Version(syntax::PreprocessorVersion { version: 450, profile: Some(syntax::PreprocessorVersionProfile::Core) }) )) ); } #[test] fn parse_pp_version_newline() { assert_eq!( preprocessor("#version 450\n"), Ok(( "", syntax::Preprocessor::Version(syntax::PreprocessorVersion { version: 450, profile: None, }) )) ); assert_eq!( preprocessor("#version 450 core\n"), Ok(( "", syntax::Preprocessor::Version(syntax::PreprocessorVersion { version: 450, profile: Some(syntax::PreprocessorVersionProfile::Core) }) )) ); } #[test] fn parse_pp_define() { let expect = |v: &str| { Ok(( "", syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { ident: "test".into(), value: v.to_owned(), }), )) }; assert_eq!(preprocessor("#define test 1.0"), expect("1.0")); assert_eq!(preprocessor("#define test \\\n 1.0"), expect("1.0")); assert_eq!(preprocessor("#define test 1.0\n"), expect("1.0")); assert_eq!( preprocessor("#define test123 .0f\n"), Ok(( "", syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { ident: "test123".into(), value: ".0f".to_owned() }) )) ); assert_eq!( preprocessor("#define test 1\n"), Ok(( "", syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { ident: "test".into(), value: "1".to_owned() }) )) ); } #[test] fn parse_pp_define_with_args() { let expected = syntax::Preprocessor::Define(syntax::PreprocessorDefine::FunctionLike { ident: "add".into(), args: vec![ syntax::Identifier::new("x").unwrap(), syntax::Identifier::new("y").unwrap(), ], value: "(x + y)".to_owned(), }); assert_eq!( preprocessor("#define \\\n add(x, y) \\\n (x + y)"), Ok(("", expected.clone())) ); assert_eq!( preprocessor("#define \\\n add( x, y ) \\\n (x + y)"), Ok(("", expected)) ); } #[test] fn parse_pp_define_multiline() { assert_eq!( preprocessor( r#"#define foo \ 32"# ), Ok(( "", syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { ident: "foo".into(), value: "32".to_owned(), }) )) ); } #[test] fn parse_pp_else() { assert_eq!( preprocessor("# else\n"), Ok(("", syntax::Preprocessor::Else)) ); } #[test] fn parse_pp_elseif() { assert_eq!( preprocessor("# elseif \\\n42\n"), Ok(( "", syntax::Preprocessor::ElseIf(syntax::PreprocessorElseIf { condition: "42".to_owned() }) )) ); } #[test] fn parse_pp_endif() { assert_eq!( preprocessor("#\\\nendif"), Ok(("", syntax::Preprocessor::EndIf)) ); } #[test] fn parse_pp_error() { assert_eq!( preprocessor("#error \\\n some message"), Ok(( "", syntax::Preprocessor::Error(syntax::PreprocessorError { message: "some message".to_owned() }) )) ); } #[test] fn parse_pp_if() { assert_eq!( preprocessor("# \\\nif 42"), Ok(( "", syntax::Preprocessor::If(syntax::PreprocessorIf { condition: "42".to_owned() }) )) ); } #[test] fn parse_pp_ifdef() { assert_eq!( preprocessor("#ifdef FOO\n"), Ok(( "", syntax::Preprocessor::IfDef(syntax::PreprocessorIfDef { ident: syntax::Identifier("FOO".to_owned()) }) )) ); } #[test] fn parse_pp_ifndef() { assert_eq!( preprocessor("#\\\nifndef \\\n FOO\n"), Ok(( "", syntax::Preprocessor::IfNDef(syntax::PreprocessorIfNDef { ident: syntax::Identifier("FOO".to_owned()) }) )) ); } #[test] fn parse_pp_include() { assert_eq!( preprocessor("#include \n"), Ok(( "", syntax::Preprocessor::Include(syntax::PreprocessorInclude { path: syntax::Path::Absolute("filename".to_owned()) }) )) ); assert_eq!( preprocessor("#include \\\n\"filename\"\n"), Ok(( "", syntax::Preprocessor::Include(syntax::PreprocessorInclude { path: syntax::Path::Relative("filename".to_owned()) }) )) ); } #[test] fn parse_pp_line() { assert_eq!( preprocessor("# line \\\n2\n"), Ok(( "", syntax::Preprocessor::Line(syntax::PreprocessorLine { line: 2, source_string_number: None, }) )) ); assert_eq!( preprocessor("#line 2 \\\n 4\n"), Ok(( "", syntax::Preprocessor::Line(syntax::PreprocessorLine { line: 2, source_string_number: Some(4), }) )) ); } #[test] fn parse_pp_pragma() { assert_eq!( preprocessor("#\\\npragma some flag"), Ok(( "", syntax::Preprocessor::Pragma(syntax::PreprocessorPragma { command: "some flag".to_owned() }) )) ); } #[test] fn parse_pp_undef() { assert_eq!( preprocessor("# undef \\\n FOO"), Ok(( "", syntax::Preprocessor::Undef(syntax::PreprocessorUndef { name: syntax::Identifier("FOO".to_owned()) }) )) ); } #[test] fn parse_pp_extension_name() { assert_eq!( pp_extension_name("all"), Ok(("", syntax::PreprocessorExtensionName::All)) ); assert_eq!( pp_extension_name("GL_foobar_extension "), Ok(( " ", syntax::PreprocessorExtensionName::Specific("GL_foobar_extension".to_owned()) )) ); } #[test] fn parse_pp_extension_behavior() { assert_eq!( pp_extension_behavior("require"), Ok(("", syntax::PreprocessorExtensionBehavior::Require)) ); assert_eq!( pp_extension_behavior("enable"), Ok(("", syntax::PreprocessorExtensionBehavior::Enable)) ); assert_eq!( pp_extension_behavior("warn"), Ok(("", syntax::PreprocessorExtensionBehavior::Warn)) ); assert_eq!( pp_extension_behavior("disable"), Ok(("", syntax::PreprocessorExtensionBehavior::Disable)) ); } #[test] fn parse_pp_extension() { assert_eq!( preprocessor("#extension all: require\n"), Ok(( "", syntax::Preprocessor::Extension(syntax::PreprocessorExtension { name: syntax::PreprocessorExtensionName::All, behavior: Some(syntax::PreprocessorExtensionBehavior::Require) }) )) ); } #[test] fn parse_dot_field_expr_array() { let src = "a[0].xyz;"; let expected = syntax::Expr::Dot( Box::new(syntax::Expr::Bracket( Box::new(syntax::Expr::Variable("a".into())), syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( Box::new(syntax::Expr::IntConst(0)), )]), }, )), "xyz".into(), ); assert_eq!(expr(src), Ok((";", expected))); } #[test] fn parse_dot_field_expr_statement() { let src = "vec3 v = smoothstep(vec3(border_width), vec3(0.0), v_barycenter).zyx;"; let fun = syntax::FunIdentifier::Identifier("smoothstep".into()); let args = vec![ syntax::Expr::FunCall( syntax::FunIdentifier::Identifier("vec3".into()), vec![syntax::Expr::Variable("border_width".into())], ), syntax::Expr::FunCall( syntax::FunIdentifier::Identifier("vec3".into()), vec![syntax::Expr::FloatConst(0.)], ), syntax::Expr::Variable("v_barycenter".into()), ]; let ini = syntax::Initializer::Simple(Box::new(syntax::Expr::Dot( Box::new(syntax::Expr::FunCall(fun, args)), "zyx".into(), ))); let sd = syntax::SingleDeclaration { ty: syntax::FullySpecifiedType { qualifier: None, ty: syntax::TypeSpecifier { ty: syntax::TypeSpecifierNonArray::Vec3, array_specifier: None, }, }, name: Some("v".into()), array_specifier: None, initializer: Some(ini), }; let expected = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Declaration( syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { head: sd, tail: Vec::new(), }), ))); assert_eq!(statement(src), Ok(("", expected))); } #[test] fn parse_arrayed_identifier() { let expected = syntax::ArrayedIdentifier::new( "foo", syntax::ArraySpecifier { dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), }, ); assert_eq!(arrayed_identifier("foo[]"), Ok(("", expected.clone()))); assert_eq!(arrayed_identifier("foo \t\n [\n\t ]"), Ok(("", expected))); } #[test] fn parse_nested_parens() { let start = std::time::Instant::now(); parens_expr("((((((((1.0f))))))))").unwrap(); let elapsed = start.elapsed(); assert!(elapsed.as_millis() < 100, "{} ms", elapsed.as_millis()); }