diff options
Diffstat (limited to 'third_party/rust/glsl/src/parse_tests.rs')
-rw-r--r-- | third_party/rust/glsl/src/parse_tests.rs | 2847 |
1 files changed, 2847 insertions, 0 deletions
diff --git a/third_party/rust/glsl/src/parse_tests.rs b/third_party/rust/glsl/src/parse_tests.rs new file mode 100644 index 0000000000..3a2c7a6880 --- /dev/null +++ b/third_party/rust/glsl/src/parse_tests.rs @@ -0,0 +1,2847 @@ +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 <filename>\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()); +} |