Skip to content

Commit dfcf31e

Browse files
committed
fix: properly write digit separators
1 parent 545955e commit dfcf31e

7 files changed

Lines changed: 151 additions & 7 deletions

File tree

src/main/java/de/marhali/json5/stream/Json5Writer.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public void writePrimitive(Json5Primitive primitive) throws IOException {
173173
writer.write(quote(instant.toString()));
174174
}
175175
} else if (primitive.isNumber()) {
176-
writer.append(formatNumberString(primitive.getAsString()));
176+
writer.append(formatNumberString(primitive.getAsString(), primitive.getNumberRadix()));
177177
} else {
178178
writer.append(primitive.getAsString());
179179
}
@@ -276,25 +276,49 @@ public void writeArray(Json5Array array, int depth) throws IOException {
276276

277277
writer.append(']');
278278
}
279-
280-
public String applyNumberSeparator(String numberString, char separator) {
279+
private String applyNumberSeparator(String numberString, int numberRadix, char separator) {
281280
StringBuilder sb = new StringBuilder(numberString);
281+
282+
boolean hasSign = numberString.startsWith("+") || numberString.startsWith("-");
283+
int offset, separatorOffset;
284+
285+
switch (numberRadix) {
286+
case 2:
287+
offset = hasSign ? 3 : 2;
288+
separatorOffset = 4;
289+
break;
290+
case 8:
291+
offset = hasSign ? 3 : 2;
292+
separatorOffset = 3;
293+
break;
294+
case 10:
295+
offset = 0;
296+
separatorOffset = 3;
297+
break;
298+
case 16:
299+
offset = hasSign ? 3 : 2;
300+
separatorOffset = 2;
301+
break;
302+
default:
303+
throw new IllegalArgumentException("Invalid number radix: " + numberRadix);
304+
}
305+
282306
int len = sb.length();
283307

284-
for (int i = len - 3; i > 0; i -= 3) {
308+
for (int i = len - separatorOffset; i > offset; i -= separatorOffset) {
285309
sb.insert(i, separator);
286310
}
287311

288312
return sb.toString();
289313
}
290314

291-
public String formatNumberString(String numberString) {
315+
public String formatNumberString(String numberString, int numberRadix) {
292316
var strategy = options.getDigitSeparatorStrategy();
293317
switch (strategy) {
294318
case JAVA_STYLE:
295-
return applyNumberSeparator(numberString, '_');
319+
return applyNumberSeparator(numberString, numberRadix, '_');
296320
case C_STYLE:
297-
return applyNumberSeparator(numberString, '\'');
321+
return applyNumberSeparator(numberString, numberRadix, '\'');
298322
default:
299323
return numberString;
300324
}

src/test/java/de/marhali/json5/e2e/roundtrips/ParserAndWriterTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,28 @@ void arrayRoundtripMinifyToPretty() throws IOException {
148148

149149
assertEquals(expectedStringifiedElement, stringifiedElement);
150150
}
151+
152+
@Test
153+
void extensionsRoundtripCStyle() throws IOException {
154+
var json5 = new Json5(Json5OptionsFixtures.EXTENSIONS_C_STYLE);
155+
156+
var element = json5.parse(TestResourceHelper.getTestResource("e2e/roundtrips/extensions-c-style.parser.json5"));
157+
var stringifiedElement = json5.serialize(element);
158+
159+
var expectedStringifiedElement = TestResourceHelper.getTestResourceContent("e2e/roundtrips/extensions-c-style.writer.json5");
160+
161+
assertEquals(expectedStringifiedElement, stringifiedElement);
162+
}
163+
164+
@Test
165+
void extensionsRoundtripJavaStyle() throws IOException {
166+
var json5 = new Json5(Json5OptionsFixtures.EXTENSIONS_JAVA_STYLE);
167+
168+
var element = json5.parse(TestResourceHelper.getTestResource("e2e/roundtrips/extensions-java-style.parser.json5"));
169+
var stringifiedElement = json5.serialize(element);
170+
171+
var expectedStringifiedElement = TestResourceHelper.getTestResourceContent("e2e/roundtrips/extensions-java-style.writer.json5");
172+
173+
assertEquals(expectedStringifiedElement, stringifiedElement);
174+
}
151175
}

src/test/java/de/marhali/json5/fixtures/Json5OptionsFixtures.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,40 @@ public class Json5OptionsFixtures {
7373
.duplicateKeyStrategy(DuplicateKeyStrategy.UNIQUE)
7474
.indentFactor(0)
7575
.build();
76+
77+
public static Json5Options EXTENSIONS_C_STYLE = Json5Options.builder()
78+
.allowNaN()
79+
.allowInfinity()
80+
.allowInvalidSurrogates()
81+
.allowBinaryLiterals()
82+
.allowOctalLiterals()
83+
.allowHexFloatingLiterals()
84+
.allowLongUnicodeEscapes()
85+
.quoteless()
86+
.parseComments()
87+
.writeComments()
88+
.trailingComma()
89+
.insertFinalNewline()
90+
.digitSeparatorStrategy(DigitSeparatorStrategy.C_STYLE)
91+
.duplicateKeyStrategy(DuplicateKeyStrategy.UNIQUE)
92+
.prettyPrinting()
93+
.build();
94+
95+
public static Json5Options EXTENSIONS_JAVA_STYLE = Json5Options.builder()
96+
.allowNaN()
97+
.allowInfinity()
98+
.allowInvalidSurrogates()
99+
.allowBinaryLiterals()
100+
.allowOctalLiterals()
101+
.allowHexFloatingLiterals()
102+
.allowLongUnicodeEscapes()
103+
.quoteless()
104+
.parseComments()
105+
.writeComments()
106+
.trailingComma()
107+
.insertFinalNewline()
108+
.digitSeparatorStrategy(DigitSeparatorStrategy.JAVA_STYLE)
109+
.duplicateKeyStrategy(DuplicateKeyStrategy.UNIQUE)
110+
.prettyPrinting()
111+
.build();
76112
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Number extensions with digit separator C-style
2+
{
3+
decimalNumber: 1234567,
4+
decimalNumberNegative: -11234567,
5+
decimalNumberPositive: +111234567,
6+
binaryNumber: 0b10101100,
7+
binaryNumberNegative: -0b101100,
8+
binaryNumberPositive: +0b1101100,
9+
octalNumber: 0o1234567,
10+
octalNumberNegative: -0o12345671,
11+
octalNumberPositive: +0o123456712,
12+
hexNumber: 0xDEADBEEF,
13+
hexNumberNegative: -0xADBEEFA,
14+
hexNumberPositive: +0xEADBEEFB
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Number extensions with digit separator C-style
2+
{
3+
decimalNumber: 1'234'567,
4+
decimalNumberNegative: -11'234'567,
5+
decimalNumberPositive: 111'234'567,
6+
binaryNumber: 0b1010'1100,
7+
binaryNumberNegative: -0b10'1100,
8+
binaryNumberPositive: 0b110'1100,
9+
octalNumber: 0o1'234'567,
10+
octalNumberNegative: -0o12'345'671,
11+
octalNumberPositive: 0o123'456'712,
12+
hexNumber: 0xde'ad'be'ef,
13+
hexNumberNegative: -0xa'db'ee'fa,
14+
hexNumberPositive: 0xea'db'ee'fb,
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Number extensions with digit separator Java-style
2+
{
3+
decimalNumber: 1234567,
4+
decimalNumberNegative: -11234567,
5+
decimalNumberPositive: +111234567,
6+
binaryNumber: 0b10101100,
7+
binaryNumberNegative: -0b101100,
8+
binaryNumberPositive: +0b1101100,
9+
octalNumber: 0o1234567,
10+
octalNumberNegative: -0o12345671,
11+
octalNumberPositive: +0o123456712,
12+
hexNumber: 0xDEADBEEF,
13+
hexNumberNegative: -0xADBEEFA,
14+
hexNumberPositive: +0xEADBEEFB
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Number extensions with digit separator Java-style
2+
{
3+
decimalNumber: 1_234_567,
4+
decimalNumberNegative: -11_234_567,
5+
decimalNumberPositive: 111_234_567,
6+
binaryNumber: 0b1010_1100,
7+
binaryNumberNegative: -0b10_1100,
8+
binaryNumberPositive: 0b110_1100,
9+
octalNumber: 0o1_234_567,
10+
octalNumberNegative: -0o12_345_671,
11+
octalNumberPositive: 0o123_456_712,
12+
hexNumber: 0xde_ad_be_ef,
13+
hexNumberNegative: -0xa_db_ee_fa,
14+
hexNumberPositive: 0xea_db_ee_fb,
15+
}

0 commit comments

Comments
 (0)