Improve handling of '_' in numeric literals

* The first character of octal literals is consumed early to
  disambiguate with a literal 0, and the next character may
  be '_'.
* Double.parseDouble and Float.parseFloat don't handle '_'.

MOE_MIGRATED_REVID=137779638
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index c8bca7b..3c89ffe 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -338,14 +338,14 @@
         break;
       case FLOAT:
         try {
-          value = new Const.FloatValue(Float.parseFloat(text));
+          value = new Const.FloatValue(Float.parseFloat(text.replace("_", "")));
         } catch (NumberFormatException e) {
           throw error("invalid float literal");
         }
         break;
       case DOUBLE:
         try {
-          value = new Const.DoubleValue(Double.parseDouble(text));
+          value = new Const.DoubleValue(Double.parseDouble(text.replace("_", "")));
         } catch (NumberFormatException e) {
           throw error("invalid double literal");
         }
diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java
index 4aef7a8..2303645 100644
--- a/java/com/google/turbine/parse/StreamLexer.java
+++ b/java/com/google/turbine/parse/StreamLexer.java
@@ -751,6 +751,7 @@
       case '5':
       case '6':
       case '7':
+      case '_':
         eat();
         break;
       default:
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index cd41dd6..6d943c4 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -255,6 +255,7 @@
       "static_member_type_import.test",
       "type_anno_qual.test",
       "array_class_literal.test",
+      "underscore_literal.test",
     };
     List<Object[]> tests =
         ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
diff --git a/javatests/com/google/turbine/lower/testdata/underscore_literal.test b/javatests/com/google/turbine/lower/testdata/underscore_literal.test
new file mode 100644
index 0000000..a4aaac2
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/underscore_literal.test
@@ -0,0 +1,14 @@
+=== Test.java ===
+class Test {
+  static final double A1 = 1_000.0d;
+  static final double A2 = 1__000.0d;
+  static final double A3 = 1_0__00.0d;
+
+  static final float B1 = 1_000.0f;
+  static final float B2 = 1__000.0f;
+  static final float B3 = 1_0__00.0f;
+
+  static final long C1 = 0_000_000L;
+  static final long C2 = 0___000_000L;
+  static final long C3 = 0_000___000L;
+}
\ No newline at end of file