Make handling of negative numbers in record-id-ids more consistent. (#3633)
This commit is contained in:
parent
08fa85b3ab
commit
0e2f83ed9d
3 changed files with 165 additions and 4 deletions
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'";
|
||||
|
|
Loading…
Reference in a new issue