Skip to content
Browse files

#196 ceylon implementation of parseInteger (with native java implemen…

…tation for minIntegerValue and maxIntegerValue)
  • Loading branch information...
1 parent 8e1aa88 commit 2544094951d6559fc103a00e68cb1d748de3f586 @loicrouchon loicrouchon committed
View
3 build.xml
@@ -216,7 +216,8 @@
<include name="ceylon/language/identityHash_.java"/>
<exclude name="ceylon/language/Integer.ceylon"/>
<include name="ceylon/language/Integer.java"/>
- <include name="ceylon/language/parseInteger_.java"/>
+ <include name="ceylon/language/minIntegerValue_.java"/>
+ <include name="ceylon/language/maxIntegerValue_.java"/>
<exclude name="ceylon/language/language.ceylon"/>
<include name="ceylon/language/language_.java"/>
<exclude name="ceylon/language/process.ceylon"/>
View
15 runtime/ceylon/language/maxIntegerValue_.java
@@ -0,0 +1,15 @@
+package ceylon.language;
+
+import com.redhat.ceylon.compiler.java.metadata.Ceylon;
+import com.redhat.ceylon.compiler.java.metadata.Attribute;
+import com.redhat.ceylon.compiler.java.metadata.TypeInfo;
+
+@Ceylon(major = 4)
+@Attribute
+public final class maxIntegerValue_ {
+
+ @TypeInfo("ceylon.language::Integer")
+ public static Integer getMaxIntegerValue$() {
+ return Integer.instance(Long.MAX_VALUE);
+ }
+}
View
15 runtime/ceylon/language/minIntegerValue_.java
@@ -0,0 +1,15 @@
+package ceylon.language;
+
+import com.redhat.ceylon.compiler.java.metadata.Ceylon;
+import com.redhat.ceylon.compiler.java.metadata.Attribute;
+import com.redhat.ceylon.compiler.java.metadata.TypeInfo;
+
+@Ceylon(major = 4)
+@Attribute
+public final class minIntegerValue_ {
+
+ @TypeInfo("ceylon.language::Integer")
+ public static Integer getMinIntegerValue$() {
+ return Integer.instance(Long.MIN_VALUE);
+ }
+}
View
116 runtime/ceylon/language/parseInteger_.java
@@ -1,116 +0,0 @@
-package ceylon.language;
-
-import com.redhat.ceylon.compiler.java.metadata.Ceylon;
-import com.redhat.ceylon.compiler.java.metadata.Method;
-import com.redhat.ceylon.compiler.java.metadata.Name;
-import com.redhat.ceylon.compiler.java.metadata.TypeInfo;
-
-@Ceylon(major = 5)
-@Method
-public final class parseInteger_
-{
-
- @TypeInfo("ceylon.language::Null|ceylon.language::Integer")
- public static Integer parseInteger(@Name("string") java.lang.String string) {
- final int length = string.length();
- if (length == 0) {
- return null;
- }
- long result = 0;
- boolean negative = false;
- int ii = 0;
- long limit = -Long.MAX_VALUE;
- long max = Long.MIN_VALUE / 10;
-
- // The sign
- char ch = string.charAt(ii);
- if (ch == '-') {
- negative = true;
- ii++;
- limit = Long.MIN_VALUE;
- } else if (ch == '+') {
- ii++;
- }
-
- // The actual number
- int sep = -1;
- int digitIndex = 0;
- loop: for (; ii < length; ii++, digitIndex++) {
- ch = string.charAt(ii);
- switch(ch) {
- case '_':
- if (sep != -1 && (digitIndex - sep) % 4 != 0) {//
- return null;
- }
- if (sep == -1) {// at most three digits before the first _
- if (digitIndex > 3) {
- return null;
- }
- sep = digitIndex;
- }
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (sep != -1 &&
- (digitIndex - sep) % 4 == 0) {// missing a _ after the first
- return null;
- }
- int digit = java.lang.Character.digit(ch, 10);
- if (result < max) {// overflow
- return null;
- }
- result *= 10;
- if (result < limit + digit) {
- return null;
- }
- // += would be much more obvious, but it doesn't work for Long.MIN_VALUE
- result -= digit;
- break;
- default: // non-digit, so not a number
- break loop;
- }
- }
-
- // check for insufficient digits after the last _
- if (sep != -1
- && ((digitIndex - sep) % 4) != 0) {
- return null;
- }
-
- // The magnitude
- long factor = 1L;
- if (ii < length) {
- switch (string.charAt(ii++)) {
- case 'P':
- factor *= 1000L;
- case 'T':
- factor *= 1000L;
- case 'G':
- factor *= 1000L;
- case 'M':
- factor *= 1000L;
- case 'k':
- factor *= 1000L;
- break;
- default:
- return null;
- }
- }
-
- if (ii < length ||
- digitIndex == 0) {
- return null;
- }
-
- return Integer.instance(negative ? result*factor : -result*factor);
- }
- private parseInteger_(){}
-}
View
13 src/ceylon/language/Integer.ceylon
@@ -21,14 +21,5 @@ shared native final class Integer(Integer integer)
shared native Character character;
}
-"The `Integer` value of the given string representation
- of an integer, or `null` if the string does not represent
- an integer or if the mathematical integer it represents
- is too large in magnitude to be represented by an
- `Integer`.
-
- The syntax accepted by this method is the same as the
- syntax for an `Integer` literal in the Ceylon language
- except that it may optionally begin with a sign
- character (`+` or `-`)."
-shared native Integer? parseInteger(String string);
+shared native Integer maxIntegerValue;
+shared native Integer minIntegerValue;
View
178 src/ceylon/language/parseInteger.ceylon
@@ -0,0 +1,178 @@
+doc "The `Integer` value of the given string representation
+ of an integer, or `null` if the string does not represent
+ an integer or if the mathematical integer it represents
+ is too large in magnitude to be represented by an
+ `Integer`.
+
+ The syntax accepted by this method is the same as the
+ syntax for an `Integer` literal in the Ceylon language
+ except that it may optionally begin with a sign
+ character (`+` or `-`)."
+shared Integer? parseInteger(String string, Integer radix = 10) {
+ Integer length = string.size;
+ if (length == 0) {
+ return null;
+ }
+ variable Integer ii = 0;
+ variable Character ch;
+ if (exists char = string[ii]) {
+ ch = char;
+ } else {
+ return null;
+ }
+ variable Integer result = 0;
+ Integer max = minIntegerValue / radix;
+ Boolean negative;
+ if (ch == '-') {
+ negative = true;
+ ii++;
+ } else if (ch == '+') {
+ negative = false;
+ ii++;
+ } else {
+ negative = false;
+ }
+ Integer limit = negative then minIntegerValue else -maxIntegerValue;
+
+ // The actual number
+ variable Integer sep = -1;
+ variable Integer digitIndex = 0;
+ while (ii < length) {
+ if (exists char = string[ii]) {
+ ch = char;
+ } else {
+ return null;
+ }
+ if (ch == '_') {
+ if (sep != -1 && (digitIndex - sep) % 4 != 0) {
+ return null;
+ }
+ if (sep == -1) {
+ // at most three digits before the first _
+ if (digitIndex > 3) {
+ return null;
+ }
+ sep = digitIndex;
+ }
+ }
+ else {
+ if (sep != -1 && (digitIndex - sep) % 4 == 0) {
+ // missing a _ after the first
+ return null;
+ }
+ if (ii + 1 == length && radix == 10 && ch in ['k','M','G','T','P']) {
+ // base 10 factor
+ break;
+ }
+ Integer digit;
+ if (exists d = parseDigit(ch, radix)) {
+ digit = d;
+ } else { // Invalid digit
+ return null;
+ }
+ if (result < max) { // overflow
+ return null;
+ }
+ result *= radix;
+ if (result < limit + digit) { // overflow
+ return null;
+ }
+ // += would be much more obvious, but it doesn't work for minIntegerValue
+ result -= digit;
+ }
+ ii++;
+ digitIndex++;
+ }
+ // check for insufficient digits after the last _
+ if (sep != -1 && ((digitIndex - sep) % 4) != 0) {
+ return null;
+ }
+ // The magnitude
+ variable Integer magnitude;
+ if (negative) {
+ magnitude = 1;
+ } else {
+ magnitude= -1;
+ }
+ if (ii < length) {
+ if (radix != 10) {
+ return null;
+ }
+ Integer power;
+ if (exists char = string[ii++]) {
+ if (char == 'P') {
+ power = 15;
+ }
+ else if (char == 'T') {
+ power = 12;
+ }
+ else if (char == 'G') {
+ power = 9;
+ }
+ else if (char == 'M') {
+ power = 6;
+ }
+ else if (char == 'k') {
+ power = 3;
+ }
+ else {
+ return null;
+ }
+ }
+ else {
+ return null;
+ }
+ magnitude *= radix ^ power;
+ }
+ if (ii < length || digitIndex == 0) {
+ return null;
+ }
+ return result * magnitude;
+}
+
+Map<Character,Integer> digitCharacters = LazyMap {
+ '0' -> 0,
+ '1' -> 1,
+ '2' -> 2,
+ '3' -> 3,
+ '4' -> 4,
+ '5' -> 5,
+ '6' -> 6,
+ '7' -> 7,
+ '8' -> 8,
+ '9' -> 9,
+ 'a' -> 10,
+ 'b' -> 11,
+ 'c' -> 12,
+ 'd' -> 13,
+ 'e' -> 14,
+ 'f' -> 15,
+ 'g' -> 16,
+ 'h' -> 17,
+ 'i' -> 18,
+ 'j' -> 19,
+ 'k' -> 20,
+ 'l' -> 21,
+ 'm' -> 22,
+ 'n' -> 23,
+ 'o' -> 24,
+ 'p' -> 25,
+ 'q' -> 26,
+ 'r' -> 27,
+ 's' -> 28,
+ 't' -> 29,
+ 'u' -> 30,
+ 'v' -> 31,
+ 'w' -> 32,
+ 'x' -> 33,
+ 'y' -> 34,
+ 'z' -> 35
+};
+
+Integer? parseDigit(Character digit, Integer radix) {
+ Integer? figure = digitCharacters.get(digit.lowercased);
+ if (exists figure, figure < radix) {
+ return figure;
+ }
+ return null;
+}
View
99 test-jvm/ceylon/language/IntegerTest.java
@@ -9,102 +9,7 @@
public class IntegerTest {
- private void assertParseInteger(Long expect, java.lang.String str) {
- Integer parsed = parseInteger(str);
- if (expect != null) {
- if (parsed == null) {
- fail(str + " didn't parse as an Integer");
- }
- assertEquals("parseInteger(" + str + ")" + parsed.longValue(),
- expect.longValue(), parsed.longValue());
- } else {
- assertNull("parseInteger(" + str + ") returned a result " + parsed, parsed);
- }
- }
-
- @Test
- public void testParseInteger() {
- assertParseInteger(0L, "0");
- assertParseInteger(1000L, "1_000");
- assertParseInteger(1000L, "1000");
- assertParseInteger(1000L, "1k");
- assertParseInteger(1000L, "+1_000");
- assertParseInteger(1000L, "+1000");
- assertParseInteger(1000L, "+1k");
- assertParseInteger(-1000L, "-1_000");
- assertParseInteger(-1000L, "-1000");
- assertParseInteger(-1000L, "-1k");
-
- assertParseInteger(0L, "0");
- assertParseInteger(0L, "00");
- assertParseInteger(0L, "0_000");
- assertParseInteger(0L, "-00");
- assertParseInteger(0L, "+00");
- assertParseInteger(0L, "0k");
-
- assertParseInteger(1L, "1");
- assertParseInteger(1L, "01");
- assertParseInteger(1L, "0_001");
- assertParseInteger(1L, "+1");
- assertParseInteger(1L, "+01");
- assertParseInteger(1L, "+0_001");
-
- assertParseInteger(-1L, "-1");
- assertParseInteger(-1L, "-01");
- assertParseInteger(-1L, "-0_001");
-
- assertParseInteger(1000L, "1k");
- assertParseInteger(1000000L, "1M");
- assertParseInteger(1000000000L, "1G");
- assertParseInteger(1000000000000L, "1T");
- assertParseInteger(1000000000000000L, "1P");
- assertParseInteger(-1000L, "-1k");
- assertParseInteger(-1000000L, "-1M");
- assertParseInteger(-1000000000L, "-1G");
- assertParseInteger(-1000000000000L, "-1T");
- assertParseInteger(-1000000000000000L, "-1P");
-
- assertParseInteger(9223372036854775807L, "9223372036854775807");
- assertParseInteger(9223372036854775807L, "9_223_372_036_854_775_807");
- assertParseInteger(-9223372036854775808L, "-9223372036854775808");
- assertParseInteger(-9223372036854775808L, "-9_223_372_036_854_775_808");
-
-
- assertParseInteger(null, "");
- assertParseInteger(null, "_");
- assertParseInteger(null, "+");
- assertParseInteger(null, "+_");
- assertParseInteger(null, "-");
- assertParseInteger(null, "-_");
- assertParseInteger(null, "foo");
- assertParseInteger(null, " 0");
- assertParseInteger(null, "0 ");
- assertParseInteger(null, "0+0");
- assertParseInteger(null, "0-0");
- assertParseInteger(null, "0+");
- assertParseInteger(null, "0-");
- assertParseInteger(null, "k");
- assertParseInteger(null, "k1");
- assertParseInteger(null, "+k");
- assertParseInteger(null, "-k");
- assertParseInteger(null, "1kk");
- assertParseInteger(null, "0_");
- assertParseInteger(null, "_0");
- assertParseInteger(null, "0_0");
- assertParseInteger(null, "0_00");
- assertParseInteger(null, "0_0000");
- assertParseInteger(null, "0_000_0");
- assertParseInteger(null, "0000_000");
- assertParseInteger(null, "1_0");
- assertParseInteger(null, "!_00");
- assertParseInteger(null, "1_0000");
- assertParseInteger(null, "1_000_0");
- assertParseInteger(null, "1000_000");
- assertParseInteger(null, "9223372036854775808");
- assertParseInteger(null, "-9223372036854775809");
- }
-
- @Test
+ @Test
public void testConversionToInteger() {
assertEquals(0.0, Integer.instance(0).getFloat(), 0.0);
assertEquals(1.0, Integer.instance(1).getFloat(), 1.0);
@@ -124,7 +29,5 @@ public void testConversionToInteger() {
} catch (OverflowException e) {
// Checking that this is thrown
}
-
}
-
}

0 comments on commit 2544094

Please sign in to comment.
Something went wrong with that request. Please try again.