Skip to content

Commit b71229f

Browse files
committed
uucore: parser: num_parser: Parse "0x"/"0b" as PartialMatch
printf treats "0x" as a partial match of 0 and "x".
1 parent 395da2e commit b71229f

File tree

1 file changed

+66
-25
lines changed

1 file changed

+66
-25
lines changed

src/uucore/src/lib/features/parser/num_parser.rs

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -450,9 +450,6 @@ pub(crate) fn parse<'a>(
450450
} else {
451451
(Base::Decimal, unsigned)
452452
};
453-
if rest.is_empty() {
454-
return Err(ExtendedParserError::NotNumeric);
455-
}
456453

457454
// Parse the integral part of the number
458455
let mut chars = rest.chars().enumerate().fuse().peekable();
@@ -518,6 +515,16 @@ pub(crate) fn parse<'a>(
518515

519516
// If no digit has been parsed, check if this is a special value, or declare the parsing unsuccessful
520517
if digits.is_none() {
518+
// If we trimmed an initial `0x`/`0b`, return a partial match.
519+
if rest != unsigned {
520+
let ebd = if negative {
521+
ExtendedBigDecimal::MinusZero
522+
} else {
523+
ExtendedBigDecimal::zero()
524+
};
525+
return Err(ExtendedParserError::PartialMatch(ebd, &unsigned[1..]));
526+
}
527+
521528
return if target == ParseTarget::Integral {
522529
Err(ExtendedParserError::NotNumeric)
523530
} else {
@@ -968,23 +975,41 @@ mod tests {
968975
))
969976
));
970977

971-
// TODO: GNU coreutils treats these as partial matches.
972-
assert_eq!(
973-
Err(ExtendedParserError::NotNumeric),
974-
ExtendedBigDecimal::extended_parse("0x")
975-
);
976-
assert_eq!(
977-
Err(ExtendedParserError::NotNumeric),
978-
ExtendedBigDecimal::extended_parse("0x.")
979-
);
980-
assert_eq!(
981-
Err(ExtendedParserError::NotNumeric),
982-
ExtendedBigDecimal::extended_parse("0xp")
983-
);
984-
assert_eq!(
985-
Err(ExtendedParserError::NotNumeric),
986-
ExtendedBigDecimal::extended_parse("0xp-2")
987-
);
978+
// Not actually hex numbers, but the prefixes look like it.
979+
assert!(matches!(f64::extended_parse("0x"),
980+
Err(ExtendedParserError::PartialMatch(f, "x")) if f == 0.0));
981+
assert!(matches!(f64::extended_parse("0x."),
982+
Err(ExtendedParserError::PartialMatch(f, "x.")) if f == 0.0));
983+
assert!(matches!(f64::extended_parse("0xp"),
984+
Err(ExtendedParserError::PartialMatch(f, "xp")) if f == 0.0));
985+
assert!(matches!(f64::extended_parse("0xp-2"),
986+
Err(ExtendedParserError::PartialMatch(f, "xp-2")) if f == 0.0));
987+
assert!(matches!(f64::extended_parse("0x.p-2"),
988+
Err(ExtendedParserError::PartialMatch(f, "x.p-2")) if f == 0.0));
989+
assert!(matches!(f64::extended_parse("0X"),
990+
Err(ExtendedParserError::PartialMatch(f, "X")) if f == 0.0));
991+
assert!(matches!(f64::extended_parse("-0x"),
992+
Err(ExtendedParserError::PartialMatch(f, "x")) if f == -0.0));
993+
assert!(matches!(f64::extended_parse("+0x"),
994+
Err(ExtendedParserError::PartialMatch(f, "x")) if f == 0.0));
995+
assert!(matches!(f64::extended_parse("-0x."),
996+
Err(ExtendedParserError::PartialMatch(f, "x.")) if f == -0.0));
997+
assert!(matches!(
998+
u64::extended_parse("0x"),
999+
Err(ExtendedParserError::PartialMatch(0, "x"))
1000+
));
1001+
assert!(matches!(
1002+
u64::extended_parse("-0x"),
1003+
Err(ExtendedParserError::PartialMatch(0, "x"))
1004+
));
1005+
assert!(matches!(
1006+
i64::extended_parse("0x"),
1007+
Err(ExtendedParserError::PartialMatch(0, "x"))
1008+
));
1009+
assert!(matches!(
1010+
i64::extended_parse("-0x"),
1011+
Err(ExtendedParserError::PartialMatch(0, "x"))
1012+
));
9881013
}
9891014

9901015
#[test]
@@ -1018,6 +1043,27 @@ mod tests {
10181043
assert_eq!(Ok(0b1011), u64::extended_parse("+0b1011"));
10191044
assert_eq!(Ok(-0b1011), i64::extended_parse("-0b1011"));
10201045

1046+
assert!(matches!(
1047+
u64::extended_parse("0b"),
1048+
Err(ExtendedParserError::PartialMatch(0, "b"))
1049+
));
1050+
assert!(matches!(
1051+
u64::extended_parse("0b."),
1052+
Err(ExtendedParserError::PartialMatch(0, "b."))
1053+
));
1054+
assert!(matches!(
1055+
u64::extended_parse("-0b"),
1056+
Err(ExtendedParserError::PartialMatch(0, "b"))
1057+
));
1058+
assert!(matches!(
1059+
i64::extended_parse("0b"),
1060+
Err(ExtendedParserError::PartialMatch(0, "b"))
1061+
));
1062+
assert!(matches!(
1063+
i64::extended_parse("-0b"),
1064+
Err(ExtendedParserError::PartialMatch(0, "b"))
1065+
));
1066+
10211067
// Binary not allowed for floats
10221068
assert!(matches!(
10231069
f64::extended_parse("0b100"),
@@ -1042,11 +1088,6 @@ mod tests {
10421088
Err(ExtendedParserError::PartialMatch(ebd, "b.")) => ebd == ExtendedBigDecimal::zero(),
10431089
_ => false,
10441090
});
1045-
// TODO: GNU coreutils treats this as partial match.
1046-
assert_eq!(
1047-
Err(ExtendedParserError::NotNumeric),
1048-
u64::extended_parse("0b")
1049-
);
10501091
}
10511092

10521093
#[test]

0 commit comments

Comments
 (0)