@Test
public void testExpression() {

  Parser<Expression> parser =
      ExpressionParser.expression(ExpressionParser.IDENTIFIER, EMPTY_BODY, StatementParser.expression(ExpressionParser.IDENTIFIER));
  TerminalParserTest.assertResult(parser, "foo", Identifier.class, "foo");
  TerminalParserTest.assertResult(parser, "(foo)", Identifier.class, "foo");
  TerminalParserTest.assertResult(parser, "((foo))", Identifier.class, "foo");
  TerminalParserTest.assertResult(parser, "foo[bar[baz]]", ArraySubscriptExpression.class, "foo[bar[baz]]");
  TerminalParserTest.assertResult(parser, "(foo) (bar)", CastExpression.class, "((foo) bar)");
  TerminalParserTest.assertResult(parser, "(foo) (bar) baz", CastExpression.class, "((foo) ((bar) baz))");
  TerminalParserTest.assertResult(parser, "new Foo(a,b)", NewExpression.class, "new Foo(a, b)");
  TerminalParserTest.assertResult(parser, "new int[n]", NewArrayExpression.class, "new int[n]");
  TerminalParserTest.assertResult(parser, "new int[n]{}", NewArrayExpression.class, "new int[n] {}");
  TerminalParserTest.assertResult(parser, "new int[]{a,b,c}", NewArrayExpression.class, "new int[] {a, b, c}");
  TerminalParserTest.assertResult(parser, "foo(a)", MethodCallExpression.class, "foo(a)");
  TerminalParserTest.assertResult(parser, "foo.f()", MethodCallExpression.class, "foo.f()");
  TerminalParserTest.assertResult(parser, "foo().bar().baz()", MethodCallExpression.class, "foo().bar().baz()");
  TerminalParserTest.assertResult(parser, "foo.new Foo()", NewExpression.class, "foo.new Foo()");
  TerminalParserTest.assertResult(parser, "foo.bar.baz", QualifiedExpression.class, "((foo.bar).baz)");
  TerminalParserTest.assertResult(parser, "foo.bar.new Foo()", NewExpression.class, "(foo.bar).new Foo()");
  TerminalParserTest.assertResult(parser, "foo++", PostfixUnaryExpression.class, "(foo++)");
  TerminalParserTest.assertResult(parser, "foo++--", PostfixUnaryExpression.class, "((foo++)--)");
  TerminalParserTest.assertResult(parser, "++foo", PrefixUnaryExpression.class, "(++foo)");
  TerminalParserTest.assertResult(parser, "++--foo", PrefixUnaryExpression.class, "(++(--foo))");
  TerminalParserTest.assertResult(parser, "++foo--", PrefixUnaryExpression.class, "(++(foo--))");
  TerminalParserTest.assertResult(parser, "+foo", PrefixUnaryExpression.class, "(+foo)");
  TerminalParserTest.assertResult(parser, "+-foo", PrefixUnaryExpression.class, "(+(-foo))");
  TerminalParserTest.assertResult(parser, "!foo", PrefixUnaryExpression.class, "(!foo)");
  TerminalParserTest.assertResult(parser, "!!foo", PrefixUnaryExpression.class, "(!(!foo))");
  TerminalParserTest.assertResult(parser, "~foo", PrefixUnaryExpression.class, "(~foo)");
  TerminalParserTest.assertResult(parser, "~~foo", PrefixUnaryExpression.class, "(~(~foo))");
  TerminalParserTest.assertResult(parser, "foo+bar", BinaryExpression.class, "(foo + bar)");
  TerminalParserTest.assertResult(parser, "a+b*c/d-e%f", BinaryExpression.class, "((a + ((b * c) / d)) - (e % f))");
  TerminalParserTest.assertResult(parser, "a<<b", BinaryExpression.class, "(a << b)");
  TerminalParserTest.assertResult(parser, "a>>b", BinaryExpression.class, "(a >> b)");
  TerminalParserTest.assertResult(parser, "a>>>b", BinaryExpression.class, "(a >>> b)");
  TerminalParserTest.assertResult(parser, "a>b", BinaryExpression.class, "(a > b)");
  TerminalParserTest.assertResult(parser, "a>b<c", BinaryExpression.class, "((a > b) < c)");
  TerminalParserTest.assertResult(parser, "a>=b", BinaryExpression.class, "(a >= b)");
  TerminalParserTest.assertResult(parser, "a>=b<=c", BinaryExpression.class, "((a >= b) <= c)");
  TerminalParserTest.assertResult(parser, "a instanceof int", InstanceOfExpression.class, "(a instanceof int)");
  TerminalParserTest.assertResult(parser, "a instanceof int instanceof boolean",
      InstanceOfExpression.class, "((a instanceof int) instanceof boolean)");
  TerminalParserTest.assertResult(parser, "a==b", BinaryExpression.class, "(a == b)");
  TerminalParserTest.assertResult(parser, "a==b!=c", BinaryExpression.class, "((a == b) != c)");
  TerminalParserTest.assertResult(parser, "a&b&c", BinaryExpression.class, "((a & b) & c)");
  TerminalParserTest.assertResult(parser, "a|b&c", BinaryExpression.class, "(a | (b & c))");
  TerminalParserTest.assertResult(parser, "a&&b|c", BinaryExpression.class, "(a && (b | c))");
  TerminalParserTest.assertResult(parser, "!a||b&&c", BinaryExpression.class, "((!a) || (b && c))");
  TerminalParserTest.assertResult(parser, "x?c:d", ConditionalExpression.class, "(x ? c : d)");
  TerminalParserTest.assertResult(parser, "a==b?b==c?x:y:z+n ? m: n",
      ConditionalExpression.class, "((a == b) ? ((b == c) ? x : y) : ((z + n) ? m : n))");
  TerminalParserTest.assertResult(parser, "a=b=c", BinaryExpression.class, "(a = (b = c))");
  TerminalParserTest.assertResult(parser, "a+=b+=c", BinaryExpression.class, "(a += (b += c))");
  TerminalParserTest.assertResult(parser, "a-=b-=c", BinaryExpression.class, "(a -= (b -= c))");
  TerminalParserTest.assertResult(parser, "a*=b*=c", BinaryExpression.class, "(a *= (b *= c))");
  TerminalParserTest.assertResult(parser, "a/=b/=c", BinaryExpression.class, "(a /= (b /= c))");
  TerminalParserTest.assertResult(parser, "a%=b%=c", BinaryExpression.class, "(a %= (b %= c))");
  TerminalParserTest.assertResult(parser, "a&=b&=c", BinaryExpression.class, "(a &= (b &= c))");
  TerminalParserTest.assertResult(parser, "a|=b|=c", BinaryExpression.class, "(a |= (b |= c))");
  TerminalParserTest.assertResult(parser, "a>>=b>>=c", BinaryExpression.class, "(a >>= (b >>= c))");
  TerminalParserTest.assertResult(parser, "a>>>=b>>>=c", BinaryExpression.class, "(a >>>= (b >>>= c))");
  TerminalParserTest.assertResult(parser, "a<<=b<<=c", BinaryExpression.class, "(a <<= (b <<= c))");
  TerminalParserTest.assertResult(parser, "a^=b^=c", BinaryExpression.class, "(a ^= (b ^= c))");
  TerminalParserTest.assertResult(parser, "Foo::new", ConstructorReference.class, "Foo::new");
  TerminalParserTest.assertResult(parser, "Foo.Bar::new", ConstructorReference.class, "(Foo.Bar)::new");
  TerminalParserTest.assertResult(parser, "x::new", ConstructorReference.class, "x::new");
  TerminalParserTest.assertResult(parser, "Foo::create", MethodReference.class, "Foo::create");
  TerminalParserTest.assertResult(parser, "Foo.Bar::create", MethodReference.class, "(Foo.Bar)::create");
  TerminalParserTest.assertResult(parser, "x::create", MethodReference.class, "x::create");
  TerminalParserTest.assertResult(parser, "Foo::<T>create", MethodReference.class, "Foo::<T>create");
  TerminalParserTest.assertResult(parser, "Foo.Bar::<T>create", MethodReference.class, "(Foo.Bar)::<T>create");
  TerminalParserTest.assertResult(parser, "x::<String>create", MethodReference.class, "x::<String>create");
  TerminalParserTest.assertResult(parser, "() -> a", LambdaExpression.class, "() -> a;");
  TerminalParserTest.assertResult(parser, "() -> {a;}", LambdaExpression.class, "() -> {a;}");
  TerminalParserTest.assertResult(parser, "x -> {}", LambdaExpression.class, "(x) -> {}");
  TerminalParserTest.assertResult(parser, "(x, y) -> {}", LambdaExpression.class, "(x, y) -> {}");
  TerminalParserTest.assertResult(parser, "(String x, int y) -> {}", LambdaExpression.class, "(String x, int y) -> {}");
  TerminalParserTest.assertResult(parser, "(String x, int y) -> {xyz;}", LambdaExpression.class, "(String x, int y) -> {xyz;}");
}