Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ impl<'a> From<Value<'a>> for serde_json::Value {
Value::BooleanLit(b) => serde_json::Value::Bool(b.value),
Value::NullKeyword(_) => serde_json::Value::Null,
Value::NumberLit(num) => {
// Check if this is a hexadecimal literal (0x or 0X prefix)
let num_str = num.value.trim_start_matches('-');
// check if this is a hexadecimal literal (0x or 0X prefix)
let num_str = num.value.trim_start_matches(['-', '+']);
if num_str.len() > 2 && (num_str.starts_with("0x") || num_str.starts_with("0X")) {
// Parse hexadecimal and convert to decimal
let hex_part = &num_str[2..];
Expand All @@ -86,8 +86,9 @@ impl<'a> From<Value<'a>> for serde_json::Value {
Err(_) => serde_json::Value::String(num.value.to_string()),
}
} else {
// Standard decimal number
match serde_json::Number::from_str(num.value) {
// standard decimal number
let num_for_parsing = num.value.trim_start_matches('+');
match serde_json::Number::from_str(num_for_parsing) {
Ok(number) => serde_json::Value::Number(number),
Err(_) => serde_json::Value::String(num.value.to_string()),
}
Expand Down
7 changes: 4 additions & 3 deletions src/cst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,7 @@ impl CstNumberLit {
let raw = self.0.borrow().value.clone();

// check if this is a hexadecimal literal (0x or 0X prefix)
let num_str = raw.trim_start_matches('-');
let num_str = raw.trim_start_matches(['-', '+']);
if num_str.len() > 2 && (num_str.starts_with("0x") || num_str.starts_with("0X")) {
// parse hexadecimal and convert to decimal
let hex_part = &num_str[2..];
Expand All @@ -1460,8 +1460,9 @@ impl CstNumberLit {
Err(_) => Some(serde_json::Value::String(raw)),
}
} else {
// standard decimal number
match serde_json::Number::from_str(&raw) {
// standard decimal number - strip leading + if present (serde_json doesn't accept it)
let num_for_parsing = raw.trim_start_matches('+');
match serde_json::Number::from_str(num_for_parsing) {
Ok(number) => Some(serde_json::Value::Number(number)),
// if the number is invalid, return it as a string (same behavior as AST conversion)
Err(_) => Some(serde_json::Value::String(raw)),
Expand Down
13 changes: 13 additions & 0 deletions src/parse_to_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,4 +602,17 @@ mod tests {
);
}
}

#[test]
fn it_should_parse_unary_plus_numbers() {
let result = parse_to_ast(r#"{ "test": +42 }"#, &Default::default(), &Default::default()).unwrap();

let value = result.value.unwrap();
let obj = value.as_object().unwrap();
assert_eq!(obj.properties.len(), 1);
assert_eq!(obj.properties[0].name.as_str(), "test");

let number_value = obj.properties[0].value.as_number_lit().unwrap();
assert_eq!(number_value.value, "+42");
}
}
25 changes: 23 additions & 2 deletions src/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl<'a> Scanner<'a> {
_ => Err(self.create_error_for_current_token(ParseErrorKind::UnexpectedToken)),
},
_ => {
if current_char == '-' || self.is_digit() {
if current_char == '-' || current_char == '+' || self.is_digit() {
self.parse_number()
} else if self.try_move_word("true") {
Ok(Token::Boolean(true))
Expand Down Expand Up @@ -154,7 +154,8 @@ impl<'a> Scanner<'a> {
fn parse_number(&mut self) -> Result<Token<'a>, ParseError> {
let start_byte_index = self.byte_index;

if self.is_negative_sign() {
// handle unary plus or minus
if self.is_negative_sign() || self.is_positive_sign() {
self.move_next_char();
}

Expand Down Expand Up @@ -423,6 +424,10 @@ impl<'a> Scanner<'a> {
self.current_char() == Some('-')
}

fn is_positive_sign(&self) -> bool {
self.current_char() == Some('+')
}

fn is_decimal_point(&self) -> bool {
self.current_char() == Some('.')
}
Expand Down Expand Up @@ -540,6 +545,22 @@ mod tests {
);
}

#[test]
fn it_tokenizes_unary_plus_numbers() {
assert_has_tokens(
"+42, +0.5, +1e10, +0xFF",
vec![
Token::Number("+42"),
Token::Comma,
Token::Number("+0.5"),
Token::Comma,
Token::Number("+1e10"),
Token::Comma,
Token::Number("+0xFF"),
],
);
}

#[test]
fn it_errors_invalid_exponent() {
assert_has_error(
Expand Down
26 changes: 26 additions & 0 deletions src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,30 @@ mod tests {

assert_eq!(result, Some(SerdeValue::Object(expected_value)));
}

#[test]
fn it_should_parse_unary_plus_numbers() {
let result = parse_to_serde_value(
r#"{
"pos1": +42,
"pos2": +0.5,
"pos3": +1e10
}"#,
&Default::default(),
)
.unwrap();

let mut expected_value = serde_json::map::Map::new();
expected_value.insert("pos1".to_string(), SerdeValue::Number(serde_json::Number::from(42)));
expected_value.insert(
"pos2".to_string(),
SerdeValue::Number(serde_json::Number::from_str("0.5").unwrap()),
);
expected_value.insert(
"pos3".to_string(),
SerdeValue::Number(serde_json::Number::from_str("1e10").unwrap()),
);

assert_eq!(result, Some(SerdeValue::Object(expected_value)));
}
}
Loading