From 973617108930fd72e3295ffbc6537b7ca9310b8e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 17:07:30 +0800 Subject: [PATCH 1/4] 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(()) } From 7eda6454a89f7d96b20070cba0b6f1168c27bf2f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 17:47:35 +0800 Subject: [PATCH 2/4] Bug fix. --- src/tokenizer.rs | 4 +--- tests/blobs.rs | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 0be44347..ca28add3 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1516,9 +1516,7 @@ fn get_next_token_inner( } else { let out: String = result.iter().filter(|&&c| c != NUMBER_SEPARATOR).collect(); - let num = INT_BASE::from_str(&out) - .map(|v| v as INT) - .map(Token::IntegerConstant); + let num = INT::from_str(&out).map(Token::IntegerConstant); // If integer parsing is unnecessary, try float instead #[cfg(not(feature = "no_float"))] diff --git a/tests/blobs.rs b/tests/blobs.rs index 7036f116..43ba16a1 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -136,6 +136,13 @@ fn test_blobs_parse() -> Result<(), Box> { 0x1cf588 ); + assert_eq!( + engine.eval::( + "let x = blob(16, 0); x.write_be(0, 8, 0x1234567890abcdef); x.write_be(8, 8, 0xabcdef1234567890); x" + )?, + vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90] + ); + Ok(()) } From aa8cc8f719656761a791d56ad991bc335460595f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 17:59:32 +0800 Subject: [PATCH 3/4] Fix builds. --- tests/custom_syntax.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index d2bb257a..1d21c315 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -268,6 +268,7 @@ fn test_custom_syntax_raw2() -> Result<(), Box> { ); assert_eq!(engine.eval::("let x = 41; x + #1")?, 42); + #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval::("#-42.abs()")?, 42); assert_eq!(engine.eval::("#42/2")?, 21); assert_eq!(engine.eval::("#-1")?, -1); From ac8324ae41734a825941b3825b0d1261098ff379 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 18 Dec 2021 18:08:48 +0800 Subject: [PATCH 4/4] Fix no_object build. --- tests/blobs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/blobs.rs b/tests/blobs.rs index 43ba16a1..90f8afac 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -138,7 +138,7 @@ fn test_blobs_parse() -> Result<(), Box> { assert_eq!( engine.eval::( - "let x = blob(16, 0); x.write_be(0, 8, 0x1234567890abcdef); x.write_be(8, 8, 0xabcdef1234567890); x" + "let x = blob(16, 0); write_be(x, 0, 8, 0x1234567890abcdef); write_be(x, 8, 8, 0xabcdef1234567890); x" )?, vec![0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90] );