Skip to content

Commit

Permalink
fix negative number parsing (#23)
Browse files Browse the repository at this point in the history
* fix negative number parsing

* fmt

* bump

* update readme
  • Loading branch information
xlc committed Jul 5, 2022
1 parent 205846e commit 2c12af4
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 35 deletions.
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lite-json"
version = "0.1.3"
version = "0.2.0"
authors = ["Bryan Chen <xlchen1291@gmail.com>"]
description = "Simple JSON parser. Wasm / no_std ready."
license = "Apache-2.0"
Expand All @@ -12,9 +12,12 @@ categories = [
]

[dependencies]
lite-parser = { version = "0.1.2", path = "parser", default-features = false }
lite-parser = { version = "0.2.0", path = "parser", default-features = false }
num-traits = { version = "0.2", optional = true, default-features = false }

[dev-dependencies]
assert_float_eq = "1.1.3"

[features]
default = ["std"]
std = [
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ Simple JSON parser written with Rust. Wasm / no_std ready.
## How to Add in Cargo.toml

### std
```
```toml
[dependencies]
lite-json = "0.1.3"
lite-json = "0.2.0"
```

### no_std
```
```toml
[dependencies]
lite-json = {version="0.1.3", default-features=false, defaults=["no_std"]}
lite-json = { version = "0.2.0", default-features = false, defaults = ["no_std"] }
```

## Example Usage
Expand All @@ -25,7 +25,7 @@ lite-json = {version="0.1.3", default-features=false, defaults=["no_std"]}

This example will create a lite-json structure, then print it as a JSON string.

```
```rs
use lite_json::Serialize;
use lite_json::json::{JsonValue, NumberValue};

Expand All @@ -45,7 +45,7 @@ fn main()
object_elements.push((object_key, JsonValue::Array(array_value)));

// Create a string value and add it to our vector.
let string_value = "Hello World!".chars().collect();
let string_value = "Hello World!".chars().collect();
let object_key = "string".chars().collect();
object_elements.push((object_key, JsonValue::String(string_value)));

Expand All @@ -63,7 +63,7 @@ fn main()
// Create a null value and add it to our vector.
let object_key = "null".chars().collect();
object_elements.push((object_key, JsonValue::Null));

// Create the object value from the vector of elements.
let object_value = JsonValue::Object(object_elements);

Expand All @@ -76,7 +76,7 @@ fn main()
```

This will output:
```
```json
{
"boolean": true,
"array": [
Expand All @@ -94,13 +94,13 @@ This will output:

This example will parse a JSON string into a lite-json structure.

```
```rs
use lite_json::json_parser::parse_json;

fn main()
{
// This is the JSON string we will use.
let json_string =
let json_string =
r#"
{
"boolean": true,
Expand Down Expand Up @@ -128,14 +128,14 @@ The parser options allows you to set the max depth of parsing nested objects. Th

Note: This example requires the `lite-parser` crate to be added to `Cargo.toml`.

```
```rs
use lite_json::json_parser::parse_json_with_options;
use lite_parser::parser::ParserOptions;

fn main()
{
// This is the JSON string we will use.
let json_string =
let json_string =
r#"
{
"boolean": true,
Expand Down
4 changes: 2 additions & 2 deletions parser/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lite-parser"
version = "0.1.2"
version = "0.2.0"
authors = ["Bryan Chen <xlchen1291@gmail.com>"]
description = "Simple parser library. Wasm / no_std ready."
license = "Apache-2.0"
Expand All @@ -12,7 +12,7 @@ categories = [
]

[dependencies]
paste = "0.1"
paste = "1.0.7"

[features]
default = ["std"]
Expand Down
107 changes: 96 additions & 11 deletions src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ use alloc::string::ToString;
use crate::traits::Serialize;

#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Copy)]
pub struct NumberValue {
pub integer: i64,
pub integer: u64,
pub fraction: u64,
pub fraction_length: u32,
pub exponent: i32,
pub negative: bool,
}

impl NumberValue {
Expand All @@ -32,8 +33,10 @@ impl Into<f64> for NumberValue {
#[cfg(not(feature = "std"))]
use num_traits::float::FloatCore as _;

let sign = if self.negative { -1.0 } else { 1.0 };
(self.integer as f64 + self.fraction as f64 / 10f64.powi(self.fraction_length as i32))
* 10f64.powi(self.exponent)
* sign
}
}

Expand Down Expand Up @@ -182,22 +185,25 @@ impl JsonValue {

impl Serialize for NumberValue {
fn serialize_to(&self, buffer: &mut Vec<u8>, _indent: u32, _level: u32) {
if self.negative {
buffer.push(b'-');
}
buffer.extend_from_slice(self.integer.to_string().as_bytes());

if self.fraction > 0 {
buffer.push('.' as u8);
buffer.push(b'.');

let fraction_nums = self.fraction.to_string();
let fraction_length = self.fraction_length as usize;
for _ in 0..fraction_length - fraction_nums.len() {
buffer.push('0' as u8);
buffer.push(b'0');
}
buffer.extend_from_slice(fraction_nums.as_bytes())
}
if self.exponent != 0 {
buffer.push('e' as u8);
buffer.push(b'e');
if self.exponent < 0 {
buffer.push('-' as u8);
buffer.push(b'-');
}
buffer.extend_from_slice(self.exponent.abs().to_string().as_bytes());
}
Expand Down Expand Up @@ -361,6 +367,7 @@ mod tests {
fraction: 0,
fraction_length: 0,
exponent: 0,
negative: false,
});
assert!(n.is_number());
assert_eq!(
Expand All @@ -369,7 +376,8 @@ mod tests {
integer: 0,
fraction: 0,
fraction_length: 0,
exponent: 0
exponent: 0,
negative: false,
}),
);
assert_eq!(n.as_object(), None);
Expand All @@ -382,7 +390,8 @@ mod tests {
integer: 0,
fraction: 0,
fraction_length: 0,
exponent: 0
exponent: 0,
negative: false,
}),
);
assert_eq!(n.clone().to_object(), None);
Expand Down Expand Up @@ -424,22 +433,25 @@ mod tests {
fraction: 0,
fraction_length: 0,
exponent: 0,
negative: false,
};
assert_eq!(val.serialize(), b"1234");

let val = NumberValue {
integer: -1234,
integer: 1234,
fraction: 0,
fraction_length: 0,
exponent: 0,
negative: true,
};
assert_eq!(val.serialize(), b"-1234");

let val = NumberValue {
integer: -1234,
integer: 1234,
fraction: 5678,
fraction_length: 4,
exponent: 0,
negative: true,
};
assert_eq!(val.serialize(), b"-1234.5678");

Expand All @@ -448,6 +460,7 @@ mod tests {
fraction: 1,
fraction_length: 3,
exponent: 0,
negative: false,
};
assert_eq!(val.serialize(), b"1234.001");

Expand All @@ -456,6 +469,7 @@ mod tests {
fraction: 0,
fraction_length: 0,
exponent: 3,
negative: false,
};
assert_eq!(val.serialize(), b"1234e3");

Expand All @@ -464,6 +478,7 @@ mod tests {
fraction: 0,
fraction_length: 0,
exponent: -5,
negative: false,
};
assert_eq!(val.serialize(), b"1234e-5");

Expand All @@ -472,14 +487,16 @@ mod tests {
fraction: 56,
fraction_length: 4,
exponent: -5,
negative: false,
};
assert_eq!(val.serialize(), b"1234.0056e-5");

let val = NumberValue {
integer: -1234,
integer: 1234,
fraction: 5,
fraction_length: 2,
exponent: 5,
negative: true,
};
assert_eq!(val.serialize(), b"-1234.05e5");
}
Expand All @@ -505,6 +522,7 @@ mod tests {
fraction: 4,
fraction_length: 2,
exponent: 0,
negative: false,
}),
),
(
Expand All @@ -515,12 +533,14 @@ mod tests {
fraction: 0,
fraction_length: 0,
exponent: -4,
negative: false,
}),
JsonValue::Number(NumberValue {
integer: 2,
fraction: 41,
fraction_length: 3,
exponent: 2,
negative: false,
}),
JsonValue::Boolean(true),
JsonValue::Boolean(false),
Expand Down Expand Up @@ -554,4 +574,69 @@ mod tests {
r#"{"test":123.04,"test2":[1e-4,2.041e2,true,false,null,"\"1n\"",{},[]]}"#
);
}

#[test]
fn to_f64_works() {
use assert_float_eq::*;

assert_f64_near!(
NumberValue {
integer: 1,
fraction: 5,
fraction_length: 1,
exponent: 0,
negative: true,
}
.to_f64(),
-1.5
);

assert_f64_near!(
NumberValue {
integer: 0,
fraction: 5,
fraction_length: 1,
exponent: 0,
negative: true,
}
.to_f64(),
-0.5
);

assert_f64_near!(
NumberValue {
integer: 0,
fraction: 5,
fraction_length: 1,
exponent: 0,
negative: false,
}
.to_f64(),
0.5
);

assert_f64_near!(
NumberValue {
integer: 1,
fraction: 15,
fraction_length: 3,
exponent: 1,
negative: false,
}
.to_f64(),
10.15
);

assert_f64_near!(
NumberValue {
integer: 1,
fraction: 15,
fraction_length: 3,
exponent: -1,
negative: true,
}
.to_f64(),
-0.1015
);
}
}
Loading

0 comments on commit 2c12af4

Please sign in to comment.