From 973617108930fd72e3295ffbc6537b7ca9310b8e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 17:07:30 +0800 Subject: [PATCH] Fix bug in hex parsing for negative numbers. --- CHANGELOG.md | 1 + src/lib.rs | 14 ++++++++++++++ src/packages/math_basic.rs | 18 ++++++++++-------- src/tokenizer.rs | 9 ++++++--- tests/number_literals.rs | 17 +++++++++++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a27f27..59d7bc88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Bug fixes --------- * Custom syntax now works properly inside binary expressions and with method calls. +* Hex numbers with the high-bit set now parse correctly into negative integer numbers. Enhancements ------------ diff --git a/src/lib.rs b/src/lib.rs index 077245c1..639d92de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,20 @@ pub type INT = i64; #[cfg(feature = "only_i32")] pub type INT = i32; +/// The system base integer type. It is defined as [`u64`]. +/// +/// If the `only_i32` feature is enabled, this will be [`u32`] instead. +#[cfg(not(feature = "only_i32"))] +#[allow(non_camel_case_types)] +type INT_BASE = u64; +/// The system integer base type. +/// It is defined as [`u32`] since the `only_i32` feature is used. +/// +/// If the `only_i32` feature is not used, this will be `u64` instead. +#[cfg(feature = "only_i32")] +#[allow(non_camel_case_types)] +type INT_BASE = u32; + /// The system floating-point type. It is defined as [`f64`]. /// Not available under `no_float`. /// diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 0479b937..2e9eec92 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use crate::plugin::*; -use crate::{def_package, Position, INT}; +use crate::{def_package, Position, INT, INT_BASE}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -120,13 +120,15 @@ mod int_functions { .into()); } - INT::from_str_radix(string.trim(), radix as u32).map_err(|err| { - EvalAltResult::ErrorArithmetic( - format!("Error parsing integer number '{}': {}", string, err), - Position::NONE, - ) - .into() - }) + INT_BASE::from_str_radix(string.trim(), radix as u32) + .map(|v| v as INT) + .map_err(|err| { + EvalAltResult::ErrorArithmetic( + format!("Error parsing integer number '{}': {}", string, err), + Position::NONE, + ) + .into() + }) } #[rhai_fn(name = "parse_int", return_raw)] pub fn parse_int(string: &str) -> Result> { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index e5a4905d..0be44347 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -5,7 +5,7 @@ use crate::engine::{ KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF, }; use crate::func::native::OnParseTokenCallback; -use crate::{Engine, LexError, StaticVec, INT}; +use crate::{Engine, LexError, StaticVec, INT, INT_BASE}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -1507,7 +1507,8 @@ fn get_next_token_inner( .filter(|&&c| c != NUMBER_SEPARATOR) .collect(); - INT::from_str_radix(&out, radix) + INT_BASE::from_str_radix(&out, radix) + .map(|v| v as INT) .map(Token::IntegerConstant) .unwrap_or_else(|_| { Token::LexError(LERR::MalformedNumber(result.into_iter().collect())) @@ -1515,7 +1516,9 @@ fn get_next_token_inner( } else { let out: String = result.iter().filter(|&&c| c != NUMBER_SEPARATOR).collect(); - let num = INT::from_str(&out).map(Token::IntegerConstant); + let num = INT_BASE::from_str(&out) + .map(|v| v as INT) + .map(Token::IntegerConstant); // If integer parsing is unnecessary, try float instead #[cfg(not(feature = "no_float"))] diff --git a/tests/number_literals.rs b/tests/number_literals.rs index 2e7b6b01..daade83b 100644 --- a/tests/number_literals.rs +++ b/tests/number_literals.rs @@ -27,6 +27,11 @@ fn test_hex_literal() -> Result<(), Box> { assert_eq!(engine.eval::("let x = 0Xf; x")?, 15); assert_eq!(engine.eval::("let x = 0xff; x")?, 255); + #[cfg(not(feature = "only_i32"))] + assert_eq!(engine.eval::("let x = 0xffffffffffffffff; x")?, -1); + #[cfg(feature = "only_i32")] + assert_eq!(engine.eval::("let x = 0xffffffff; x")?, -1); + Ok(()) } @@ -51,6 +56,18 @@ fn test_binary_literal() -> Result<(), Box> { engine.eval::("let x = 0b0011_1100_1010_0101; x")?, 15525 ); + #[cfg(not(feature = "only_i32"))] + assert_eq!( + engine.eval::( + "let x = 0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111; x" + )?, + -1 + ); + #[cfg(feature = "only_i32")] + assert_eq!( + engine.eval::("let x = 0b11111111_11111111_11111111_11111111; x")?, + -1 + ); Ok(()) }