diff --git a/src/main/java/xjs/data/JsonLiteral.java b/src/main/java/xjs/data/JsonLiteral.java index a7e175d..be4bf03 100644 --- a/src/main/java/xjs/data/JsonLiteral.java +++ b/src/main/java/xjs/data/JsonLiteral.java @@ -57,11 +57,11 @@ public boolean isNull() { @Override public boolean asBoolean() { - switch (this.value) { - case TRUE: return true; - case FALSE: return false; - default: return super.asBoolean(); - } + return switch (this.value) { + case TRUE -> true; + case FALSE -> false; + default -> super.asBoolean(); + }; } @Override diff --git a/src/main/java/xjs/data/serialization/parser/DjsParser.java b/src/main/java/xjs/data/serialization/parser/DjsParser.java index 040a512..9b1e38d 100644 --- a/src/main/java/xjs/data/serialization/parser/DjsParser.java +++ b/src/main/java/xjs/data/serialization/parser/DjsParser.java @@ -6,6 +6,7 @@ import xjs.data.Json; import xjs.data.JsonArray; import xjs.data.JsonLiteral; +import xjs.data.JsonNumber; import xjs.data.JsonObject; import xjs.data.JsonString; import xjs.data.JsonValue; @@ -257,6 +258,8 @@ protected JsonValue readSingle() { } final String text = t.parsed(); return switch (text) { + case "infinity" -> new JsonNumber(Double.POSITIVE_INFINITY); + case "-infinity" -> new JsonNumber(Double.NEGATIVE_INFINITY); case "true" -> JsonLiteral.jsonTrue(); case "false" -> JsonLiteral.jsonFalse(); case "null" -> JsonLiteral.jsonNull(); diff --git a/src/main/java/xjs/data/serialization/token/Tokenizer.java b/src/main/java/xjs/data/serialization/token/Tokenizer.java index 9ce828c..0e9f654 100644 --- a/src/main/java/xjs/data/serialization/token/Tokenizer.java +++ b/src/main/java/xjs/data/serialization/token/Tokenizer.java @@ -214,6 +214,9 @@ protected Token number() throws IOException { } else if (reader.current == '-') { reader.read(); if (!reader.isDigit()) { + if (reader.readInfinity()) { + return this.newNumberToken(reader.endCapture(), Double.NEGATIVE_INFINITY); + } reader.invalidateCapture(); return this.newSymbolToken('-'); } diff --git a/src/main/java/xjs/data/serialization/util/PositionTrackingReader.java b/src/main/java/xjs/data/serialization/util/PositionTrackingReader.java index e9808cb..72ffbe2 100644 --- a/src/main/java/xjs/data/serialization/util/PositionTrackingReader.java +++ b/src/main/java/xjs/data/serialization/util/PositionTrackingReader.java @@ -400,6 +400,29 @@ public boolean readDigit() throws IOException { return true; } + /** + * Reads the string "infinity" from the current index. If + * the reader encounters a syntax error, a {@link SyntaxException} + * will be thrown. + * + * @return true, if the content from the current index is "infinity". + * @throws IOException If the underlying reader throws an exception. + */ + public boolean readInfinity() throws IOException { + char[] infinity = new char[]{'i', 'n', 'f', 'i', 'n', 'i', 't', 'y'}; + for (char c : infinity) { + if (!this.readIf(c)) { + return false; + } + } + + // check if it is exactly "infinity", not "infinityy" or similar + if (this.readIf('y')) { + throw this.unexpected('y'); + } + return true; + } + /** * Reads all possible digits from the current point. * diff --git a/src/main/java/xjs/data/serialization/writer/ElementWriter.java b/src/main/java/xjs/data/serialization/writer/ElementWriter.java index ec9f42e..da8bdd2 100644 --- a/src/main/java/xjs/data/serialization/writer/ElementWriter.java +++ b/src/main/java/xjs/data/serialization/writer/ElementWriter.java @@ -477,7 +477,14 @@ protected void writeNumber(final double decimal) throws IOException { this.tw.write(Long.toString(integer)); return; } - String res = BigDecimal.valueOf(decimal).toEngineeringString(); + String res; + if (decimal == Double.POSITIVE_INFINITY) { + res = "infinity"; + } else if (decimal == Double.NEGATIVE_INFINITY) { + res = "-infinity"; + } else { + res = BigDecimal.valueOf(decimal).toEngineeringString(); + } if (res.endsWith(".0")) { res = res.substring(0, res.length() - 2); } else if (res.contains("E")) { @@ -525,15 +532,15 @@ protected static String doEscapeString( if (c == quote) { return "\\" + quote; } - switch (c) { - case '\t': return "\\t"; - case '\n': return "\\n"; - case '\r': return "\\r"; - case '\f': return "\\f"; - case '\b': return "\\b"; - case '\\': return "\\\\"; - default: return null; - } + return switch (c) { + case '\t' -> "\\t"; + case '\n' -> "\\n"; + case '\r' -> "\\r"; + case '\f' -> "\\f"; + case '\b' -> "\\b"; + case '\\' -> "\\\\"; + default -> null; + }; } protected void writeMulti(final String value) throws IOException { diff --git a/src/main/java/xjs/data/serialization/writer/JsonWriter.java b/src/main/java/xjs/data/serialization/writer/JsonWriter.java index 8c27895..4d87fe5 100644 --- a/src/main/java/xjs/data/serialization/writer/JsonWriter.java +++ b/src/main/java/xjs/data/serialization/writer/JsonWriter.java @@ -34,20 +34,11 @@ protected void writeValue() throws IOException { final JsonValue value = this.current(); switch (value.getType()) { - case OBJECT: - this.writeObject(); - break; - case ARRAY: - this.writeArray(); - break; - case NUMBER: - this.writeNumber(value.asDouble()); - break; - case STRING: - this.writeQuoted(value.asString(), '"'); - break; - default: - this.tw.write(value.toString()); + case OBJECT -> this.writeObject(); + case ARRAY -> this.writeArray(); + case NUMBER -> this.writeNumber(value.asDouble()); + case STRING -> this.writeQuoted(value.asString(), '"'); + default -> this.tw.write(value.toString()); } } diff --git a/src/test/java/xjs/data/serialization/parser/DjsParserTest.java b/src/test/java/xjs/data/serialization/parser/DjsParserTest.java index df2cd0c..968ea5b 100644 --- a/src/test/java/xjs/data/serialization/parser/DjsParserTest.java +++ b/src/test/java/xjs/data/serialization/parser/DjsParserTest.java @@ -37,6 +37,14 @@ public void parse_readsMultipleUnquotedKeys() { .matches(this.parse("{k1:'v1',k2:'v2'}"))); } + @Test + public void parse_readsInfiniteNumbers() { + assertEquals(Double.POSITIVE_INFINITY, this.parse("infinity").asDouble()); + assertEquals(Double.NEGATIVE_INFINITY, this.parse("-infinity").asDouble()); + + assertThrows(SyntaxException.class, () -> this.parse("-infinityy")); + } + @Test public void parse_readsOpenRoot() { assertTrue(new JsonObject().add("a", 1).add("b", 2) diff --git a/src/test/java/xjs/data/serialization/writer/DjsWriterTest.java b/src/test/java/xjs/data/serialization/writer/DjsWriterTest.java index 4fd6326..954b636 100644 --- a/src/test/java/xjs/data/serialization/writer/DjsWriterTest.java +++ b/src/test/java/xjs/data/serialization/writer/DjsWriterTest.java @@ -51,6 +51,12 @@ public void write_printsInteger() { assertEquals("1234", write(Json.value(1234))); } + @Test + public void write_printsInfiniteNumbers() { + assertEquals("infinity", write(Json.value(Double.POSITIVE_INFINITY))); + assertEquals("-infinity", write(Json.value(Double.NEGATIVE_INFINITY))); + } + @Test public void write_printsDecimal() { assertEquals("12.34", write(Json.value(12.34)));