Skip to content

Commit b35344b

Browse files
authored
fix: support numbers with plus sign at start (#58)
1 parent 6c4221d commit b35344b

File tree

5 files changed

+71
-9
lines changed

5 files changed

+71
-9
lines changed

src/ast.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ impl<'a> From<Value<'a>> for serde_json::Value {
6969
Value::BooleanLit(b) => serde_json::Value::Bool(b.value),
7070
Value::NullKeyword(_) => serde_json::Value::Null,
7171
Value::NumberLit(num) => {
72-
// Check if this is a hexadecimal literal (0x or 0X prefix)
73-
let num_str = num.value.trim_start_matches('-');
72+
// check if this is a hexadecimal literal (0x or 0X prefix)
73+
let num_str = num.value.trim_start_matches(['-', '+']);
7474
if num_str.len() > 2 && (num_str.starts_with("0x") || num_str.starts_with("0X")) {
7575
// Parse hexadecimal and convert to decimal
7676
let hex_part = &num_str[2..];
@@ -86,8 +86,9 @@ impl<'a> From<Value<'a>> for serde_json::Value {
8686
Err(_) => serde_json::Value::String(num.value.to_string()),
8787
}
8888
} else {
89-
// Standard decimal number
90-
match serde_json::Number::from_str(num.value) {
89+
// standard decimal number
90+
let num_for_parsing = num.value.trim_start_matches('+');
91+
match serde_json::Number::from_str(num_for_parsing) {
9192
Ok(number) => serde_json::Value::Number(number),
9293
Err(_) => serde_json::Value::String(num.value.to_string()),
9394
}

src/cst/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,7 @@ impl CstNumberLit {
14441444
let raw = self.0.borrow().value.clone();
14451445

14461446
// check if this is a hexadecimal literal (0x or 0X prefix)
1447-
let num_str = raw.trim_start_matches('-');
1447+
let num_str = raw.trim_start_matches(['-', '+']);
14481448
if num_str.len() > 2 && (num_str.starts_with("0x") || num_str.starts_with("0X")) {
14491449
// parse hexadecimal and convert to decimal
14501450
let hex_part = &num_str[2..];
@@ -1460,8 +1460,9 @@ impl CstNumberLit {
14601460
Err(_) => Some(serde_json::Value::String(raw)),
14611461
}
14621462
} else {
1463-
// standard decimal number
1464-
match serde_json::Number::from_str(&raw) {
1463+
// standard decimal number - strip leading + if present (serde_json doesn't accept it)
1464+
let num_for_parsing = raw.trim_start_matches('+');
1465+
match serde_json::Number::from_str(num_for_parsing) {
14651466
Ok(number) => Some(serde_json::Value::Number(number)),
14661467
// if the number is invalid, return it as a string (same behavior as AST conversion)
14671468
Err(_) => Some(serde_json::Value::String(raw)),

src/parse_to_ast.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,4 +602,17 @@ mod tests {
602602
);
603603
}
604604
}
605+
606+
#[test]
607+
fn it_should_parse_unary_plus_numbers() {
608+
let result = parse_to_ast(r#"{ "test": +42 }"#, &Default::default(), &Default::default()).unwrap();
609+
610+
let value = result.value.unwrap();
611+
let obj = value.as_object().unwrap();
612+
assert_eq!(obj.properties.len(), 1);
613+
assert_eq!(obj.properties[0].name.as_str(), "test");
614+
615+
let number_value = obj.properties[0].value.as_number_lit().unwrap();
616+
assert_eq!(number_value.value, "+42");
617+
}
605618
}

src/scanner.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl<'a> Scanner<'a> {
7979
_ => Err(self.create_error_for_current_token(ParseErrorKind::UnexpectedToken)),
8080
},
8181
_ => {
82-
if current_char == '-' || self.is_digit() {
82+
if current_char == '-' || current_char == '+' || self.is_digit() {
8383
self.parse_number()
8484
} else if self.try_move_word("true") {
8585
Ok(Token::Boolean(true))
@@ -154,7 +154,8 @@ impl<'a> Scanner<'a> {
154154
fn parse_number(&mut self) -> Result<Token<'a>, ParseError> {
155155
let start_byte_index = self.byte_index;
156156

157-
if self.is_negative_sign() {
157+
// handle unary plus or minus
158+
if self.is_negative_sign() || self.is_positive_sign() {
158159
self.move_next_char();
159160
}
160161

@@ -423,6 +424,10 @@ impl<'a> Scanner<'a> {
423424
self.current_char() == Some('-')
424425
}
425426

427+
fn is_positive_sign(&self) -> bool {
428+
self.current_char() == Some('+')
429+
}
430+
426431
fn is_decimal_point(&self) -> bool {
427432
self.current_char() == Some('.')
428433
}
@@ -540,6 +545,22 @@ mod tests {
540545
);
541546
}
542547

548+
#[test]
549+
fn it_tokenizes_unary_plus_numbers() {
550+
assert_has_tokens(
551+
"+42, +0.5, +1e10, +0xFF",
552+
vec![
553+
Token::Number("+42"),
554+
Token::Comma,
555+
Token::Number("+0.5"),
556+
Token::Comma,
557+
Token::Number("+1e10"),
558+
Token::Comma,
559+
Token::Number("+0xFF"),
560+
],
561+
);
562+
}
563+
543564
#[test]
544565
fn it_errors_invalid_exponent() {
545566
assert_has_error(

src/serde.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,30 @@ mod tests {
104104

105105
assert_eq!(result, Some(SerdeValue::Object(expected_value)));
106106
}
107+
108+
#[test]
109+
fn it_should_parse_unary_plus_numbers() {
110+
let result = parse_to_serde_value(
111+
r#"{
112+
"pos1": +42,
113+
"pos2": +0.5,
114+
"pos3": +1e10
115+
}"#,
116+
&Default::default(),
117+
)
118+
.unwrap();
119+
120+
let mut expected_value = serde_json::map::Map::new();
121+
expected_value.insert("pos1".to_string(), SerdeValue::Number(serde_json::Number::from(42)));
122+
expected_value.insert(
123+
"pos2".to_string(),
124+
SerdeValue::Number(serde_json::Number::from_str("0.5").unwrap()),
125+
);
126+
expected_value.insert(
127+
"pos3".to_string(),
128+
SerdeValue::Number(serde_json::Number::from_str("1e10").unwrap()),
129+
);
130+
131+
assert_eq!(result, Some(SerdeValue::Object(expected_value)));
132+
}
107133
}

0 commit comments

Comments
 (0)