diff options
-rw-r--r-- | gradle/check.gradle | 7 | ||||
-rw-r--r-- | src/main/ghpages/index.html | 6 | ||||
-rw-r--r-- | src/main/java/org/anarres/cpp/LexerSource.java | 85 | ||||
-rw-r--r-- | src/main/java/org/anarres/cpp/NumericValue.java | 29 | ||||
-rw-r--r-- | src/main/java/org/anarres/cpp/Preprocessor.java | 14 | ||||
-rw-r--r-- | src/test/java/org/anarres/cpp/NumericValueTest.java | 77 |
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); + + } +} |