Make handling of negative numbers in record-id-ids more consistent. (#3633)

This commit is contained in:
Mees Delzenne 2024-03-05 19:43:04 +01:00 committed by GitHub
parent 08fa85b3ab
commit 0e2f83ed9d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 165 additions and 4 deletions

View file

@ -9,7 +9,7 @@ use nom::{
branch::alt,
bytes::complete::tag,
character::complete::char,
combinator::{cut, map, value},
combinator::{cut, map, opt, value},
sequence::delimited,
Err, Parser,
};
@ -67,7 +67,24 @@ pub fn thing_raw(i: &str) -> IResult<&str, Thing> {
pub fn id(i: &str) -> IResult<&str, Id> {
alt((
map(integer, Id::Number),
map(ident_raw, Id::String),
map(
|i| {
let (i, _) = tag("+")(i)?;
ident_raw(i)
},
Id::String,
),
map(
|i| {
let (i, minus) = opt(tag("-"))(i)?;
let (i, mut res) = ident_raw(i)?;
if minus.is_some() {
res.insert(0, '-');
}
Ok((i, res))
},
Id::String,
),
map(object, Id::Object),
map(array, Id::Array),
))(i)
@ -243,6 +260,64 @@ mod tests {
assert_eq!("⟨100⟩", format!("{}", out));
}
#[test]
fn thing_integer_min() {
let sql = format!("test:{}", i64::MIN);
let res = thing(&sql);
let (_, out) = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(i64::MIN),
}
);
}
#[test]
fn thing_integer_max() {
let sql = format!("test:{}", i64::MAX);
let res = thing(&sql);
let (_, out) = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(i64::MAX),
}
);
}
#[test]
fn thing_integer_more_then_max() {
let max_str = format!("{}", (i64::MAX as u64) + 1);
let sql = format!("test:{}", max_str);
let res = thing(&sql);
let (_, out) = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(max_str),
}
);
}
#[test]
fn thing_integer_more_then_min() {
let min_str = format!("-{}", (i64::MAX as u64) + 2);
let sql = format!("test:{}", min_str);
let res = thing(&sql);
let (_, out) = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(min_str),
}
);
}
#[test]
fn id_either() {
let sql = "100test";

View file

@ -244,7 +244,7 @@ impl Lexer<'_> {
}
self.reader.next();
loop {
match dbg!(self.reader.peek()) {
match self.reader.peek() {
Some(x @ b'0'..=b'9') => {
self.reader.next();
self.scratch.push(x as char);

View file

@ -9,7 +9,7 @@ use crate::{
token::{t, NumberKind, TokenKind},
},
};
use std::ops::Bound;
use std::{cmp::Ordering, ops::Bound};
impl Parser<'_> {
pub fn parse_record_string(&mut self, double: bool) -> ParseResult<Thing> {
@ -208,6 +208,34 @@ impl Parser<'_> {
let array = self.parse_array(token.span)?;
Ok(Id::Array(array))
}
t!("+") => {
self.peek();
self.no_whitespace()?;
expected!(self, TokenKind::Number(NumberKind::Integer));
let text = self.lexer.string.take().unwrap();
if let Ok(number) = text.parse() {
Ok(Id::Number(number))
} else {
Ok(Id::String(text))
}
}
t!("-") => {
self.peek();
self.no_whitespace()?;
expected!(self, TokenKind::Number(NumberKind::Integer));
let text = self.lexer.string.take().unwrap();
if let Ok(number) = text.parse::<u64>() {
// Parse to u64 and check if the value is equal to `-i64::MIN` via u64 as
// `-i64::MIN` doesn't fit in an i64
match number.cmp(&((i64::MAX as u64) + 1)) {
Ordering::Less => Ok(Id::Number(-(number as i64))),
Ordering::Equal => Ok(Id::Number(i64::MIN)),
Ordering::Greater => Ok(Id::String(format!("-{}", text))),
}
} else {
Ok(Id::String(text))
}
}
TokenKind::Number(NumberKind::Integer) => {
// Id handle numbers more loose then other parts of the code.
// If number can't fit in a i64 it will instead be parsed as a string.
@ -285,6 +313,64 @@ mod tests {
);
}
#[test]
fn thing_integer_min() {
let sql = format!("test:{}", i64::MIN);
let res = thing(&sql);
let out = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(i64::MIN),
}
);
}
#[test]
fn thing_integer_max() {
let sql = format!("test:{}", i64::MAX);
let res = thing(&sql);
let out = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(i64::MAX),
}
);
}
#[test]
fn thing_integer_more_then_max() {
let max_str = format!("{}", (i64::MAX as u64) + 1);
let sql = format!("test:{}", max_str);
let res = thing(&sql);
let out = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(max_str),
}
);
}
#[test]
fn thing_integer_more_then_min() {
let min_str = format!("-{}", (i64::MAX as u64) + 2);
let sql = format!("test:{}", min_str);
let res = thing(&sql);
let out = res.unwrap();
assert_eq!(
out,
Thing {
tb: String::from("test"),
id: Id::from(min_str),
}
);
}
#[test]
fn thing_string() {
let sql = "r'test:001'";