Skip to content

Commit 00cb580

Browse files
authored
Merge pull request #600 from ron-rs/integer-suffix
Fix integer suffix parsing for non-decimal base
2 parents 567683c + 5203b3f commit 00cb580

3 files changed

Lines changed: 108 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## Unreleased
88

9+
### Format Changes
10+
911
- Ignore `#![type = "..."]` and `#![schema = "..."]` attributes ([#596](https://github.com/ron-rs/ron/pull/596))
1012

13+
### Bug Fixes
14+
15+
- Fixed parsing of integer type suffixes for non-decimal numbers ([#600](https://github.com/ron-rs/ron/pull/600))
16+
1117
## [0.12.0] - 2025-11-12
1218

1319
### API Changes

src/parse.rs

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,7 @@ impl<'a> Parser<'a> {
276276
Ok(num_acc)
277277
}
278278

279-
fn parse_integer<T: Num>(&mut self, sign: i8) -> Result<T> {
280-
let base = match () {
281-
() if self.consume_str("0b") => 2,
282-
() if self.consume_str("0o") => 8,
283-
() if self.consume_str("0x") => 16,
284-
() => 10,
285-
};
286-
279+
fn parse_integer<T: Num>(&mut self, sign: i8, base: u8) -> Result<T> {
287280
let num_bytes = self.next_chars_while_len(is_int_char);
288281

289282
if num_bytes == 0 {
@@ -344,6 +337,13 @@ impl<'a> Parser<'a> {
344337
};
345338
let sign = if is_negative { -1 } else { 1 };
346339

340+
let base = match () {
341+
() if self.consume_str("0b") => 2,
342+
() if self.consume_str("0o") => 8,
343+
() if self.consume_str("0x") => 16,
344+
() => 10,
345+
};
346+
347347
let num_bytes = self.next_chars_while_len(is_int_char);
348348

349349
if self.src()[num_bytes..].starts_with(['i', 'u']) {
@@ -356,56 +356,62 @@ impl<'a> Parser<'a> {
356356
let suffix_bytes = self.src();
357357
self.set_cursor(int_cursor);
358358
(
359-
self.parse_integer::<i8>(sign).map(ParsedInteger::I8),
359+
self.parse_integer::<i8>(sign, base).map(ParsedInteger::I8),
360360
suffix_bytes,
361361
)
362362
} else if self.consume_ident("i16") {
363363
let suffix_bytes = self.src();
364364
self.set_cursor(int_cursor);
365365
(
366-
self.parse_integer::<i16>(sign).map(ParsedInteger::I16),
366+
self.parse_integer::<i16>(sign, base)
367+
.map(ParsedInteger::I16),
367368
suffix_bytes,
368369
)
369370
} else if self.consume_ident("i32") {
370371
let suffix_bytes = self.src();
371372
self.set_cursor(int_cursor);
372373
(
373-
self.parse_integer::<i32>(sign).map(ParsedInteger::I32),
374+
self.parse_integer::<i32>(sign, base)
375+
.map(ParsedInteger::I32),
374376
suffix_bytes,
375377
)
376378
} else if self.consume_ident("i64") {
377379
let suffix_bytes = self.src();
378380
self.set_cursor(int_cursor);
379381
(
380-
self.parse_integer::<i64>(sign).map(ParsedInteger::I64),
382+
self.parse_integer::<i64>(sign, base)
383+
.map(ParsedInteger::I64),
381384
suffix_bytes,
382385
)
383386
} else if self.consume_ident("u8") {
384387
let suffix_bytes = self.src();
385388
self.set_cursor(int_cursor);
386389
(
387-
self.parse_integer::<u8>(sign).map(ParsedInteger::U8),
390+
self.parse_integer::<u8>(sign, base).map(ParsedInteger::U8),
388391
suffix_bytes,
389392
)
390393
} else if self.consume_ident("u16") {
391394
let suffix_bytes = self.src();
392395
self.set_cursor(int_cursor);
393396
(
394-
self.parse_integer::<u16>(sign).map(ParsedInteger::U16),
397+
self.parse_integer::<u16>(sign, base)
398+
.map(ParsedInteger::U16),
395399
suffix_bytes,
396400
)
397401
} else if self.consume_ident("u32") {
398402
let suffix_bytes = self.src();
399403
self.set_cursor(int_cursor);
400404
(
401-
self.parse_integer::<u32>(sign).map(ParsedInteger::U32),
405+
self.parse_integer::<u32>(sign, base)
406+
.map(ParsedInteger::U32),
402407
suffix_bytes,
403408
)
404409
} else if self.consume_ident("u64") {
405410
let suffix_bytes = self.src();
406411
self.set_cursor(int_cursor);
407412
(
408-
self.parse_integer::<u64>(sign).map(ParsedInteger::U64),
413+
self.parse_integer::<u64>(sign, base)
414+
.map(ParsedInteger::U64),
409415
suffix_bytes,
410416
)
411417
} else {
@@ -414,14 +420,16 @@ impl<'a> Parser<'a> {
414420
let suffix_bytes = self.src();
415421
self.set_cursor(int_cursor);
416422
(
417-
self.parse_integer::<i128>(sign).map(ParsedInteger::I128),
423+
self.parse_integer::<i128>(sign, base)
424+
.map(ParsedInteger::I128),
418425
suffix_bytes,
419426
)
420427
} else if self.consume_ident("u128") {
421428
let suffix_bytes = self.src();
422429
self.set_cursor(int_cursor);
423430
(
424-
self.parse_integer::<u128>(sign).map(ParsedInteger::U128),
431+
self.parse_integer::<u128>(sign, base)
432+
.map(ParsedInteger::U128),
425433
suffix_bytes,
426434
)
427435
} else {
@@ -449,7 +457,7 @@ impl<'a> Parser<'a> {
449457
self.set_cursor(int_cursor);
450458
}
451459

452-
T::parse(self, sign)
460+
T::parse(self, sign, base)
453461
}
454462

455463
pub fn any_number(&mut self) -> Result<Number> {
@@ -1461,16 +1469,16 @@ impl_num! { i8 i16 i32 i64 u8 u16 u32 u64 }
14611469
impl_num! { i128 u128 }
14621470

14631471
pub trait Integer: Sized {
1464-
fn parse(parser: &mut Parser, sign: i8) -> Result<Self>;
1472+
fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result<Self>;
14651473

14661474
fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result<Self>;
14671475
}
14681476

14691477
macro_rules! impl_integer {
14701478
($wrap:ident($ty:ty)) => {
14711479
impl Integer for $ty {
1472-
fn parse(parser: &mut Parser, sign: i8) -> Result<Self> {
1473-
parser.parse_integer(sign)
1480+
fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result<Self> {
1481+
parser.parse_integer(sign, base)
14741482
}
14751483

14761484
fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result<Self> {
@@ -1518,9 +1526,9 @@ pub enum ParsedInteger {
15181526
}
15191527

15201528
impl Integer for ParsedInteger {
1521-
fn parse(parser: &mut Parser, sign: i8) -> Result<Self> {
1529+
fn parse(parser: &mut Parser, sign: i8, base: u8) -> Result<Self> {
15221530
if sign < 0 {
1523-
let signed = parser.parse_integer::<LargeSInt>(-1)?;
1531+
let signed = parser.parse_integer::<LargeSInt>(-1, base)?;
15241532

15251533
return if let Ok(x) = i8::try_from(signed) {
15261534
Ok(ParsedInteger::I8(x))
@@ -1542,7 +1550,7 @@ impl Integer for ParsedInteger {
15421550
};
15431551
}
15441552

1545-
let unsigned = parser.parse_integer::<LargeUInt>(1)?;
1553+
let unsigned = parser.parse_integer::<LargeUInt>(1, base)?;
15461554

15471555
if let Ok(x) = u8::try_from(unsigned) {
15481556
Ok(ParsedInteger::U8(x))

tests/599_integer_suffix.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#[test]
2+
fn base_2() {
3+
assert_eq!(ron::from_str("-0b101010i32"), Ok(-0b101010i32));
4+
assert_eq!(ron::from_str("0b101010u32"), Ok(0b101010u32));
5+
assert_eq!(ron::from_str("-0b10_10_10i32"), Ok(-0b101010i32));
6+
assert_eq!(ron::from_str("0b101_010_u32"), Ok(0b101010u32));
7+
8+
assert_eq!(
9+
ron::from_str::<i32>("-0b101010i31"),
10+
Err(ron::error::SpannedError {
11+
code: ron::error::Error::TrailingCharacters,
12+
span: ron::error::Span {
13+
start: ron::error::Position { line: 1, col: 10 },
14+
end: ron::error::Position { line: 1, col: 10 }
15+
}
16+
})
17+
);
18+
}
19+
20+
#[test]
21+
fn base_8() {
22+
assert_eq!(ron::from_str("-0o52i32"), Ok(-0o52i32));
23+
assert_eq!(ron::from_str("0o52u32"), Ok(0o52u32));
24+
assert_eq!(ron::from_str("-0o52_i32"), Ok(-0o52i32));
25+
assert_eq!(ron::from_str("0o5_2u32"), Ok(0o52u32));
26+
27+
assert_eq!(
28+
ron::from_str::<i32>("0o_52_i32"),
29+
Err(ron::error::SpannedError {
30+
code: ron::error::Error::UnderscoreAtBeginning,
31+
span: ron::error::Span {
32+
start: ron::error::Position { line: 1, col: 7 },
33+
end: ron::error::Position {
34+
line: 1,
35+
col: 3, // FIXME
36+
}
37+
}
38+
})
39+
);
40+
}
41+
42+
#[test]
43+
fn base_10() {
44+
assert_eq!(ron::from_str("-42i32"), Ok(-42i32));
45+
assert_eq!(ron::from_str("42u32"), Ok(42u32));
46+
assert_eq!(ron::from_str("-42_i32"), Ok(-42i32));
47+
assert_eq!(ron::from_str("4_2u32"), Ok(42u32));
48+
49+
assert_eq!(ron::from_str("00i32"), Ok(0i32));
50+
}
51+
52+
#[test]
53+
fn base_16() {
54+
assert_eq!(ron::from_str("-0x2Ai32"), Ok(-0x2Ai32));
55+
assert_eq!(ron::from_str("0x2Au32"), Ok(0x2Au32));
56+
assert_eq!(ron::from_str("-0x2_Ai32"), Ok(-0x2Ai32));
57+
assert_eq!(ron::from_str("0x2A_u32"), Ok(0x2Au32));
58+
59+
assert_eq!(
60+
ron::from_str::<i32>("0x2Aj32"),
61+
Err(ron::error::SpannedError {
62+
code: ron::error::Error::TrailingCharacters,
63+
span: ron::error::Span {
64+
start: ron::error::Position { line: 1, col: 5 },
65+
end: ron::error::Position { line: 1, col: 5 }
66+
}
67+
})
68+
);
69+
}

0 commit comments

Comments
 (0)