aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShevek <[email protected]>2014-01-17 22:01:07 -0800
committerShevek <[email protected]>2014-01-17 22:01:07 -0800
commitca42036955f2589d034a688267eb81c5da2c824d (patch)
treebba007070561d64ce2b48f82b2bcf075a4631878
parent5e8bc477e233588204cdea0e8a3dafedc4b66de2 (diff)
Fix lots of NumericValue issues - probably more to go.
-rw-r--r--gradle/check.gradle7
-rw-r--r--src/main/ghpages/index.html6
-rw-r--r--src/main/java/org/anarres/cpp/LexerSource.java85
-rw-r--r--src/main/java/org/anarres/cpp/NumericValue.java29
-rw-r--r--src/main/java/org/anarres/cpp/Preprocessor.java14
-rw-r--r--src/test/java/org/anarres/cpp/NumericValueTest.java77
6 files changed, 182 insertions, 36 deletions
diff --git a/gradle/check.gradle b/gradle/check.gradle
index 57068f7..760629a 100644
--- a/gradle/check.gradle
+++ b/gradle/check.gradle
@@ -20,3 +20,10 @@ apply plugin: 'cobertura'
cobertura {
coverageFormats = [ 'html', 'xml' ]
}
+githubPages {
+ pages {
+ from(cobertura.coverageReportDir) {
+ into "docs/cobertura"
+ }
+ }
+}
diff --git a/src/main/ghpages/index.html b/src/main/ghpages/index.html
new file mode 100644
index 0000000..32292c3
--- /dev/null
+++ b/src/main/ghpages/index.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+<a href="docs/javadoc/">Javadoc</a>
+<a href="docs/cobertura/">Coverage</a>
+</body>
+</html>
diff --git a/src/main/java/org/anarres/cpp/LexerSource.java b/src/main/java/org/anarres/cpp/LexerSource.java
index 5f1dac3..bdb5f27 100644
--- a/src/main/java/org/anarres/cpp/LexerSource.java
+++ b/src/main/java/org/anarres/cpp/LexerSource.java
@@ -542,32 +542,40 @@ public class LexerSource extends Source {
/* We already chewed a zero, so empty is fine. */
@Nonnull
- private Token number_octal()
+ private Token number_octal(boolean negative)
throws IOException,
LexerException {
- StringBuilder text = new StringBuilder("0");
+ StringBuilder text = new StringBuilder(negative ? "-0" : "0");
String integer = _number_part(text, 8);
+ NumericValue value = new NumericValue(8, negative, integer);
int d = read();
- NumericValue value = new NumericValue(8, integer);
+ if (d == '.') {
+ text.append((char) d);
+ String fraction = _number_part(text, 16);
+ value.setFractionalPart(fraction);
+ d = read();
+ }
return _number_suffix(text, value, d);
}
/* We do not know whether know the first digit is valid. */
@Nonnull
- private Token number_hex(char x)
+ private Token number_hex(char x, boolean negative)
throws IOException,
LexerException {
- StringBuilder text = new StringBuilder("0");
+ StringBuilder text = new StringBuilder(negative ? "-0" : "0");
text.append(x);
String integer = _number_part(text, 16);
- NumericValue value = new NumericValue(16, integer);
+ NumericValue value = new NumericValue(16, negative, integer);
int d = read();
if (d == '.') {
+ text.append((char) d);
String fraction = _number_part(text, 16);
value.setFractionalPart(fraction);
d = read();
}
if (d == 'P' || d == 'p') {
+ text.append((char) d);
String exponent = _number_part(text, 10);
value.setExponent(exponent);
d = read();
@@ -579,12 +587,12 @@ public class LexerSource extends Source {
/* We know we have at least one valid digit, but empty is not
* fine. */
@Nonnull
- private Token number_decimal()
+ private Token number_decimal(boolean negative)
throws IOException,
LexerException {
- StringBuilder text = new StringBuilder();
+ StringBuilder text = new StringBuilder(negative ? "-" : "");
String integer = _number_part(text, 10);
- NumericValue value = new NumericValue(10, integer);
+ NumericValue value = new NumericValue(10, negative, integer);
int d = read();
if (d == '.') {
text.append((char) d);
@@ -603,6 +611,41 @@ public class LexerSource extends Source {
}
@Nonnull
+ private Token number()
+ throws IOException,
+ LexerException {
+ boolean negative = false;
+ Token tok;
+ int c = read();
+ if (c == '-') {
+ negative = true;
+ c = read();
+ }
+ if (c == '0') {
+ int d = read();
+ if (d == 'x' || d == 'X') {
+ tok = number_hex((char) d, negative);
+ } else if (d == '.') {
+ unread(d);
+ unread(c);
+ tok = number_decimal(negative);
+ } else {
+ unread(d);
+ tok = number_octal(negative);
+ }
+ } else if (Character.isDigit(c)) {
+ unread(c);
+ tok = number_decimal(negative);
+ } else if (c == '.') {
+ unread(c);
+ tok = number_decimal(negative);
+ } else {
+ throw new LexerException("Asked to parse something as a number which isn't: " + (char) c);
+ }
+ return tok;
+ }
+
+ @Nonnull
private Token identifier(int c)
throws IOException,
LexerException {
@@ -722,6 +765,10 @@ public class LexerSource extends Source {
tok = new Token(ARROW);
else
unread(d);
+ if (Character.isDigit(d)) {
+ unread('-');
+ tok = number();
+ }
break;
case '*':
@@ -839,26 +886,11 @@ public class LexerSource extends Source {
unread(d);
if (Character.isDigit(d)) {
unread('.');
- tok = number_decimal();
+ tok = number();
}
/* XXX decimal fraction */
break;
- case '0':
- /* octal or hex */
- d = read();
- if (d == 'x' || d == 'X')
- tok = number_hex((char) d);
- else if (d == '.') {
- unread(d);
- unread(c);
- tok = number_decimal();
- } else {
- unread(d);
- tok = number_octal();
- }
- break;
-
case '\'':
tok = string('\'', '\'');
break;
@@ -878,7 +910,7 @@ public class LexerSource extends Source {
tok = whitespace(c);
} else if (Character.isDigit(c)) {
unread(c);
- tok = number_decimal();
+ tok = number();
} else if (Character.isJavaIdentifierStart(c)) {
tok = identifier(c);
} else {
@@ -904,6 +936,7 @@ public class LexerSource extends Source {
return tok;
}
+ @Override
public void close()
throws IOException {
if (reader != null) {
diff --git a/src/main/java/org/anarres/cpp/NumericValue.java b/src/main/java/org/anarres/cpp/NumericValue.java
index 8d961c4..ad0bfc0 100644
--- a/src/main/java/org/anarres/cpp/NumericValue.java
+++ b/src/main/java/org/anarres/cpp/NumericValue.java
@@ -18,6 +18,9 @@ package org.anarres.cpp;
import java.math.BigDecimal;
import java.math.BigInteger;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
public class NumericValue extends Number {
@@ -31,24 +34,33 @@ public class NumericValue extends Number {
public static final int FF_SIZE = F_INT | F_LONG | F_LONGLONG | F_FLOAT | F_DOUBLE;
private final int base;
+ private final boolean negative;
private final String integer;
private String fraction;
private String exponent;
private int flags;
- public NumericValue(int base, String integer) {
+ public NumericValue(int base, boolean negative, String integer) {
this.base = base;
+ this.negative = negative;
this.integer = integer;
}
+ @Nonnegative
public int getBase() {
return base;
}
+ public boolean isNegative() {
+ return negative;
+ }
+
+ @Nonnull
public String getIntegerPart() {
return integer;
}
+ @CheckForNull
public String getFractionalPart() {
return fraction;
}
@@ -57,6 +69,7 @@ public class NumericValue extends Number {
this.fraction = fraction;
}
+ @CheckForNull
public String getExponent() {
return exponent;
}
@@ -78,6 +91,7 @@ public class NumericValue extends Number {
* precision numbers is nontrivial, and this routine gets it wrong
* in many important cases.
*/
+ @Nonnull
public BigDecimal toBigDecimal() {
int scale = 0;
String text = getIntegerPart();
@@ -93,6 +107,7 @@ public class NumericValue extends Number {
return new BigDecimal(unscaled, scale);
}
+ @Nonnull
public Number toJavaLangNumber() {
int flags = getFlags();
if ((flags & F_DOUBLE) != 0)
@@ -113,21 +128,27 @@ public class NumericValue extends Number {
@Override
public int intValue() {
- return Integer.parseInt(toString());
+ int v = integer.isEmpty() ? 0 : Integer.parseInt(integer, base);
+ return isNegative() ? -v : v;
}
@Override
public long longValue() {
- return Long.parseLong(toString());
+ long v = integer.isEmpty() ? 0 : Long.parseLong(integer, base);
+ return isNegative() ? -v : v;
}
@Override
public float floatValue() {
+ if (getBase() != 10)
+ return longValue();
return Float.parseFloat(toString());
}
@Override
public double doubleValue() {
+ if (getBase() != 10)
+ return longValue();
return Double.parseDouble(toString());
}
@@ -141,6 +162,8 @@ public class NumericValue extends Number {
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
+ if (isNegative())
+ buf.append('-');
switch (base) {
case 8:
buf.append('0');
diff --git a/src/main/java/org/anarres/cpp/Preprocessor.java b/src/main/java/org/anarres/cpp/Preprocessor.java
index e4ecdc1..cb7c633 100644
--- a/src/main/java/org/anarres/cpp/Preprocessor.java
+++ b/src/main/java/org/anarres/cpp/Preprocessor.java
@@ -787,8 +787,8 @@ public class Preprocessor implements Closeable {
push_source(new FixedTokenSource(
new Token[]{new Token(NUMBER,
orig.getLine(), orig.getColumn(),
- String.valueOf(orig.getLine()),
- new NumericValue(10, "" + orig.getLine()))}
+ Integer.toString(orig.getLine()),
+ new NumericValue(10, false, Integer.toString(orig.getLine())))}
), true);
} else if (m == __FILE__) {
StringBuilder buf = new StringBuilder("\"");
@@ -823,8 +823,8 @@ public class Preprocessor implements Closeable {
push_source(new FixedTokenSource(
new Token[]{new Token(NUMBER,
orig.getLine(), orig.getColumn(),
- String.valueOf(value),
- new NumericValue(10, "" + value))}
+ Integer.toString(value),
+ new NumericValue(10, false, Integer.toString(value)))}
), true);
} else {
push_source(new MacroTokenSource(m, args), true);
@@ -1388,17 +1388,17 @@ public class Preprocessor implements Closeable {
+ la.getText());
tok = new Token(NUMBER,
la.getLine(), la.getColumn(),
- "0", new NumericValue(10, "0"));
+ "0", new NumericValue(10, false, "0"));
} else if (macros.containsKey(la.getText())) {
// System.out.println("Found macro");
tok = new Token(NUMBER,
la.getLine(), la.getColumn(),
- "1", new NumericValue(10, "1"));
+ "1", new NumericValue(10, false, "1"));
} else {
// System.out.println("Not found macro");
tok = new Token(NUMBER,
la.getLine(), la.getColumn(),
- "0", new NumericValue(10, "0"));
+ "0", new NumericValue(10, false, "0"));
}
if (paren) {
diff --git a/src/test/java/org/anarres/cpp/NumericValueTest.java b/src/test/java/org/anarres/cpp/NumericValueTest.java
new file mode 100644
index 0000000..847be79
--- /dev/null
+++ b/src/test/java/org/anarres/cpp/NumericValueTest.java
@@ -0,0 +1,77 @@
+package org.anarres.cpp;
+
+import java.io.IOException;
+import org.junit.Test;
+import static org.anarres.cpp.Token.*;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author shevek
+ */
+public class NumericValueTest {
+
+ private Token testNumericValue(String in) throws IOException, LexerException {
+ StringLexerSource s = new StringLexerSource(in);
+
+ Token tok = s.token();
+ System.out.println("Token is " + tok);
+ assertEquals(NUMBER, tok.getType());
+
+ Token eof = s.token();
+ assertEquals("Didn't get EOF, but " + tok, EOF, eof.getType());
+
+ return tok;
+ }
+
+ private void testNumericValue(String in, double out) throws IOException, LexerException {
+ System.out.println("Testing '" + in + "' -> " + out);
+ Token tok = testNumericValue(in);
+ assertEquals(in, tok.getText());
+ NumericValue value = (NumericValue) tok.getValue();
+ assertEquals(out, value.doubleValue(), 0.01d);
+ assertEquals((float) out, value.floatValue(), 0.01f);
+ assertEquals((long) out, value.longValue());
+ assertEquals((int) out, value.intValue());
+ }
+
+ @Test
+ public void testNumericValue() throws Exception {
+
+ // Zero
+ testNumericValue("0", 0);
+
+ // Decimal
+ testNumericValue("1", 1);
+ testNumericValue("1L", 1);
+ testNumericValue("12", 12);
+ testNumericValue("12L", 12);
+
+ // Hex
+ testNumericValue("0xf", 0xf);
+ testNumericValue("0xfL", 0xf);
+ testNumericValue("0x12", 0x12);
+ testNumericValue("0x12L", 0x12);
+
+ // Negative
+ testNumericValue("-0", 0);
+ testNumericValue("-1", -1);
+
+ // Negative hex
+ testNumericValue("-0x56", -0x56);
+ testNumericValue("-0x102", -0x102);
+
+ // Octal and negative octal
+ testNumericValue("0673", Integer.parseInt("673", 8));
+ testNumericValue("-0673", Integer.parseInt("-673", 8));
+
+ // Floating point
+ testNumericValue(".0", 0);
+ testNumericValue(".00", 0);
+ testNumericValue("0.", 0);
+ testNumericValue("0.0", 0);
+ testNumericValue("00.0", 0);
+ testNumericValue("00.", 0);
+
+ }
+}