Change StringNumber to BigInteger

This commit is contained in:
Fabrice Reix 2023-12-14 17:57:52 +01:00 committed by hurl-bot
parent 1c24185aeb
commit 112d0e9457
No known key found for this signature in database
GPG Key ID: 1283A2B4A0DCAF8D
12 changed files with 202 additions and 323 deletions

View File

@ -68,7 +68,7 @@ impl Number {
Number::Float(f) => {
serde_json::Value::Number(serde_json::Number::from_f64(*f).unwrap())
}
Number::String(s) => {
Number::BigInteger(s) => {
let number = serde_json::Number::from_str(s).unwrap();
serde_json::Value::Number(number)
}

View File

@ -25,7 +25,7 @@ use std::fmt;
pub enum Number {
Float(f64),
Integer(i64),
String(String),
BigInteger(String),
}
// You must implement it yourself because of the Float
@ -34,7 +34,7 @@ impl PartialEq for Number {
match (self, other) {
(Number::Float(v1), Number::Float(v2)) => (v1 - v2).abs() < f64::EPSILON,
(Number::Integer(v1), Number::Integer(v2)) => v1 == v2,
(Number::String(v1), Number::String(v2)) => v1 == v2,
(Number::BigInteger(v1), Number::BigInteger(v2)) => v1 == v2,
_ => false,
}
}
@ -47,7 +47,7 @@ impl fmt::Display for Number {
let value = match self {
Number::Float(f) => format_float(*f),
Number::Integer(x) => x.to_string(),
Number::String(s) => s.to_string(),
Number::BigInteger(s) => s.to_string(),
};
write!(f, "{value}")
}
@ -66,7 +66,7 @@ impl Number {
match self {
Number::Float(_) => "float".to_string(),
Number::Integer(_) => "integer".to_string(),
Number::String(_) => "string".to_string(),
Number::BigInteger(_) => "string".to_string(),
}
}
}
@ -150,10 +150,13 @@ mod tests {
assert_eq!(Number::from(1.0).to_string(), "1.0".to_string());
assert_eq!(Number::from(1.1).to_string(), "1.1".to_string());
assert_eq!(
Number::String("1.1".to_string()).to_string(),
Number::BigInteger("1.1".to_string()).to_string(),
"1.1".to_string()
);
assert_eq!(Number::String("1".to_string()).to_string(), "1".to_string());
assert_eq!(
Number::BigInteger("1".to_string()).to_string(),
"1".to_string()
);
}
#[test]
@ -173,9 +176,9 @@ mod tests {
let float_min = Number::from(f64::MIN);
let float_max = Number::from(f64::MAX);
let number_one = Number::String("1".to_string());
let number_two = Number::String("2".to_string());
let number_two_with_decimal = Number::String("2.0".to_string());
let number_one = Number::BigInteger("1".to_string());
let number_two = Number::BigInteger("2".to_string());
let number_two_with_decimal = Number::BigInteger("2.0".to_string());
assert_eq!(integer_minus_one.cmp_value(&integer_zero), Ordering::Less);

View File

@ -19,8 +19,8 @@ use std::collections::HashMap;
use std::time::Duration;
use hurl_core::ast::{
BooleanOption, Entry, EntryOption, Float, NaturalOption, OptionKind, Retry, RetryOption,
SectionValue, VariableDefinition, VariableValue,
BooleanOption, Entry, EntryOption, Float, NaturalOption, Number as AstNumber, OptionKind,
Retry, RetryOption, SectionValue, VariableDefinition, VariableValue,
};
use crate::http::{IpResolve, RequestedHttpVersion};
@ -344,8 +344,7 @@ fn eval_variable_value(
match variable_value {
VariableValue::Null => Ok(Value::Null),
VariableValue::Bool(v) => Ok(Value::Bool(*v)),
VariableValue::Integer(v) => Ok(Value::Number(Number::Integer(*v))),
VariableValue::Float(Float { value, .. }) => Ok(Value::Number(Number::Float(*value))),
VariableValue::Number(v) => Ok(eval_number(v)),
VariableValue::String(template) => {
let s = template::eval_template(template, variables)?;
Ok(Value::String(s))
@ -353,6 +352,14 @@ fn eval_variable_value(
}
}
fn eval_number(number: &AstNumber) -> Value {
match number {
AstNumber::Float(Float { value, .. }) => Value::Number(Number::Float(*value)),
AstNumber::Integer(value) => Value::Number(Number::Integer(*value)),
AstNumber::BigInteger(value) => Value::Number(Number::BigInteger(value.clone())),
}
}
#[cfg(test)]
mod tests {
use hurl_core::ast::{Expr, Pos, SourceInfo, Variable, Whitespace};

View File

@ -132,7 +132,7 @@ impl Number {
match self {
Number::Float(f) => format!("float <{}>", format_float(*f)),
Number::Integer(v) => format!("int <{v}>"),
Number::String(s) => format!("number <{s}>"),
Number::BigInteger(s) => format!("number <{s}>"),
}
}
}
@ -160,7 +160,7 @@ impl Number {
match self {
Number::Float(f) => format!("float <{}>", format_float(*f)),
Number::Integer(value) => format!("integer <{value}>"),
Number::String(s) => format!("number <{s}>"),
Number::BigInteger(s) => format!("number <{s}>"),
}
}
}
@ -523,7 +523,8 @@ fn eval_match(
/// Evaluates if an `actual` value is an integer.
fn eval_is_integer(actual: &Value) -> Result<AssertResult, Error> {
Ok(AssertResult {
success: matches!(actual, Value::Number(Number::Integer(_))),
success: matches!(actual, Value::Number(Number::Integer(_)))
|| matches!(actual, Value::Number(Number::BigInteger(_))),
actual: actual.display(),
expected: "integer".to_string(),
type_mismatch: false,

View File

@ -74,6 +74,6 @@ fn eval_number(number: &Number) -> ValueNumber {
match number {
Number::Float(value) => ValueNumber::Float(value.value),
Number::Integer(value) => ValueNumber::Integer(*value),
Number::String(value) => ValueNumber::String(value.clone()),
Number::BigInteger(value) => ValueNumber::BigInteger(value.clone()),
}
}

View File

@ -292,7 +292,7 @@ impl Value {
} else if n.is_i64() {
Value::Number(Number::from(n.as_i64().unwrap()))
} else {
Value::Number(Number::String(n.to_string()))
Value::Number(Number::BigInteger(n.to_string()))
}
}
serde_json::Value::String(s) => Value::String(s.to_string()),
@ -505,7 +505,14 @@ pub mod tests {
serde_json::from_str("1000000000000000000000").unwrap();
assert_eq!(
Value::from_json(&json_number),
Value::Number(Number::String("1000000000000000000000".to_string()))
Value::Number(Number::BigInteger("1000000000000000000000".to_string()))
);
let json_number: serde_json::Value =
serde_json::from_str("1000000000000000000000.5").unwrap();
assert_eq!(
Value::from_json(&json_number),
Value::Number(Number::Float(1000000000000000000000.5f64))
)
}

View File

@ -602,7 +602,7 @@ pub struct Filename {
pub enum Number {
Float(Float),
Integer(i64),
String(String),
BigInteger(String),
}
// keep Number terminology for both Integer and Decimal Numbers
@ -834,8 +834,7 @@ pub struct VariableDefinition {
pub enum VariableValue {
Null,
Bool(bool),
Integer(i64),
Float(Float),
Number(Number),
String(Template),
}

View File

@ -85,7 +85,7 @@ impl fmt::Display for Number {
match self {
Number::Float(value) => write!(f, "{}", value),
Number::Integer(value) => write!(f, "{}", value),
Number::String(value) => write!(f, "{}", value),
Number::BigInteger(value) => write!(f, "{}", value),
}
}
}
@ -207,8 +207,7 @@ impl fmt::Display for VariableValue {
let s = match self {
VariableValue::Null => "null".to_string(),
VariableValue::Bool(value) => value.to_string(),
VariableValue::Integer(n) => n.to_string(),
VariableValue::Float(x) => x.to_string(),
VariableValue::Number(n) => n.to_string(),
VariableValue::String(s) => s.to_string(),
};
write!(f, "{}", s)

View File

@ -271,8 +271,7 @@ impl HtmlFormatter {
match option {
VariableValue::Null => self.fmt_span("null", "null"),
VariableValue::Bool(v) => self.fmt_bool(*v),
VariableValue::Integer(v) => self.fmt_number(v),
VariableValue::Float(v) => self.fmt_number(&v.encoded),
VariableValue::Number(v) => self.fmt_number(v),
VariableValue::String(t) => self.fmt_template(t),
}
}

View File

@ -16,7 +16,6 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::parser::error::*;
use crate::parser::primitives::try_literal;
use crate::parser::reader::Reader;
@ -61,36 +60,6 @@ pub fn natural(reader: &mut Reader) -> ParseResult<u64> {
}
}
pub fn number(reader: &mut Reader) -> ParseResult<Number> {
choice(
&[
|p1| match float(p1) {
Ok(value) => Ok(Number::Float(value)),
Err(e) => Err(e.recoverable()),
},
|p1| match integer(p1) {
Ok(value) => Ok(Number::Integer(value)),
Err(e) => Err(e.recoverable()),
},
|p1| match string_number(p1) {
Ok(value) => Ok(Number::String(value)),
Err(e) => Err(e),
},
],
reader,
)
.map_err(|e| {
let inner = if e.recoverable {
ParseError::Expecting {
value: "number".to_string(),
}
} else {
e.inner
};
Error::new(e.pos, true, inner)
})
}
pub fn integer(reader: &mut Reader) -> ParseResult<i64> {
let sign = match try_literal("-", reader) {
Err(_) => 1,
@ -100,70 +69,58 @@ pub fn integer(reader: &mut Reader) -> ParseResult<i64> {
Ok(sign * (nat as i64))
}
pub fn float(reader: &mut Reader) -> ParseResult<Float> {
// non recoverable after the dot
// an integer is parsed ok as float => no like a computer language
pub fn number(reader: &mut Reader) -> ParseResult<Number> {
let start = reader.state;
let sign = match try_literal("-", reader) {
Err(_) => "",
Ok(_) => "-",
};
let nat = natural(reader)?;
try_literal(".", reader)?;
if reader.is_eof() {
let inner = ParseError::Expecting {
value: String::from("natural"),
};
return Err(Error::new(reader.state.pos, false, inner));
}
let s = reader.read_while(|c| c.is_ascii_digit());
if s.is_empty() {
let inner = ParseError::Expecting {
value: String::from("natural"),
};
return Err(Error::new(reader.state.pos, false, inner));
}
match format!("{sign}{nat}.{s}").parse() {
Ok(value) => {
let encoded = reader.peek_back(start.cursor);
Ok(Float { value, encoded })
}
Err(_) => {
let inner = ParseError::Expecting {
value: String::from("float"),
};
Err(Error::new(start.pos, false, inner))
}
}
}
pub fn string_number(reader: &mut Reader) -> ParseResult<String> {
let sign = match try_literal("-", reader) {
Err(_) => "",
Ok(_) => "-",
};
let integer = reader.read_while(|c| c.is_ascii_digit());
if integer.is_empty() {
let integer_digits = reader.read_while(|c| c.is_ascii_digit());
if integer_digits.is_empty() {
let inner = ParseError::Expecting {
value: "number".to_string(),
};
return Err(Error::new(reader.state.pos, true, inner));
// if the first digit is zero, you should not have any more digits
} else if integer_digits.len() > 1 && integer_digits.starts_with('0') {
let save = reader.state;
let inner = ParseError::Expecting {
value: String::from("natural"),
};
return Err(Error::new(save.pos, false, inner));
}
let decimal = if try_literal(".", reader).is_ok() {
let s = reader.read_while(|c| c.is_ascii_digit());
if s.is_empty() {
// Float
if try_literal(".", reader).is_ok() {
let save = reader.state;
let decimal_digits = reader.read_while(|c| c.is_ascii_digit());
if decimal_digits.is_empty() {
let inner = ParseError::Expecting {
value: "decimals".to_string(),
value: String::from("decimal digits"),
};
return Err(Error::new(reader.state.pos, false, inner));
return Err(Error::new(save.pos, false, inner));
}
format!(".{s}")
match format!("{sign}{integer_digits}.{decimal_digits}").parse() {
Ok(value) => {
let encoded = reader.peek_back(start.cursor);
Ok(Number::Float(Float { value, encoded }))
}
Err(_) => {
let inner = ParseError::Expecting {
value: String::from("float"),
};
Err(Error::new(start.pos, false, inner))
}
}
// Integer or BigInteger
} else {
String::new()
};
Ok(format!("{sign}{integer}{decimal}"))
match format!("{sign}{integer_digits}").parse() {
Ok(value) => Ok(Number::Integer(value)),
Err(_) => Ok(Number::BigInteger(integer_digits)),
}
}
}
#[cfg(test)]
@ -177,10 +134,6 @@ mod tests {
assert_eq!(natural(&mut reader).unwrap(), 0);
assert_eq!(reader.state.cursor, 1);
let mut reader = Reader::new("0.");
assert_eq!(natural(&mut reader).unwrap(), 0);
assert_eq!(reader.state.cursor, 1);
let mut reader = Reader::new("10x");
assert_eq!(natural(&mut reader).unwrap(), 10);
assert_eq!(reader.state.cursor, 2);
@ -223,9 +176,49 @@ mod tests {
}
#[test]
pub fn test_number() {
let mut reader = Reader::new("1");
assert_eq!(number(&mut reader).unwrap(), Number::Integer(1));
fn test_integer() {
let mut reader = Reader::new("0");
assert_eq!(integer(&mut reader).unwrap(), 0);
assert_eq!(reader.state.cursor, 1);
let mut reader = Reader::new("-1");
assert_eq!(integer(&mut reader).unwrap(), -1);
assert_eq!(reader.state.cursor, 2);
let mut reader = Reader::new("0");
assert_eq!(number(&mut reader).unwrap(), Number::Integer(0));
assert_eq!(reader.state.cursor, 1);
let mut reader = Reader::new("10x");
assert_eq!(number(&mut reader).unwrap(), Number::Integer(10));
assert_eq!(reader.state.cursor, 2);
let mut reader = Reader::new("-10x");
assert_eq!(number(&mut reader).unwrap(), Number::Integer(-10));
assert_eq!(reader.state.cursor, 3);
}
#[test]
fn test_float() {
let mut reader = Reader::new("1.0");
assert_eq!(
number(&mut reader).unwrap(),
Number::Float(Float {
value: 1.0,
encoded: "1.0".to_string()
})
);
assert_eq!(reader.state.cursor, 3);
let mut reader = Reader::new("-1.0");
assert_eq!(
number(&mut reader).unwrap(),
Number::Float(Float {
value: -1.0,
encoded: "-1.0".to_string()
})
);
assert_eq!(reader.state.cursor, 4);
let mut reader = Reader::new("1.1");
assert_eq!(
@ -235,16 +228,84 @@ mod tests {
encoded: "1.1".to_string()
})
);
assert_eq!(reader.state.cursor, 3);
let mut reader = Reader::new("1000000000000000000000");
let mut reader = Reader::new("1.100");
assert_eq!(
number(&mut reader).unwrap(),
Number::String("1000000000000000000000".to_string())
Number::Float(Float {
value: 1.1,
encoded: "1.100".to_string()
})
);
assert_eq!(reader.state.cursor, 5);
let mut reader = Reader::new("1.01");
assert_eq!(
number(&mut reader).unwrap(),
Number::Float(Float {
value: 1.01,
encoded: "1.01".to_string()
})
);
assert_eq!(reader.state.cursor, 4);
let mut reader = Reader::new("1.010");
assert_eq!(
number(&mut reader).unwrap(),
Number::Float(Float {
value: 1.01,
encoded: "1.010".to_string()
})
);
assert_eq!(reader.state.cursor, 5);
// provide more digits than necessary
let mut reader = Reader::new("-0.3333333333333333333");
assert_eq!(
number(&mut reader).unwrap(),
Number::Float(Float {
value: -0.3333333333333333,
encoded: "-0.3333333333333333333".to_string()
})
);
assert_eq!(reader.state.cursor, 22);
let mut reader = Reader::new("1000000000000000000000.5");
assert_eq!(
number(&mut reader).unwrap(),
Number::Float(Float {
value: 1000000000000000000000.0,
encoded: "1000000000000000000000.5".to_string()
})
);
assert_eq!(reader.state.cursor, 24);
}
#[test]
pub fn test_number_error() {
let mut reader = Reader::new("");
let error = number(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("number")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
let mut reader = Reader::new("-");
let error = number(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("number")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 2 });
assert!(error.recoverable);
let mut reader = Reader::new("x");
let error = number(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
@ -255,208 +316,16 @@ mod tests {
}
);
assert!(error.recoverable);
}
#[test]
pub fn test_integer() {
let mut reader = Reader::new("1");
assert_eq!(integer(&mut reader).unwrap(), 1);
let mut reader = Reader::new("1.1");
assert_eq!(integer(&mut reader).unwrap(), 1);
let mut reader = Reader::new("-1.1");
assert_eq!(integer(&mut reader).unwrap(), -1);
let mut reader = Reader::new("x");
let error = integer(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("natural")
}
);
assert!(error.recoverable);
}
#[test]
fn test_float() {
let mut reader = Reader::new("1.0");
assert_eq!(
float(&mut reader).unwrap(),
Float {
value: 1.0,
encoded: "1.0".to_string()
}
);
assert_eq!(reader.state.cursor, 3);
let mut reader = Reader::new("-1.0");
assert_eq!(
float(&mut reader).unwrap(),
Float {
value: -1.0,
encoded: "-1.0".to_string()
}
);
assert_eq!(reader.state.cursor, 4);
let mut reader = Reader::new("1.1");
assert_eq!(
float(&mut reader).unwrap(),
Float {
value: 1.1,
encoded: "1.1".to_string()
}
);
assert_eq!(reader.state.cursor, 3);
let mut reader = Reader::new("1.100");
assert_eq!(
float(&mut reader).unwrap(),
Float {
value: 1.1,
encoded: "1.100".to_string()
}
);
assert_eq!(reader.state.cursor, 5);
let mut reader = Reader::new("1.01");
assert_eq!(
float(&mut reader).unwrap(),
Float {
value: 1.01,
encoded: "1.01".to_string()
}
);
assert_eq!(reader.state.cursor, 4);
let mut reader = Reader::new("1.010");
assert_eq!(
float(&mut reader).unwrap(),
Float {
value: 1.01,
encoded: "1.010".to_string()
}
);
assert_eq!(reader.state.cursor, 5);
// provide more digits than necessary
let mut reader = Reader::new("-0.3333333333333333333");
assert_eq!(
float(&mut reader).unwrap(),
Float {
value: -0.3333333333333333,
encoded: "-0.3333333333333333333".to_string()
}
);
assert_eq!(reader.state.cursor, 22);
}
#[test]
fn test_float_error() {
let mut reader = Reader::new("");
let error = float(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("natural")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
let mut reader = Reader::new("-");
let error = float(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("natural")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 2 });
assert!(error.recoverable);
let mut reader = Reader::new("1");
let error = float(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from(".")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 2 });
assert!(error.recoverable);
let mut reader = Reader::new("1x");
let error = float(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from(".")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 2 });
assert!(error.recoverable);
let mut reader = Reader::new("1.");
let error = float(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("natural")
}
);
let error = number(&mut reader).err().unwrap();
assert_eq!(error.pos, Pos { line: 1, column: 3 });
assert!(!error.recoverable);
let mut reader = Reader::new("1.x");
let error = float(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("natural")
value: String::from("decimal digits")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 3 });
assert!(!error.recoverable);
}
#[test]
pub fn test_string_number() {
let mut reader = Reader::new("1");
assert_eq!(string_number(&mut reader).unwrap(), "1");
let mut reader = Reader::new("1000000000000000000000");
assert_eq!(
string_number(&mut reader).unwrap(),
"1000000000000000000000"
);
}
#[test]
fn test_string_number_error() {
let mut reader = Reader::new("1.x");
let error = string_number(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("decimals")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 3 });
assert!(!error.recoverable);
let mut reader = Reader::new("{{");
let error = string_number(&mut reader).err().unwrap();
assert_eq!(
error.inner,
ParseError::Expecting {
value: String::from("number")
}
);
assert_eq!(error.pos, Pos { line: 1, column: 1 });
assert!(error.recoverable);
}
}

View File

@ -19,7 +19,7 @@
use crate::ast::*;
use crate::parser::combinators::*;
use crate::parser::error::*;
use crate::parser::number::{float, integer, natural};
use crate::parser::number::{integer, natural, number};
use crate::parser::primitives::*;
use crate::parser::reader::Reader;
use crate::parser::string::*;
@ -320,12 +320,8 @@ fn variable_value(reader: &mut Reader) -> ParseResult<VariableValue> {
Ok(value) => Ok(VariableValue::Bool(value)),
Err(e) => Err(e),
},
|p1| match float(p1) {
Ok(value) => Ok(VariableValue::Float(value)),
Err(e) => Err(e),
},
|p1| match integer(p1) {
Ok(value) => Ok(VariableValue::Integer(value)),
|p1| match number(p1) {
Ok(value) => Ok(VariableValue::Number(value)),
Err(e) => Err(e),
},
|p1| match quoted_template(p1) {
@ -542,7 +538,7 @@ mod tests {
end: Pos { line: 1, column: 3 },
},
},
value: VariableValue::Integer(1),
value: VariableValue::Number(Number::Integer(1)),
}
);
}
@ -561,7 +557,7 @@ mod tests {
let mut reader = Reader::new("1");
assert_eq!(
variable_value(&mut reader).unwrap(),
VariableValue::Integer(1)
VariableValue::Number(Number::Integer(1))
);
let mut reader = Reader::new("toto");

View File

@ -959,8 +959,7 @@ impl Tokenizable for VariableValue {
match self {
VariableValue::Null => vec![Token::Keyword("null".to_string())],
VariableValue::Bool(v) => vec![Token::Boolean(v.to_string())],
VariableValue::Integer(v) => vec![Token::Number(v.to_string())],
VariableValue::Float(v) => vec![Token::Number(v.to_string())],
VariableValue::Number(v) => vec![Token::Number(v.to_string())],
VariableValue::String(v) => v.tokenize(),
}
}