diff --git a/RELEASES.md b/RELEASES.md index 8d3db9ed..3ecbd4ab 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -24,6 +24,7 @@ New features * Plugins support via procedural macros. * Scripted functions are allowed in packages. +* `parse_int` and `parse_float` functions. Version 0.18.3 diff --git a/doc/src/language/num-fn.md b/doc/src/language/num-fn.md index c9427a35..df656053 100644 --- a/doc/src/language/num-fn.md +++ b/doc/src/language/num-fn.md @@ -9,11 +9,11 @@ Integer Functions The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`]) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only: -| Function | Description | -| ------------ | ----------------------------------------------------------------------- | -| `abs` | absolute value | -| `sign` | returns -1 (`INT`) if the number is negative, +1 if positive, 0 if zero | -| [`to_float`] | converts an integer type to `FLOAT` | +| Function | No available under | Description | +| -------- | :----------------: | ---------------------------------------------------------------------- | +| `abs` | | absolute value | +| `sign` | | return -1 (`INT`) if the number is negative, +1 if positive, 0 if zero | + Floating-Point Functions ----------------------- @@ -31,3 +31,16 @@ operate on `f64` only: | Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` methods and properties | | Conversion | [`to_int`] | | Testing | `is_nan`, `is_finite`, `is_infinite` methods and properties | + + +Conversion Functions +------------------- + +The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`]) +parse numbers: + +| Function | No available under | Description | +| --------------- | :----------------: | -------------------------------------------------- | +| [`to_float`] | [`no_float`] | convert an integer type to `FLOAT` | +| [`parse_int`] | | convert a [string] to `INT` with an optional radix | +| [`parse_float`] | [`no_float`] | convert a [string] to `FLOAT` | diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 2fd2a9c4..06a32ffc 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -67,6 +67,9 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { reg_functions!(lib += basic_to_int::to_int(char)); + set_exported_fn!(lib, "parse_int", parse_int); + set_exported_fn!(lib, "parse_int", parse_int_radix); + #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] { @@ -223,6 +226,21 @@ mod float_functions { Ok((x.trunc() as INT).into()) } } + + #[rhai_fn(return_raw)] + #[inline] + pub fn parse_float(s: &str) -> Result> { + s.trim() + .parse::() + .map(Into::::into) + .map_err(|err| { + EvalAltResult::ErrorArithmetic( + format!("Error parsing floating-point number '{}': {}", s, err), + Position::none(), + ) + .into() + }) + } } #[cfg(not(feature = "no_float"))] @@ -249,3 +267,30 @@ gen_conversion_functions!(numbers_to_int => to_int (i8, u8, i16, u16, i32, u32, #[cfg(not(feature = "only_i64"))] #[cfg(not(target_arch = "wasm32"))] gen_conversion_functions!(num_128_to_int => to_int (i128, u128) -> INT); + +#[export_fn(return_raw)] +fn parse_int_radix(s: &str, radix: INT) -> Result> { + if radix < 2 || radix > 36 { + return EvalAltResult::ErrorArithmetic( + format!("Invalid radix: '{}'", radix), + Position::none(), + ) + .into(); + } + + INT::from_str_radix(s.trim(), radix as u32) + .map(Into::::into) + .map_err(|err| { + EvalAltResult::ErrorArithmetic( + format!("Error parsing integer number '{}': {}", s, err), + Position::none(), + ) + .into() + }) +} + +#[export_fn(return_raw)] +#[inline(always)] +fn parse_int(s: &str) -> Result> { + parse_int_radix(s, 10) +} diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index d6f57c02..de7a0484 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -151,6 +151,7 @@ fn to_string(x: &mut T) -> ImmutableString { fn to_debug(x: &mut T) -> ImmutableString { format!("{:?}", x).into() } + #[cfg(not(feature = "no_object"))] mod format_map { use super::*; diff --git a/tests/float.rs b/tests/float.rs index 211828d0..ea87ece3 100644 --- a/tests/float.rs +++ b/tests/float.rs @@ -20,6 +20,15 @@ fn test_float() -> Result<(), Box> { Ok(()) } +#[test] +fn test_float_parse() -> Result<(), Box> { + let engine = Engine::new(); + + assert!((engine.eval::(r#"parse_float("9.9999")"#)? - 9.9999 as FLOAT).abs() < EPSILON); + + Ok(()) +} + #[test] #[cfg(not(feature = "no_object"))] fn test_struct_with_float() -> Result<(), Box> { diff --git a/tests/math.rs b/tests/math.rs index c12e3e34..fa398c83 100644 --- a/tests/math.rs +++ b/tests/math.rs @@ -108,3 +108,14 @@ fn test_math() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_math_parse() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!(engine.eval::(r#"parse_int("42")"#)?, 42); + assert_eq!(engine.eval::(r#"parse_int("42", 16)"#)?, 0x42); + assert_eq!(engine.eval::(r#"parse_int("abcdef", 16)"#)?, 0xabcdef); + + Ok(()) +}