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,
|
branch::alt,
|
||||||
bytes::complete::tag,
|
bytes::complete::tag,
|
||||||
character::complete::char,
|
character::complete::char,
|
||||||
combinator::{cut, map, value},
|
combinator::{cut, map, opt, value},
|
||||||
sequence::delimited,
|
sequence::delimited,
|
||||||
Err, Parser,
|
Err, Parser,
|
||||||
};
|
};
|
||||||
|
@ -67,7 +67,24 @@ pub fn thing_raw(i: &str) -> IResult<&str, Thing> {
|
||||||
pub fn id(i: &str) -> IResult<&str, Id> {
|
pub fn id(i: &str) -> IResult<&str, Id> {
|
||||||
alt((
|
alt((
|
||||||
map(integer, Id::Number),
|
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(object, Id::Object),
|
||||||
map(array, Id::Array),
|
map(array, Id::Array),
|
||||||
))(i)
|
))(i)
|
||||||
|
@ -243,6 +260,64 @@ mod tests {
|
||||||
assert_eq!("⟨100⟩", format!("{}", out));
|
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]
|
#[test]
|
||||||
fn id_either() {
|
fn id_either() {
|
||||||
let sql = "100test";
|
let sql = "100test";
|
||||||
|
|
|
@ -244,7 +244,7 @@ impl Lexer<'_> {
|
||||||
}
|
}
|
||||||
self.reader.next();
|
self.reader.next();
|
||||||
loop {
|
loop {
|
||||||
match dbg!(self.reader.peek()) {
|
match self.reader.peek() {
|
||||||
Some(x @ b'0'..=b'9') => {
|
Some(x @ b'0'..=b'9') => {
|
||||||
self.reader.next();
|
self.reader.next();
|
||||||
self.scratch.push(x as char);
|
self.scratch.push(x as char);
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
token::{t, NumberKind, TokenKind},
|
token::{t, NumberKind, TokenKind},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::ops::Bound;
|
use std::{cmp::Ordering, ops::Bound};
|
||||||
|
|
||||||
impl Parser<'_> {
|
impl Parser<'_> {
|
||||||
pub fn parse_record_string(&mut self, double: bool) -> ParseResult<Thing> {
|
pub fn parse_record_string(&mut self, double: bool) -> ParseResult<Thing> {
|
||||||
|
@ -208,6 +208,34 @@ impl Parser<'_> {
|
||||||
let array = self.parse_array(token.span)?;
|
let array = self.parse_array(token.span)?;
|
||||||
Ok(Id::Array(array))
|
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) => {
|
TokenKind::Number(NumberKind::Integer) => {
|
||||||
// Id handle numbers more loose then other parts of the code.
|
// 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.
|
// 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]
|
#[test]
|
||||||
fn thing_string() {
|
fn thing_string() {
|
||||||
let sql = "r'test:001'";
|
let sql = "r'test:001'";
|
||||||
|
|
Loading…
Reference in a new issue