Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/glsl/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 79 kB image not shown  

Quelle  parse_tests.rs   Sprache: unbekannt

 
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() {
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet)  ]