Fix bugs and add comments to standard library.
This commit is contained in:
parent
e24848668a
commit
3667761340
@ -12,12 +12,16 @@ Bug fixes
|
||||
* `set_bit` for bit-flags with negative index now works correctly.
|
||||
* Misnamed `params` field `name` in the JSON output of `Engine::gen_fn_metadata_to_json` is fixed (was incorrectly named `type`).
|
||||
* Fixes a potential `unsafe` violation in `for` loop.
|
||||
* Missing `to_hex`, `to_octal` and `to_binary` for `i128` and `u128` are added.
|
||||
* `remove` for arrays and BLOB's now treat negative index correctly.
|
||||
* `parse_int` now works properly for negative numbers.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* Formatting of return types in functions metadata info is improved.
|
||||
* Use `SmartString` for `Scope` variable names and remove `unsafe` lifetime casting.
|
||||
* Functions in the standard library now have doc-comments (which can be obtained via `Engine::gen_fn_metadata_to_json`).
|
||||
|
||||
|
||||
Version 1.4.0
|
||||
|
@ -121,14 +121,17 @@ macro_rules! gen_arithmetic_functions {
|
||||
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||
x ^ y
|
||||
}
|
||||
/// Return true if the number is zero.
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: $arg_type) -> bool {
|
||||
x == 0
|
||||
}
|
||||
/// Return true if the number is odd.
|
||||
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||
pub fn is_odd(x: $arg_type) -> bool {
|
||||
x % 2 != 0
|
||||
}
|
||||
/// Return true if the number is even.
|
||||
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||
pub fn is_even(x: $arg_type) -> bool {
|
||||
x % 2 == 0
|
||||
@ -157,6 +160,7 @@ macro_rules! gen_signed_functions {
|
||||
pub fn plus(x: $arg_type) -> $arg_type {
|
||||
x
|
||||
}
|
||||
/// Return the absolute value of the number.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn abs(x: $arg_type) -> RhaiResultOf<$arg_type> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
@ -165,6 +169,11 @@ macro_rules! gen_signed_functions {
|
||||
Ok(x.abs())
|
||||
}
|
||||
}
|
||||
/// Return the sign (as an integer) of the number according to the following:
|
||||
///
|
||||
/// * `0` if the number is zero
|
||||
/// * `1` if the number is positive
|
||||
/// * `-1` if the number is negative
|
||||
pub fn sign(x: $arg_type) -> INT {
|
||||
x.signum() as INT
|
||||
}
|
||||
@ -216,14 +225,17 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
mod int_functions {
|
||||
/// Return true if the number is zero.
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: INT) -> bool {
|
||||
x == 0
|
||||
}
|
||||
/// Return true if the number is odd.
|
||||
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||
pub fn is_odd(x: INT) -> bool {
|
||||
x % 2 != 0
|
||||
}
|
||||
/// Return true if the number is even.
|
||||
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||
pub fn is_even(x: INT) -> bool {
|
||||
x % 2 == 0
|
||||
@ -334,9 +346,15 @@ mod f32_functions {
|
||||
pub fn plus(x: f32) -> f32 {
|
||||
x
|
||||
}
|
||||
/// Return the absolute value of the floating-point number.
|
||||
pub fn abs(x: f32) -> f32 {
|
||||
x.abs()
|
||||
}
|
||||
/// Return the sign (as an integer) of the floating-point number according to the following:
|
||||
///
|
||||
/// * `0` if the number is zero
|
||||
/// * `1` if the number is positive
|
||||
/// * `-1` if the number is negative
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn sign(x: f32) -> RhaiResultOf<INT> {
|
||||
match x.signum() {
|
||||
@ -345,6 +363,7 @@ mod f32_functions {
|
||||
x => Ok(x as INT),
|
||||
}
|
||||
}
|
||||
/// Return true if the floating-point number is zero.
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: f32) -> bool {
|
||||
x == 0.0
|
||||
@ -442,9 +461,15 @@ mod f64_functions {
|
||||
pub fn plus(x: f64) -> f64 {
|
||||
x
|
||||
}
|
||||
/// Return the absolute value of the floating-point number.
|
||||
pub fn abs(x: f64) -> f64 {
|
||||
x.abs()
|
||||
}
|
||||
/// Return the sign (as an integer) of the floating-point number according to the following:
|
||||
///
|
||||
/// * `0` if the number is zero
|
||||
/// * `1` if the number is positive
|
||||
/// * `-1` if the number is negative
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn sign(x: f64) -> RhaiResultOf<INT> {
|
||||
match x.signum() {
|
||||
@ -453,6 +478,7 @@ mod f64_functions {
|
||||
x => Ok(x as INT),
|
||||
}
|
||||
}
|
||||
/// Return true if the floating-point number is zero.
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: f64) -> bool {
|
||||
x == 0.0
|
||||
@ -536,9 +562,15 @@ pub mod decimal_functions {
|
||||
pub fn plus(x: Decimal) -> Decimal {
|
||||
x
|
||||
}
|
||||
/// Return the absolute value of the decimal number.
|
||||
pub fn abs(x: Decimal) -> Decimal {
|
||||
x.abs()
|
||||
}
|
||||
/// Return the sign (as an integer) of the decimal number according to the following:
|
||||
///
|
||||
/// * `0` if the number is zero
|
||||
/// * `1` if the number is positive
|
||||
/// * `-1` if the number is negative
|
||||
pub fn sign(x: Decimal) -> INT {
|
||||
if x == Decimal::zero() {
|
||||
0
|
||||
@ -548,6 +580,7 @@ pub mod decimal_functions {
|
||||
1
|
||||
}
|
||||
}
|
||||
/// Return true if the decimal number is zero.
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: Decimal) -> bool {
|
||||
x.is_zero()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,21 @@ def_package! {
|
||||
mod bit_field_functions {
|
||||
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
||||
|
||||
/// Return `true` if the specified `bit` in the number is set.
|
||||
///
|
||||
/// If `bit` < 0, position counts from the MSB (Most Significant Bit).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// print(x.get_bit(5)); // prints false
|
||||
///
|
||||
/// print(x.get_bit(6)); // prints true
|
||||
///
|
||||
/// print(x.get_bit(-48)); // prints true on 64-bit
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn get_bit(value: INT, bit: INT) -> RhaiResultOf<bool> {
|
||||
let bit = calc_index(BITS, bit, true, || {
|
||||
@ -29,6 +44,28 @@ mod bit_field_functions {
|
||||
|
||||
Ok((value & (1 << bit)) != 0)
|
||||
}
|
||||
/// Set the specified `bit` in the number if the new value is `true`.
|
||||
/// Clear the `bit` if the new value is `false`.
|
||||
///
|
||||
/// If `bit` < 0, position counts from the MSB (Most Significant Bit).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// x.set_bit(5, true);
|
||||
///
|
||||
/// print(x); // prints 123488
|
||||
///
|
||||
/// x.set_bit(6, false);
|
||||
///
|
||||
/// print(x); // prints 123424
|
||||
///
|
||||
/// x.set_bit(-48, false);
|
||||
///
|
||||
/// print(x); // prints 57888 on 64-bit
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn set_bit(value: &mut INT, bit: INT, new_value: bool) -> RhaiResultOf<()> {
|
||||
let bit = calc_index(BITS, bit, true, || {
|
||||
@ -44,26 +81,57 @@ mod bit_field_functions {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Return an exclusive range of bits in the number as a new number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// print(x.get_bits(5..10)); // print 18
|
||||
/// ```
|
||||
#[rhai_fn(name = "get_bits", return_raw)]
|
||||
pub fn get_bits_range(value: INT, range: ExclusiveRange) -> RhaiResultOf<INT> {
|
||||
let from = INT::max(range.start, 0);
|
||||
let to = INT::max(range.end, from);
|
||||
get_bits(value, from, to - from)
|
||||
}
|
||||
/// Return an inclusive range of bits in the number as a new number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// print(x.get_bits(5..=9)); // print 18
|
||||
/// ```
|
||||
#[rhai_fn(name = "get_bits", return_raw)]
|
||||
pub fn get_bits_range_inclusive(value: INT, range: InclusiveRange) -> RhaiResultOf<INT> {
|
||||
let from = INT::max(*range.start(), 0);
|
||||
let to = INT::max(*range.end(), from - 1);
|
||||
get_bits(value, from, to - from + 1)
|
||||
}
|
||||
/// Return a portion of bits in the number as a new number.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the MSB (Most Significant Bit).
|
||||
/// * If `bits` ≤ 0, zero is returned.
|
||||
/// * If `start` position + `bits` ≥ total number of bits, the bits after the `start` position are returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// print(x.get_bits(5, 8)); // print 18
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn get_bits(value: INT, bit: INT, bits: INT) -> RhaiResultOf<INT> {
|
||||
pub fn get_bits(value: INT, start: INT, bits: INT) -> RhaiResultOf<INT> {
|
||||
if bits <= 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let bit = calc_index(BITS, bit, true, || {
|
||||
ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into()
|
||||
let bit = calc_index(BITS, start, true, || {
|
||||
ERR::ErrorBitFieldBounds(BITS, start, Position::NONE).into()
|
||||
})?;
|
||||
|
||||
let bits = if bit + bits as usize > BITS {
|
||||
@ -81,6 +149,17 @@ mod bit_field_functions {
|
||||
|
||||
Ok(((value & (mask << bit)) >> bit) & mask)
|
||||
}
|
||||
/// Replace an exclusive range of bits in the number with a new value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// x.set_bits(5..10, 42);
|
||||
///
|
||||
/// print(x); // print 123200
|
||||
/// ```
|
||||
#[rhai_fn(name = "set_bits", return_raw)]
|
||||
pub fn set_bits_range(
|
||||
value: &mut INT,
|
||||
@ -91,6 +170,17 @@ mod bit_field_functions {
|
||||
let to = INT::max(range.end, from);
|
||||
set_bits(value, from, to - from, new_value)
|
||||
}
|
||||
/// Replace an inclusive range of bits in the number with a new value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// x.set_bits(5..=9, 42);
|
||||
///
|
||||
/// print(x); // print 123200
|
||||
/// ```
|
||||
#[rhai_fn(name = "set_bits", return_raw)]
|
||||
pub fn set_bits_range_inclusive(
|
||||
value: &mut INT,
|
||||
@ -101,6 +191,25 @@ mod bit_field_functions {
|
||||
let to = INT::max(*range.end(), from - 1);
|
||||
set_bits(value, from, to - from + 1, new_value)
|
||||
}
|
||||
/// Replace a portion of bits in the number with a new value.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the MSB (Most Significant Bit).
|
||||
/// * If `bits` ≤ 0, the number is not modified.
|
||||
/// * If `start` position + `bits` ≥ total number of bits, the bits after the `start` position are replaced.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = 123456;
|
||||
///
|
||||
/// x.set_bits(5, 8, 42);
|
||||
///
|
||||
/// print(x); // prints 124224
|
||||
///
|
||||
/// x.set_bits(-16, 10, 42);
|
||||
///
|
||||
/// print(x); // prints 11821949021971776 on 64-bit
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn set_bits(value: &mut INT, bit: INT, bits: INT, new_value: INT) -> RhaiResultOf<()> {
|
||||
if bits <= 0 {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#![cfg(not(feature = "no_index"))]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::eval::calc_offset_len;
|
||||
use crate::eval::{calc_index, calc_offset_len};
|
||||
use crate::plugin::*;
|
||||
use crate::{
|
||||
def_package, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
|
||||
@ -14,12 +14,26 @@ use std::{any::TypeId, mem};
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
|
||||
const INT_BYTES: usize = mem::size_of::<INT>();
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
const FLOAT_BYTES: usize = mem::size_of::<FLOAT>();
|
||||
|
||||
def_package! {
|
||||
/// Package of basic BLOB utilities.
|
||||
crate::BasicBlobPackage => |lib| {
|
||||
lib.standard = true;
|
||||
|
||||
combine_with_exported_module!(lib, "blob", blob_functions);
|
||||
combine_with_exported_module!(lib, "parse_int", parse_int_functions);
|
||||
combine_with_exported_module!(lib, "write_int", write_int_functions);
|
||||
combine_with_exported_module!(lib, "write_string", write_string_functions);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
combine_with_exported_module!(lib, "parse_float", parse_float_functions);
|
||||
combine_with_exported_module!(lib, "write_float", write_float_functions);
|
||||
}
|
||||
|
||||
// Register blob iterator
|
||||
lib.set_iterable::<Blob>();
|
||||
@ -28,13 +42,38 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
pub mod blob_functions {
|
||||
/// Return a new, empty BLOB.
|
||||
pub const fn blob() -> Blob {
|
||||
Blob::new()
|
||||
}
|
||||
/// Return a new BLOB of the specified length, filled with zeros.
|
||||
///
|
||||
/// If `len` ≤ 0, an empty BLOB is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob(10);
|
||||
///
|
||||
/// print(b); // prints "[0000000000000000 0000]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "blob", return_raw)]
|
||||
pub fn blob_with_capacity(ctx: NativeCallContext, len: INT) -> RhaiResultOf<Blob> {
|
||||
blob_with_capacity_and_value(ctx, len, 0)
|
||||
}
|
||||
/// Return a new BLOB of the specified length, filled with copies of the initial `value`.
|
||||
///
|
||||
/// If `len` ≤ 0, an empty BLOB is returned.
|
||||
///
|
||||
/// Only the lower 8 bits of the initial `value` are used; all other bits are ignored.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob(10, 0x42);
|
||||
///
|
||||
/// print(b); // prints "[4242424242424242 4242]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "blob", return_raw)]
|
||||
pub fn blob_with_capacity_and_value(
|
||||
ctx: NativeCallContext,
|
||||
@ -56,14 +95,40 @@ pub mod blob_functions {
|
||||
blob.resize(len, (value & 0x000000ff) as u8);
|
||||
Ok(blob)
|
||||
}
|
||||
/// Return the length of the BLOB.
|
||||
#[rhai_fn(name = "len", get = "len", pure)]
|
||||
pub fn len(blob: &mut Blob) -> INT {
|
||||
blob.len() as INT
|
||||
}
|
||||
pub fn push(blob: &mut Blob, item: INT) {
|
||||
let item = (item & 0x000000ff) as u8;
|
||||
blob.push(item);
|
||||
/// Add a new byte `value` to the end of the BLOB.
|
||||
///
|
||||
/// Only the lower 8 bits of the `value` are used; all other bits are ignored.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b.push(0x42);
|
||||
///
|
||||
/// print(b); // prints "[42]"
|
||||
/// ```
|
||||
pub fn push(blob: &mut Blob, value: INT) {
|
||||
let value = (value & 0x000000ff) as u8;
|
||||
blob.push(value);
|
||||
}
|
||||
/// Add another BLOB to the end of the BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob(5, 0x42);
|
||||
/// let b2 = blob(3, 0x11);
|
||||
///
|
||||
/// b1.push(b2);
|
||||
///
|
||||
/// print(b1); // prints "[4242424242111111]"
|
||||
/// ```
|
||||
pub fn append(blob: &mut Blob, y: Blob) {
|
||||
if !y.is_empty() {
|
||||
if blob.is_empty() {
|
||||
@ -73,6 +138,18 @@ pub mod blob_functions {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Add another BLOB to the end of the BLOB, returning it as a new BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob(5, 0x42);
|
||||
/// let b2 = blob(3, 0x11);
|
||||
///
|
||||
/// print(b1 + b2); // prints "[4242424242111111]"
|
||||
///
|
||||
/// print(b1); // prints "[4242424242]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn concat(blob1: Blob, blob2: Blob) -> Blob {
|
||||
if !blob2.is_empty() {
|
||||
@ -87,29 +164,65 @@ pub mod blob_functions {
|
||||
blob1
|
||||
}
|
||||
}
|
||||
pub fn insert(blob: &mut Blob, index: INT, item: INT) {
|
||||
let item = (item & 0x000000ff) as u8;
|
||||
/// Add a byte `value` to the BLOB at a particular `index` position.
|
||||
///
|
||||
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `index` < -length of BLOB, the byte value is added to the beginning of the BLOB.
|
||||
/// * If `index` ≥ length of BLOB, the byte value is appended to the end of the BLOB.
|
||||
///
|
||||
/// Only the lower 8 bits of the `value` are used; all other bits are ignored.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob(5, 0x42);
|
||||
///
|
||||
/// b.insert(2, 0x18);
|
||||
///
|
||||
/// print(b); // prints "[4242184242]"
|
||||
/// ```
|
||||
pub fn insert(blob: &mut Blob, index: INT, value: INT) {
|
||||
let value = (value & 0x000000ff) as u8;
|
||||
|
||||
if blob.is_empty() {
|
||||
blob.push(item);
|
||||
blob.push(value);
|
||||
return;
|
||||
}
|
||||
|
||||
let (index, _) = calc_offset_len(blob.len(), index, 0);
|
||||
|
||||
if index >= blob.len() {
|
||||
blob.push(item);
|
||||
blob.push(value);
|
||||
} else {
|
||||
blob.insert(index, item);
|
||||
blob.insert(index, value);
|
||||
}
|
||||
}
|
||||
/// Pad the BLOB to at least the specified length with copies of a specified byte `value`.
|
||||
///
|
||||
/// If `len` ≤ length of BLOB, no padding is done.
|
||||
///
|
||||
/// Only the lower 8 bits of the `value` are used; all other bits are ignored.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob(3, 0x42);
|
||||
///
|
||||
/// b.pad(5, 0x18)
|
||||
///
|
||||
/// print(b); // prints "[4242421818]"
|
||||
///
|
||||
/// b.pad(3, 0xab)
|
||||
///
|
||||
/// print(b); // prints "[4242421818]"
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn pad(ctx: NativeCallContext, blob: &mut Blob, len: INT, item: INT) -> RhaiResultOf<()> {
|
||||
pub fn pad(ctx: NativeCallContext, blob: &mut Blob, len: INT, value: INT) -> RhaiResultOf<()> {
|
||||
if len <= 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let item = (item & 0x000000ff) as u8;
|
||||
let value = (value & 0x000000ff) as u8;
|
||||
let _ctx = ctx;
|
||||
|
||||
// Check if blob will be over max size limit
|
||||
@ -121,11 +234,26 @@ pub mod blob_functions {
|
||||
}
|
||||
|
||||
if len as usize > blob.len() {
|
||||
blob.resize(len as usize, item);
|
||||
blob.resize(len as usize, value);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Remove the last byte from the BLOB and return it.
|
||||
///
|
||||
/// If the BLOB is empty, zero is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(b.pop()); // prints 5
|
||||
///
|
||||
/// print(b); // prints "[01020304]"
|
||||
/// ```
|
||||
pub fn pop(blob: &mut Blob) -> INT {
|
||||
if blob.is_empty() {
|
||||
0
|
||||
@ -133,6 +261,21 @@ pub mod blob_functions {
|
||||
blob.pop().map_or_else(|| 0, |v| v as INT)
|
||||
}
|
||||
}
|
||||
/// Remove the first byte from the BLOB and return it.
|
||||
///
|
||||
/// If the BLOB is empty, zero is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(b.shift()); // prints 1
|
||||
///
|
||||
/// print(b); // prints "[02030405]"
|
||||
/// ```
|
||||
pub fn shift(blob: &mut Blob) -> INT {
|
||||
if blob.is_empty() {
|
||||
0
|
||||
@ -140,18 +283,61 @@ pub mod blob_functions {
|
||||
blob.remove(0) as INT
|
||||
}
|
||||
}
|
||||
pub fn remove(blob: &mut Blob, len: INT) -> INT {
|
||||
if len < 0 || (len as usize) >= blob.len() {
|
||||
0
|
||||
} else {
|
||||
blob.remove(len as usize) as INT
|
||||
}
|
||||
/// Remove the byte at the specified `index` from the BLOB and return it.
|
||||
///
|
||||
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `index` < -length of BLOB, zero is returned.
|
||||
/// * If `index` ≥ length of BLOB, zero is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(x.remove(1)); // prints 2
|
||||
///
|
||||
/// print(x); // prints "[01030405]"
|
||||
///
|
||||
/// print(x.remove(-2)); // prints 4
|
||||
///
|
||||
/// print(x); // prints "[010305]"
|
||||
/// ```
|
||||
pub fn remove(blob: &mut Blob, index: INT) -> INT {
|
||||
let index = match calc_index(blob.len(), index, true, || Err(())) {
|
||||
Ok(n) => n,
|
||||
Err(_) => return 0,
|
||||
};
|
||||
|
||||
blob.remove(index) as INT
|
||||
}
|
||||
/// Clear the BLOB.
|
||||
pub fn clear(blob: &mut Blob) {
|
||||
if !blob.is_empty() {
|
||||
blob.clear();
|
||||
}
|
||||
}
|
||||
/// Cut off the BLOB at the specified length.
|
||||
///
|
||||
/// * If `len` ≤ 0, the BLOB is cleared.
|
||||
/// * If `len` ≥ length of BLOB, the BLOB is not truncated.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// b.truncate(3);
|
||||
///
|
||||
/// print(b); // prints "[010203]"
|
||||
///
|
||||
/// b.truncate(10);
|
||||
///
|
||||
/// print(b); // prints "[010203]"
|
||||
/// ```
|
||||
pub fn truncate(blob: &mut Blob, len: INT) {
|
||||
if !blob.is_empty() {
|
||||
if len >= 0 {
|
||||
@ -161,6 +347,26 @@ pub mod blob_functions {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Cut off the head of the BLOB, leaving a tail of the specified length.
|
||||
///
|
||||
/// * If `len` ≤ 0, the BLOB is cleared.
|
||||
/// * If `len` ≥ length of BLOB, the BLOB is not modified.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// b.chop(3);
|
||||
///
|
||||
/// print(b); // prints "[030405]"
|
||||
///
|
||||
/// b.chop(10);
|
||||
///
|
||||
/// print(b); // prints "[030405]"
|
||||
/// ```
|
||||
pub fn chop(blob: &mut Blob, len: INT) {
|
||||
if !blob.is_empty() {
|
||||
if len <= 0 {
|
||||
@ -170,23 +376,84 @@ pub mod blob_functions {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Reverse the BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(b); // prints "[0102030405]"
|
||||
///
|
||||
/// b.reverse();
|
||||
///
|
||||
/// print(b); // prints "[0504030201]"
|
||||
/// ```
|
||||
pub fn reverse(blob: &mut Blob) {
|
||||
if !blob.is_empty() {
|
||||
blob.reverse();
|
||||
}
|
||||
}
|
||||
/// Replace an exclusive range of the BLOB with another BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob(10, 0x42);
|
||||
/// let b2 = blob(5, 0x18);
|
||||
///
|
||||
/// b1.splice(1..4, b2);
|
||||
///
|
||||
/// print(b1); // prints "[4218181818184242 42424242]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "splice")]
|
||||
pub fn splice_range(blob: &mut Blob, range: ExclusiveRange, replace: Blob) {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
splice(blob, start, end - start, replace)
|
||||
}
|
||||
/// Replace an inclusive range of the BLOB with another BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob(10, 0x42);
|
||||
/// let b2 = blob(5, 0x18);
|
||||
///
|
||||
/// b1.splice(1..=4, b2);
|
||||
///
|
||||
/// print(b1); // prints "[4218181818184242 424242]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "splice")]
|
||||
pub fn splice_range_inclusive(blob: &mut Blob, range: InclusiveRange, replace: Blob) {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
splice(blob, start, end - start + 1, replace)
|
||||
}
|
||||
/// Replace a portion of the BLOB with another BLOB.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `start` < -length of BLOB, position counts from the beginning of the BLOB.
|
||||
/// * If `start` ≥ length of BLOB, the other BLOB is appended to the end of the BLOB.
|
||||
/// * If `len` ≤ 0, the other BLOB is inserted into the BLOB at the `start` position without replacing anything.
|
||||
/// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is replaced.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob(10, 0x42);
|
||||
/// let b2 = blob(5, 0x18);
|
||||
///
|
||||
/// b1.splice(1, 3, b2);
|
||||
///
|
||||
/// print(b1); // prints "[4218181818184242 42424242]"
|
||||
///
|
||||
/// b1.splice(-5, 4, b2);
|
||||
///
|
||||
/// print(b1); // prints "[4218181818184218 1818181842]"
|
||||
/// ```
|
||||
pub fn splice(blob: &mut Blob, start: INT, len: INT, replace: Blob) {
|
||||
if blob.is_empty() {
|
||||
*blob = replace;
|
||||
@ -201,18 +468,65 @@ pub mod blob_functions {
|
||||
blob.splice(start..start + len, replace);
|
||||
}
|
||||
}
|
||||
/// Copy an exclusive range of the BLOB and return it as a new BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(b.extract(1..3)); // prints "[0203]"
|
||||
///
|
||||
/// print(b); // prints "[0102030405]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "extract")]
|
||||
pub fn extract_range(blob: &mut Blob, range: ExclusiveRange) -> Blob {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
extract(blob, start, end - start)
|
||||
}
|
||||
/// Copy an inclusive range of the BLOB and return it as a new BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(b.extract(1..=3)); // prints "[020304]"
|
||||
///
|
||||
/// print(b); // prints "[0102030405]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "extract")]
|
||||
pub fn extract_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
extract(blob, start, end - start + 1)
|
||||
}
|
||||
/// Copy a portion of the BLOB and return it as a new BLOB.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `start` < -length of BLOB, position counts from the beginning of the BLOB.
|
||||
/// * If `start` ≥ length of BLOB, an empty BLOB is returned.
|
||||
/// * If `len` ≤ 0, an empty BLOB is returned.
|
||||
/// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is copied and returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(b.extract(1, 3)); // prints "[020303]"
|
||||
///
|
||||
/// print(b.extract(-3, 2)); // prints "[0304]"
|
||||
///
|
||||
/// print(b); // prints "[0102030405]"
|
||||
/// ```
|
||||
pub fn extract(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
return Blob::new();
|
||||
@ -226,19 +540,59 @@ pub mod blob_functions {
|
||||
blob[start..start + len].to_vec()
|
||||
}
|
||||
}
|
||||
/// Copy a portion of the BLOB beginning at the `start` position till the end and return it as
|
||||
/// a new BLOB.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `start` < -length of BLOB, the entire BLOB is copied and returned.
|
||||
/// * If `start` ≥ length of BLOB, an empty BLOB is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b = blob();
|
||||
///
|
||||
/// b += 1; b += 2; b += 3; b += 4; b += 5;
|
||||
///
|
||||
/// print(b.extract(2)); // prints "[030405]"
|
||||
///
|
||||
/// print(b.extract(-3)); // prints "[030405]"
|
||||
///
|
||||
/// print(b); // prints "[0102030405]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "extract")]
|
||||
pub fn extract_tail(blob: &mut Blob, start: INT) -> Blob {
|
||||
extract(blob, start, INT::MAX)
|
||||
}
|
||||
/// Cut off the BLOB at `index` and return it as a new BLOB.
|
||||
///
|
||||
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `index` is zero, the entire BLOB is cut and returned.
|
||||
/// * If `index` < -length of BLOB, the entire BLOB is cut and returned.
|
||||
/// * If `index` ≥ length of BLOB, nothing is cut from the BLOB and an empty BLOB is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob();
|
||||
///
|
||||
/// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5;
|
||||
///
|
||||
/// let b2 = b1.split(2);
|
||||
///
|
||||
/// print(b2); // prints "[030405]"
|
||||
///
|
||||
/// print(b1); // prints "[0102]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "split")]
|
||||
pub fn split_at(blob: &mut Blob, start: INT) -> Blob {
|
||||
pub fn split_at(blob: &mut Blob, index: INT) -> Blob {
|
||||
if blob.is_empty() {
|
||||
return Blob::new();
|
||||
}
|
||||
|
||||
let (start, len) = calc_offset_len(blob.len(), start, INT::MAX);
|
||||
let (index, len) = calc_offset_len(blob.len(), index, INT::MAX);
|
||||
|
||||
if start == 0 {
|
||||
if index == 0 {
|
||||
if len > blob.len() {
|
||||
mem::take(blob)
|
||||
} else {
|
||||
@ -246,26 +600,95 @@ pub mod blob_functions {
|
||||
result.extend(blob.drain(blob.len() - len..));
|
||||
result
|
||||
}
|
||||
} else if start >= blob.len() {
|
||||
} else if index >= blob.len() {
|
||||
Blob::new()
|
||||
} else {
|
||||
let mut result = Blob::new();
|
||||
result.extend(blob.drain(start as usize..));
|
||||
result.extend(blob.drain(index as usize..));
|
||||
result
|
||||
}
|
||||
}
|
||||
/// Remove all bytes in the BLOB within an exclusive range and return them as a new BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob();
|
||||
///
|
||||
/// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5;
|
||||
///
|
||||
/// let b2 = b1.drain(1..3);
|
||||
///
|
||||
/// print(b1); // prints "[010405]"
|
||||
///
|
||||
/// print(b2); // prints "[0203]"
|
||||
///
|
||||
/// let b3 = b1.drain(2..3);
|
||||
///
|
||||
/// print(b1); // prints "[0104]"
|
||||
///
|
||||
/// print(b3); // prints "[05]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "drain")]
|
||||
pub fn drain_range(blob: &mut Blob, range: ExclusiveRange) -> Blob {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
drain(blob, start, end - start)
|
||||
}
|
||||
/// Remove all bytes in the BLOB within an inclusive range and return them as a new BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob();
|
||||
///
|
||||
/// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5;
|
||||
///
|
||||
/// let b2 = b1.drain(1..=2);
|
||||
///
|
||||
/// print(b1); // prints "[010405]"
|
||||
///
|
||||
/// print(b2); // prints "[0203]"
|
||||
///
|
||||
/// let b3 = b1.drain(2..=2);
|
||||
///
|
||||
/// print(b1); // prints "[0104]"
|
||||
///
|
||||
/// print(b3); // prints "[05]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "drain")]
|
||||
pub fn drain_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
drain(blob, start, end - start + 1)
|
||||
}
|
||||
/// Remove all bytes within a portion of the BLOB and return them as a new BLOB.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `start` < -length of BLOB, position counts from the beginning of the BLOB.
|
||||
/// * If `start` ≥ length of BLOB, nothing is removed and an empty BLOB is returned.
|
||||
/// * If `len` ≤ 0, nothing is removed and an empty BLOB is returned.
|
||||
/// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB after the `start` position is removed and returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob();
|
||||
///
|
||||
/// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5;
|
||||
///
|
||||
/// let b2 = b1.drain(1, 2);
|
||||
///
|
||||
/// print(b1); // prints "[010405]"
|
||||
///
|
||||
/// print(b2); // prints "[0203]"
|
||||
///
|
||||
/// let b3 = b1.drain(-1, 1);
|
||||
///
|
||||
/// print(b3); // prints "[0104]"
|
||||
///
|
||||
/// print(z); // prints "[5]"
|
||||
/// ```
|
||||
pub fn drain(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
return Blob::new();
|
||||
@ -279,18 +702,87 @@ pub mod blob_functions {
|
||||
blob.drain(start..start + len).collect()
|
||||
}
|
||||
}
|
||||
/// Remove all bytes in the BLOB not within an exclusive range and return them as a new BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob();
|
||||
///
|
||||
/// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5;
|
||||
///
|
||||
/// let b2 = b1.retain(1..4);
|
||||
///
|
||||
/// print(b1); // prints "[020304]"
|
||||
///
|
||||
/// print(b2); // prints "[0105]"
|
||||
///
|
||||
/// let b3 = b1.retain(1..3);
|
||||
///
|
||||
/// print(b1); // prints "[0304]"
|
||||
///
|
||||
/// print(b2); // prints "[01]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "retain")]
|
||||
pub fn retain_range(blob: &mut Blob, range: ExclusiveRange) -> Blob {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
retain(blob, start, end - start)
|
||||
}
|
||||
/// Remove all bytes in the BLOB not within an inclusive range and return them as a new BLOB.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob();
|
||||
///
|
||||
/// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5;
|
||||
///
|
||||
/// let b2 = b1.retain(1..=3);
|
||||
///
|
||||
/// print(b1); // prints "[020304]"
|
||||
///
|
||||
/// print(b2); // prints "[0105]"
|
||||
///
|
||||
/// let b3 = b1.retain(1..=2);
|
||||
///
|
||||
/// print(b1); // prints "[0304]"
|
||||
///
|
||||
/// print(b2); // prints "[01]"
|
||||
/// ```
|
||||
#[rhai_fn(name = "retain")]
|
||||
pub fn retain_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
retain(blob, start, end - start + 1)
|
||||
}
|
||||
/// Remove all bytes not within a portion of the BLOB and return them as a new BLOB.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||
/// * If `start` < -length of BLOB, position counts from the beginning of the BLOB.
|
||||
/// * If `start` ≥ length of BLOB, all elements are removed returned.
|
||||
/// * If `len` ≤ 0, all elements are removed and returned.
|
||||
/// * If `start` position + `len` ≥ length of BLOB, entire portion of the BLOB before the `start` position is removed and returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let b1 = blob();
|
||||
///
|
||||
/// b1 += 1; b1 += 2; b1 += 3; b1 += 4; b1 += 5;
|
||||
///
|
||||
/// let b2 = b1.retain(1, 2);
|
||||
///
|
||||
/// print(b1); // prints "[0203]"
|
||||
///
|
||||
/// print(b2); // prints "[010405]"
|
||||
///
|
||||
/// let b3 = b1.retain(-1, 1);
|
||||
///
|
||||
/// print(b1); // prints "[03]"
|
||||
///
|
||||
/// print(b3); // prints "[02]"
|
||||
/// ```
|
||||
pub fn retain(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
return Blob::new();
|
||||
@ -307,7 +799,10 @@ pub mod blob_functions {
|
||||
drained
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod parse_int_functions {
|
||||
#[inline]
|
||||
fn parse_int(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> INT {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
@ -319,8 +814,6 @@ pub mod blob_functions {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const INT_BYTES: usize = mem::size_of::<INT>();
|
||||
|
||||
let len = usize::min(len, INT_BYTES);
|
||||
|
||||
let mut buf = [0_u8; INT_BYTES];
|
||||
@ -364,7 +857,70 @@ pub mod blob_functions {
|
||||
pub fn parse_be_int(blob: &mut Blob, start: INT, len: INT) -> INT {
|
||||
parse_int(blob, start, len, false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod parse_float_functions {
|
||||
#[inline]
|
||||
fn parse_float(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> FLOAT {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let (start, len) = calc_offset_len(blob.len(), start, len);
|
||||
|
||||
if len == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let len = usize::min(len, FLOAT_BYTES);
|
||||
|
||||
let mut buf = [0_u8; FLOAT_BYTES];
|
||||
|
||||
buf[..len].copy_from_slice(&blob[start..][..len]);
|
||||
|
||||
if is_le {
|
||||
FLOAT::from_le_bytes(buf)
|
||||
} else {
|
||||
FLOAT::from_be_bytes(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(name = "parse_le_float")]
|
||||
pub fn parse_le_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
parse_le_float(blob, start, end - start)
|
||||
}
|
||||
#[rhai_fn(name = "parse_le_float")]
|
||||
pub fn parse_le_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
parse_le_float(blob, start, end - start + 1)
|
||||
}
|
||||
pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
||||
parse_float(blob, start, len, true)
|
||||
}
|
||||
#[rhai_fn(name = "parse_be_float")]
|
||||
pub fn parse_be_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
parse_be_float(blob, start, end - start)
|
||||
}
|
||||
#[rhai_fn(name = "parse_be_float")]
|
||||
pub fn parse_be_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
parse_be_float(blob, start, end - start + 1)
|
||||
}
|
||||
pub fn parse_be_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
||||
parse_float(blob, start, len, false)
|
||||
}
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod write_int_functions {
|
||||
#[inline]
|
||||
fn write_int(blob: &mut Blob, start: INT, len: INT, value: INT, is_le: bool) {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
@ -377,8 +933,6 @@ pub mod blob_functions {
|
||||
return;
|
||||
}
|
||||
|
||||
const INT_BYTES: usize = mem::size_of::<INT>();
|
||||
|
||||
let len = usize::min(len, INT_BYTES);
|
||||
|
||||
let buf = if is_le {
|
||||
@ -389,13 +943,13 @@ pub mod blob_functions {
|
||||
|
||||
blob[start..][..len].copy_from_slice(&buf[..len]);
|
||||
}
|
||||
#[rhai_fn(name = "write_le_int")]
|
||||
#[rhai_fn(name = "write_le")]
|
||||
pub fn write_le_int_range(blob: &mut Blob, range: ExclusiveRange, value: INT) {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
write_le_int(blob, start, end - start, value)
|
||||
}
|
||||
#[rhai_fn(name = "write_le_int")]
|
||||
#[rhai_fn(name = "write_le")]
|
||||
pub fn write_le_int_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: INT) {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
@ -421,73 +975,11 @@ pub mod blob_functions {
|
||||
pub fn write_be_int(blob: &mut Blob, start: INT, len: INT, value: INT) {
|
||||
write_int(blob, start, len, value, false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline]
|
||||
fn parse_float(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> FLOAT {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let (start, len) = calc_offset_len(blob.len(), start, len);
|
||||
|
||||
if len == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const FLOAT_BYTES: usize = mem::size_of::<FLOAT>();
|
||||
|
||||
let len = usize::min(len, FLOAT_BYTES);
|
||||
|
||||
let mut buf = [0_u8; FLOAT_BYTES];
|
||||
|
||||
buf[..len].copy_from_slice(&blob[start..][..len]);
|
||||
|
||||
if is_le {
|
||||
FLOAT::from_le_bytes(buf)
|
||||
} else {
|
||||
FLOAT::from_be_bytes(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "parse_le_float")]
|
||||
pub fn parse_le_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
parse_le_float(blob, start, end - start)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "parse_le_float")]
|
||||
pub fn parse_le_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
parse_le_float(blob, start, end - start + 1)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
||||
parse_float(blob, start, len, true)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "parse_be_float")]
|
||||
pub fn parse_be_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
parse_be_float(blob, start, end - start)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "parse_be_float")]
|
||||
pub fn parse_be_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
parse_be_float(blob, start, end - start + 1)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub fn parse_be_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
||||
parse_float(blob, start, len, false)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[export_module]
|
||||
mod write_float_functions {
|
||||
#[inline]
|
||||
fn write_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT, is_le: bool) {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
@ -500,8 +992,6 @@ pub mod blob_functions {
|
||||
return;
|
||||
}
|
||||
|
||||
const FLOAT_BYTES: usize = mem::size_of::<FLOAT>();
|
||||
|
||||
let len = usize::min(len, FLOAT_BYTES);
|
||||
let buf = if is_le {
|
||||
value.to_le_bytes()
|
||||
@ -511,44 +1001,42 @@ pub mod blob_functions {
|
||||
|
||||
blob[start..][..len].copy_from_slice(&buf[..len]);
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "write_le_float")]
|
||||
#[rhai_fn(name = "write_le")]
|
||||
pub fn write_le_float_range(blob: &mut Blob, range: ExclusiveRange, value: FLOAT) {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
write_le_float(blob, start, end - start, value)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "write_le_float")]
|
||||
#[rhai_fn(name = "write_le")]
|
||||
pub fn write_le_float_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: FLOAT) {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
write_le_float(blob, start, end - start + 1, value)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "write_le")]
|
||||
pub fn write_le_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
|
||||
write_float(blob, start, len, value, true)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "write_be")]
|
||||
pub fn write_be_float_range(blob: &mut Blob, range: ExclusiveRange, value: FLOAT) {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
write_be_float(blob, start, end - start, value)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "write_be")]
|
||||
pub fn write_be_float_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: FLOAT) {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
write_be_float(blob, start, end - start + 1, value)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "write_be")]
|
||||
pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
|
||||
write_float(blob, start, len, value, false)
|
||||
}
|
||||
}
|
||||
|
||||
#[export_module]
|
||||
mod write_string_functions {
|
||||
#[inline]
|
||||
fn write_string(blob: &mut Blob, start: INT, len: INT, string: &str, ascii_only: bool) {
|
||||
if len <= 0 || blob.is_empty() || string.is_empty() {
|
||||
|
@ -14,122 +14,34 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
mod fn_ptr_functions {
|
||||
/// Return the name of the function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// fn double(x) { x * 2 }
|
||||
///
|
||||
/// let f = Fn("double");
|
||||
///
|
||||
/// print(f.name); // prints "double"
|
||||
/// ```
|
||||
#[rhai_fn(name = "name", get = "name", pure)]
|
||||
pub fn name(fn_ptr: &mut FnPtr) -> ImmutableString {
|
||||
fn_ptr.fn_name_raw().into()
|
||||
}
|
||||
|
||||
/// Return `true` if the function is an anonymous function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let f = |x| x * 2;
|
||||
///
|
||||
/// print(f.is_anonymous); // prints true
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[rhai_fn(name = "is_anonymous", get = "is_anonymous", pure)]
|
||||
pub fn is_anonymous(fn_ptr: &mut FnPtr) -> bool {
|
||||
fn_ptr.is_anonymous()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> crate::Array {
|
||||
collect_fn_metadata(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
||||
use crate::{ast::ScriptFnDef, Array, Identifier, Map};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
// Create a metadata record for a function.
|
||||
fn make_metadata(
|
||||
dict: &BTreeSet<Identifier>,
|
||||
namespace: Option<Identifier>,
|
||||
func: &ScriptFnDef,
|
||||
) -> Map {
|
||||
const DICT: &str = "key exists";
|
||||
|
||||
let mut map = Map::new();
|
||||
|
||||
if let Some(ns) = namespace {
|
||||
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
|
||||
}
|
||||
map.insert(
|
||||
dict.get("name").expect(DICT).clone(),
|
||||
func.name.clone().into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("access").expect(DICT).clone(),
|
||||
match func.access {
|
||||
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
||||
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("is_anonymous").expect(DICT).clone(),
|
||||
func.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("params").expect(DICT).clone(),
|
||||
func.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Into::into)
|
||||
.collect::<Array>()
|
||||
.into(),
|
||||
);
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
// Intern strings
|
||||
let dict: BTreeSet<Identifier> = [
|
||||
"namespace",
|
||||
"name",
|
||||
"access",
|
||||
"public",
|
||||
"private",
|
||||
"is_anonymous",
|
||||
"params",
|
||||
]
|
||||
.iter()
|
||||
.map(|&s| s.into())
|
||||
.collect();
|
||||
|
||||
let mut _list = ctx.iter_namespaces().flat_map(Module::iter_script_fn).fold(
|
||||
Array::new(),
|
||||
|mut list, (_, _, _, _, f)| {
|
||||
list.push(make_metadata(&dict, None, f).into());
|
||||
list
|
||||
},
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
// Recursively scan modules for script-defined functions.
|
||||
fn scan_module(
|
||||
list: &mut Array,
|
||||
dict: &BTreeSet<Identifier>,
|
||||
namespace: Identifier,
|
||||
module: &Module,
|
||||
) {
|
||||
module.iter_script_fn().for_each(|(_, _, _, _, f)| {
|
||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
||||
});
|
||||
module.iter_sub_modules().for_each(|(ns, m)| {
|
||||
let ns = format!(
|
||||
"{}{}{}",
|
||||
namespace,
|
||||
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
||||
ns
|
||||
);
|
||||
scan_module(list, dict, ns.into(), m.as_ref())
|
||||
});
|
||||
}
|
||||
|
||||
ctx.iter_imports_raw()
|
||||
.for_each(|(ns, m)| scan_module(&mut _list, &dict, ns.clone(), m.as_ref()));
|
||||
}
|
||||
|
||||
_list
|
||||
}
|
||||
|
@ -246,10 +246,20 @@ macro_rules! reg_range {
|
||||
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
$lib.update_fn_metadata(_hash, &[
|
||||
$lib.update_fn_metadata_with_comments(_hash, [
|
||||
concat!("from: ", stringify!($y)),
|
||||
concat!("to: ", stringify!($y)),
|
||||
concat!("Iterator<Item=", stringify!($y), ">")
|
||||
concat!("Iterator<Item=", stringify!($y), ">"),
|
||||
], [
|
||||
"/// Return an iterator over the range of `from..to`.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// for n in range(8, 18) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]);
|
||||
|
||||
$lib.set_iterator::<RangeInclusive<$y>>();
|
||||
@ -261,11 +271,27 @@ macro_rules! reg_range {
|
||||
let _hash = $lib.set_native_fn($x, |from: $y, to: $y, step: $y| StepRange::new(from, to, step));
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
$lib.update_fn_metadata(_hash, &[
|
||||
$lib.update_fn_metadata_with_comments(_hash, [
|
||||
concat!("from: ", stringify!($y)),
|
||||
concat!("to: ", stringify!($y)),
|
||||
concat!("step: ", stringify!($y)),
|
||||
concat!("Iterator<Item=", stringify!($y), ">")
|
||||
], [
|
||||
"/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.",
|
||||
"///",
|
||||
"/// If `from` > `to` and `step` < 0, the iteration goes backwards.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// for n in range(8, 18, 3) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"///",
|
||||
"/// for n in range(18, 8, -3) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]);
|
||||
)*
|
||||
};
|
||||
@ -359,7 +385,27 @@ def_package! {
|
||||
|
||||
let _hash = lib.set_native_fn("range", StepFloatRange::new);
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"],
|
||||
[
|
||||
"/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.",
|
||||
"///",
|
||||
"/// If `from` > `to` and `step` < 0, the iteration goes backwards.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// for n in range(8.0, 18.0, 3.0) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"///",
|
||||
"/// for n in range(18.0, 8.0, -3.0) {",
|
||||
"/// print(n);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
@ -421,7 +467,15 @@ def_package! {
|
||||
|
||||
let _hash = lib.set_native_fn("range", StepDecimalRange::new);
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"],
|
||||
[
|
||||
"/// Return an iterator over the range of `from..to`, each iterator increasing by `step`.",
|
||||
"///",
|
||||
"/// If `from` > `to` and `step` < 0, the iteration goes backwards.",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Register string iterator
|
||||
@ -433,7 +487,21 @@ def_package! {
|
||||
Ok(CharsStream::new(string, from, to - from))
|
||||
});
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["string: &str", "range: Range<INT>", "Iterator<Item=char>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["string: &str", "range: Range<INT>", "Iterator<Item=char>"],
|
||||
[
|
||||
"/// Return an iterator over an exclusive range of characters in the string.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
r#"/// for ch in "hello, world!".chars(2..5) {"#,
|
||||
"/// print(ch);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| {
|
||||
let from = INT::max(*range.start(), 0);
|
||||
@ -441,25 +509,105 @@ def_package! {
|
||||
Ok(CharsStream::new(string, from, to-from + 1))
|
||||
});
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive<INT>", "Iterator<Item=char>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["string: &str", "range: RangeInclusive<INT>", "Iterator<Item=char>"],
|
||||
[
|
||||
"/// Return an iterator over an inclusive range of characters in the string.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
r#"/// for ch in "hello, world!".chars(2..=6) {"#,
|
||||
"/// print(ch);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["string: &str", "start: INT", "len: INT", "Iterator<Item=char>"],
|
||||
[
|
||||
"/// Return an iterator over a portion of characters in the string.",
|
||||
"///",
|
||||
"/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).",
|
||||
"/// * If `start` < -length of string, position counts from the beginning of the string.",
|
||||
"/// * If `start` ≥ length of string, an empty iterator is returned.",
|
||||
"/// * If `len` ≤ 0, an empty iterator is returned.",
|
||||
"/// * If `start` position + `len` ≥ length of string, all characters of the string after the `start` position are iterated.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
r#"/// for ch in "hello, world!".chars(2, 4) {"#,
|
||||
"/// print(ch);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("chars", |string, from| Ok(CharsStream::new(string, from, INT::MAX)));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "Iterator<Item=char>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["string: &str", "from: INT", "Iterator<Item=char>"],
|
||||
[
|
||||
"/// Return an iterator over the characters in the string starting from the `start` position.",
|
||||
"///",
|
||||
"/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).",
|
||||
"/// * If `start` < -length of string, position counts from the beginning of the string.",
|
||||
"/// * If `start` ≥ length of string, an empty iterator is returned.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
r#"/// for ch in "hello, world!".chars(2) {"#,
|
||||
"/// print(ch);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("chars", |string| Ok(CharsStream::new(string, 0, INT::MAX)));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["string: &str", "Iterator<Item=char>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["string: &str", "Iterator<Item=char>"],
|
||||
[
|
||||
"/// Return an iterator over the characters in the string.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
r#"/// for ch in "hello, world!".chars() {"#,
|
||||
"/// print(ch);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX)));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["string: &mut ImmutableString", "Iterator<Item=char>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["string: &mut ImmutableString", "Iterator<Item=char>"],
|
||||
[
|
||||
"/// Return an iterator over all the characters in the string.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
r#"/// for ch in "hello, world!".chars {"#,
|
||||
"/// print(ch);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Register bit-field iterator
|
||||
@ -471,7 +619,23 @@ def_package! {
|
||||
BitRange::new(value, from, to - from)
|
||||
});
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "range: Range<INT>", "Iterator<Item=bool>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["value: INT", "range: Range<INT>", "Iterator<Item=bool>"],
|
||||
[
|
||||
"/// Return an iterator over an exclusive range of bits in the number.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// let x = 123456;",
|
||||
"///",
|
||||
"/// for bit in x.bits(10..24) {",
|
||||
"/// print(bit);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| {
|
||||
let from = INT::max(*range.start(), 0);
|
||||
@ -479,25 +643,111 @@ def_package! {
|
||||
BitRange::new(value, from, to - from + 1)
|
||||
});
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive<INT>", "Iterator<Item=bool>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["value: INT", "range: RangeInclusive<INT>", "Iterator<Item=bool>"],
|
||||
[
|
||||
"/// Return an iterator over an inclusive range of bits in the number.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// let x = 123456;",
|
||||
"///",
|
||||
"/// for bit in x.bits(10..=23) {",
|
||||
"/// print(bit);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("bits", BitRange::new);
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"],
|
||||
[
|
||||
"/// Return an iterator over a portion of bits in the number.",
|
||||
"///",
|
||||
"/// * If `start` < 0, position counts from the MSB (Most Significant Bit)>.",
|
||||
"/// * If `len` ≤ 0, an empty iterator is returned.",
|
||||
"/// * If `start` position + `len` ≥ length of string, all bits of the number after the `start` position are iterated.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// let x = 123456;",
|
||||
"///",
|
||||
"/// for bit in x.bits(10, 8) {",
|
||||
"/// print(bit);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator<Item=bool>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["value: INT", "from: INT", "Iterator<Item=bool>"],
|
||||
[
|
||||
"/// Return an iterator over the bits in the number starting from the specified `start` position.",
|
||||
"///",
|
||||
"/// If `start` < 0, position counts from the MSB (Most Significant Bit)>.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// let x = 123456;",
|
||||
"///",
|
||||
"/// for bit in x.bits(10) {",
|
||||
"/// print(bit);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) );
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: INT", "Iterator<Item=bool>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["value: INT", "Iterator<Item=bool>"],
|
||||
[
|
||||
"/// Return an iterator over all the bits in the number.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// let x = 123456;",
|
||||
"///",
|
||||
"/// for bit in x.bits() {",
|
||||
"/// print(bit);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) );
|
||||
#[cfg(feature = "metadata")]
|
||||
lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range<INT>", "Iterator<Item=bool>"]);
|
||||
lib.update_fn_metadata_with_comments(
|
||||
_hash,
|
||||
["value: &mut INT", "range: Range<INT>", "Iterator<Item=bool>"],
|
||||
[
|
||||
"/// Return an iterator over all the bits in the number.",
|
||||
"///",
|
||||
"/// # Example",
|
||||
"///",
|
||||
"/// ```rhai",
|
||||
"/// let x = 123456;",
|
||||
"///",
|
||||
"/// for bit in x.bits {",
|
||||
"/// print(bit);",
|
||||
"/// }",
|
||||
"/// ```"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
combine_with_exported_module!(lib, "range", range_functions);
|
||||
@ -506,37 +756,45 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
mod range_functions {
|
||||
/// Return the start of the exclusive range.
|
||||
#[rhai_fn(get = "start", name = "start", pure)]
|
||||
pub fn start(range: &mut ExclusiveRange) -> INT {
|
||||
range.start
|
||||
}
|
||||
/// Return the end of the exclusive range.
|
||||
#[rhai_fn(get = "end", name = "end", pure)]
|
||||
pub fn end(range: &mut ExclusiveRange) -> INT {
|
||||
range.end
|
||||
}
|
||||
/// Return `true` if the range is inclusive.
|
||||
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
||||
pub fn is_inclusive(range: &mut ExclusiveRange) -> bool {
|
||||
let _range = range;
|
||||
false
|
||||
}
|
||||
/// Return `true` if the range is exclusive.
|
||||
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
|
||||
pub fn is_exclusive(range: &mut ExclusiveRange) -> bool {
|
||||
let _range = range;
|
||||
true
|
||||
}
|
||||
/// Return the start of the inclusive range.
|
||||
#[rhai_fn(get = "start", name = "start", pure)]
|
||||
pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
|
||||
*range.start()
|
||||
}
|
||||
/// Return the end of the inclusive range.
|
||||
#[rhai_fn(get = "end", name = "end", pure)]
|
||||
pub fn end_inclusive(range: &mut InclusiveRange) -> INT {
|
||||
*range.end()
|
||||
}
|
||||
/// Return `true` if the range is inclusive.
|
||||
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
||||
pub fn is_inclusive_inclusive(range: &mut InclusiveRange) -> bool {
|
||||
let _range = range;
|
||||
true
|
||||
}
|
||||
/// Return `true` if the range is exclusive.
|
||||
#[rhai_fn(get = "is_exclusive", name = "is_exclusive", pure)]
|
||||
pub fn is_exclusive_inclusive(range: &mut InclusiveRange) -> bool {
|
||||
let _range = range;
|
||||
|
@ -53,4 +53,112 @@ mod core_functions {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> crate::Array {
|
||||
collect_fn_metadata(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
||||
use crate::{ast::ScriptFnDef, Array, Identifier, Map};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
// Create a metadata record for a function.
|
||||
fn make_metadata(
|
||||
dict: &BTreeSet<Identifier>,
|
||||
namespace: Option<Identifier>,
|
||||
func: &ScriptFnDef,
|
||||
) -> Map {
|
||||
const DICT: &str = "key exists";
|
||||
|
||||
let mut map = Map::new();
|
||||
|
||||
if let Some(ns) = namespace {
|
||||
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
|
||||
}
|
||||
map.insert(
|
||||
dict.get("name").expect(DICT).clone(),
|
||||
func.name.clone().into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("access").expect(DICT).clone(),
|
||||
match func.access {
|
||||
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
||||
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("is_anonymous").expect(DICT).clone(),
|
||||
func.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("params").expect(DICT).clone(),
|
||||
func.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Into::into)
|
||||
.collect::<Array>()
|
||||
.into(),
|
||||
);
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
// Intern strings
|
||||
let dict: BTreeSet<Identifier> = [
|
||||
"namespace",
|
||||
"name",
|
||||
"access",
|
||||
"public",
|
||||
"private",
|
||||
"is_anonymous",
|
||||
"params",
|
||||
]
|
||||
.iter()
|
||||
.map(|&s| s.into())
|
||||
.collect();
|
||||
|
||||
let mut _list = ctx.iter_namespaces().flat_map(Module::iter_script_fn).fold(
|
||||
Array::new(),
|
||||
|mut list, (_, _, _, _, f)| {
|
||||
list.push(make_metadata(&dict, None, f).into());
|
||||
list
|
||||
},
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
// Recursively scan modules for script-defined functions.
|
||||
fn scan_module(
|
||||
list: &mut Array,
|
||||
dict: &BTreeSet<Identifier>,
|
||||
namespace: Identifier,
|
||||
module: &Module,
|
||||
) {
|
||||
module.iter_script_fn().for_each(|(_, _, _, _, f)| {
|
||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
||||
});
|
||||
module.iter_sub_modules().for_each(|(ns, m)| {
|
||||
let ns = format!(
|
||||
"{}{}{}",
|
||||
namespace,
|
||||
crate::tokenizer::Token::DoubleColon.literal_syntax(),
|
||||
ns
|
||||
);
|
||||
scan_module(list, dict, ns.into(), m.as_ref())
|
||||
});
|
||||
}
|
||||
|
||||
ctx.iter_imports_raw()
|
||||
.for_each(|(ns, m)| scan_module(&mut _list, &dict, ns.clone(), m.as_ref()));
|
||||
}
|
||||
|
||||
_list
|
||||
}
|
||||
|
@ -20,15 +20,32 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
mod map_functions {
|
||||
/// Return the number of properties in the object map.
|
||||
#[rhai_fn(pure)]
|
||||
pub fn len(map: &mut Map) -> INT {
|
||||
map.len() as INT
|
||||
}
|
||||
/// Clear the object map.
|
||||
pub fn clear(map: &mut Map) {
|
||||
if !map.is_empty() {
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
/// Remove any property of the specified `name` from the object map, returning its value.
|
||||
///
|
||||
/// If the property does not exist, `()` is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m = #{a:1, b:2, c:3};
|
||||
///
|
||||
/// let x = m.remove("b");
|
||||
///
|
||||
/// print(x); // prints 2
|
||||
///
|
||||
/// print(m); // prints "#{a:1, c:3}"
|
||||
/// ```
|
||||
pub fn remove(map: &mut Map, name: ImmutableString) -> Dynamic {
|
||||
if !map.is_empty() {
|
||||
map.remove(name.as_str()).unwrap_or_else(|| Dynamic::UNIT)
|
||||
@ -36,12 +53,38 @@ mod map_functions {
|
||||
Dynamic::UNIT
|
||||
}
|
||||
}
|
||||
/// Add all property values of another object map into the object map.
|
||||
/// Existing property values of the same names are replaced.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m = #{a:1, b:2, c:3};
|
||||
/// let n = #{a: 42, d:0};
|
||||
///
|
||||
/// m.mixin(n);
|
||||
///
|
||||
/// print(m); // prints "#{a:42, b:2, c:3, d:0}"
|
||||
/// ```
|
||||
#[rhai_fn(name = "mixin", name = "+=")]
|
||||
pub fn mixin(map: &mut Map, map2: Map) {
|
||||
if !map2.is_empty() {
|
||||
map.extend(map2.into_iter());
|
||||
}
|
||||
}
|
||||
/// Make a copy of the object map, add all property values of another object map
|
||||
/// (existing property values of the same names are replaced), then returning it.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m = #{a:1, b:2, c:3};
|
||||
/// let n = #{a: 42, d:0};
|
||||
///
|
||||
/// print(m + n); // prints "#{a:42, b:2, c:3, d:0}"
|
||||
///
|
||||
/// print(m); // prints "#{a:1, b:2, c:3}"
|
||||
/// ```
|
||||
#[rhai_fn(name = "+")]
|
||||
pub fn merge(map1: Map, map2: Map) -> Map {
|
||||
if map2.is_empty() {
|
||||
@ -54,6 +97,19 @@ mod map_functions {
|
||||
map1
|
||||
}
|
||||
}
|
||||
/// Add all property values of another object map into the object map.
|
||||
/// Only properties that do not originally exist in the object map are added.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m = #{a:1, b:2, c:3};
|
||||
/// let n = #{a: 42, d:0};
|
||||
///
|
||||
/// m.fill_with(n);
|
||||
///
|
||||
/// print(m); // prints "#{a:1, b:2, c:3, d:0}"
|
||||
/// ```
|
||||
pub fn fill_with(map: &mut Map, map2: Map) {
|
||||
if !map2.is_empty() {
|
||||
if map.is_empty() {
|
||||
@ -65,6 +121,22 @@ mod map_functions {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Return `true` if two object maps are equal (i.e. all property values are equal).
|
||||
///
|
||||
/// The operator `==` is used to compare property values and must be defined,
|
||||
/// otherwise `false` is assumed.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m1 = #{a:1, b:2, c:3};
|
||||
/// let m2 = #{a:1, b:2, c:3};
|
||||
/// let m3 = #{a:1, c:3};
|
||||
///
|
||||
/// print(m1 == m2); // prints true
|
||||
///
|
||||
/// print(m1 == m3); // prints false
|
||||
/// ```
|
||||
#[rhai_fn(name = "==", return_raw, pure)]
|
||||
pub fn equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
||||
if map1.len() != map2.len() {
|
||||
@ -92,11 +164,36 @@ mod map_functions {
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
/// Return `true` if two object maps are not equal (i.e. at least one property value is not equal).
|
||||
///
|
||||
/// The operator `==` is used to compare property values and must be defined,
|
||||
/// otherwise `false` is assumed.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m1 = #{a:1, b:2, c:3};
|
||||
/// let m2 = #{a:1, b:2, c:3};
|
||||
/// let m3 = #{a:1, c:3};
|
||||
///
|
||||
/// print(m1 != m2); // prints false
|
||||
///
|
||||
/// print(m1 != m3); // prints true
|
||||
/// ```
|
||||
#[rhai_fn(name = "!=", return_raw, pure)]
|
||||
pub fn not_equals(ctx: NativeCallContext, map1: &mut Map, map2: Map) -> RhaiResultOf<bool> {
|
||||
equals(ctx, map1, map2).map(|r| !r)
|
||||
}
|
||||
|
||||
/// Return an array with all the property names in the object map.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m = #{a:1, b:2, c:3};
|
||||
///
|
||||
/// print(m.keys()); // prints ["a", "b", "c"]
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[rhai_fn(pure)]
|
||||
pub fn keys(map: &mut Map) -> Array {
|
||||
@ -106,6 +203,15 @@ mod map_functions {
|
||||
map.keys().cloned().map(Into::into).collect()
|
||||
}
|
||||
}
|
||||
/// Return an array with all the property values in the object map.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m = #{a:1, b:2, c:3};
|
||||
///
|
||||
/// print(m.values()); // prints "[1, 2, 3]""
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[rhai_fn(pure)]
|
||||
pub fn values(map: &mut Map) -> Array {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, Position, RhaiResultOf, ERR, INT, UNSIGNED_INT};
|
||||
use crate::{def_package, Position, RhaiResultOf, ERR, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@ -108,6 +108,34 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
mod int_functions {
|
||||
/// Parse a string into an integer number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = parse_int("123");
|
||||
///
|
||||
/// print(x); // prints 123
|
||||
/// ```
|
||||
#[rhai_fn(name = "parse_int", return_raw)]
|
||||
pub fn parse_int(string: &str) -> RhaiResultOf<INT> {
|
||||
parse_int_radix(string, 10)
|
||||
}
|
||||
/// Parse a string into an integer number of the specified `radix`.
|
||||
///
|
||||
/// `radix` must be between 2 and 36.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = parse_int("123");
|
||||
///
|
||||
/// print(x); // prints 123
|
||||
///
|
||||
/// let y = parse_int("123abc", 16);
|
||||
///
|
||||
/// print(y); // prints 1194684 (0x123abc)
|
||||
/// ```
|
||||
#[rhai_fn(name = "parse_int", return_raw)]
|
||||
pub fn parse_int_radix(string: &str, radix: INT) -> RhaiResultOf<INT> {
|
||||
if !(2..=36).contains(&radix) {
|
||||
@ -118,19 +146,13 @@ mod int_functions {
|
||||
.into());
|
||||
}
|
||||
|
||||
UNSIGNED_INT::from_str_radix(string.trim(), radix as u32)
|
||||
.map(|v| v as INT)
|
||||
.map_err(|err| {
|
||||
ERR::ErrorArithmetic(
|
||||
format!("Error parsing integer number '{}': {}", string, err),
|
||||
Position::NONE,
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
#[rhai_fn(name = "parse_int", return_raw)]
|
||||
pub fn parse_int(string: &str) -> RhaiResultOf<INT> {
|
||||
parse_int_radix(string, 10)
|
||||
INT::from_str_radix(string.trim(), radix as u32).map_err(|err| {
|
||||
ERR::ErrorArithmetic(
|
||||
format!("Error parsing integer number '{}': {}", string, err),
|
||||
Position::NONE,
|
||||
)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,46 +161,60 @@ mod int_functions {
|
||||
mod trig_functions {
|
||||
use crate::FLOAT;
|
||||
|
||||
/// Return the sine of the floating-point number in radians.
|
||||
pub fn sin(x: FLOAT) -> FLOAT {
|
||||
x.sin()
|
||||
}
|
||||
/// Return the cosine of the floating-point number in radians.
|
||||
pub fn cos(x: FLOAT) -> FLOAT {
|
||||
x.cos()
|
||||
}
|
||||
/// Return the tangent of the floating-point number in radians.
|
||||
pub fn tan(x: FLOAT) -> FLOAT {
|
||||
x.tan()
|
||||
}
|
||||
/// Return the hyperbolic sine of the floating-point number in radians.
|
||||
pub fn sinh(x: FLOAT) -> FLOAT {
|
||||
x.sinh()
|
||||
}
|
||||
/// Return the hyperbolic cosine of the floating-point number in radians.
|
||||
pub fn cosh(x: FLOAT) -> FLOAT {
|
||||
x.cosh()
|
||||
}
|
||||
/// Return the hyperbolic tangent of the floating-point number in radians.
|
||||
pub fn tanh(x: FLOAT) -> FLOAT {
|
||||
x.tanh()
|
||||
}
|
||||
/// Return the arc-sine of the floating-point number, in radians.
|
||||
pub fn asin(x: FLOAT) -> FLOAT {
|
||||
x.asin()
|
||||
}
|
||||
/// Return the arc-cosine of the floating-point number, in radians.
|
||||
pub fn acos(x: FLOAT) -> FLOAT {
|
||||
x.acos()
|
||||
}
|
||||
/// Return the arc-tangent of the floating-point number, in radians.
|
||||
pub fn atan(x: FLOAT) -> FLOAT {
|
||||
x.atan()
|
||||
}
|
||||
/// Return the arc-tangent of the floating-point numbers `x` and `y`, in radians.
|
||||
#[rhai_fn(name = "atan")]
|
||||
pub fn atan2(x: FLOAT, y: FLOAT) -> FLOAT {
|
||||
x.atan2(y)
|
||||
}
|
||||
/// Return the arc-hyperbolic-sine of the floating-point number, in radians.
|
||||
pub fn asinh(x: FLOAT) -> FLOAT {
|
||||
x.asinh()
|
||||
}
|
||||
/// Return the arc-hyperbolic-cosine of the floating-point number, in radians.
|
||||
pub fn acosh(x: FLOAT) -> FLOAT {
|
||||
x.acosh()
|
||||
}
|
||||
/// Return the arc-hyperbolic-tangent of the floating-point number, in radians.
|
||||
pub fn atanh(x: FLOAT) -> FLOAT {
|
||||
x.atanh()
|
||||
}
|
||||
/// Return the hypotenuse of a triangle with sides `x` and `y`.
|
||||
pub fn hypot(x: FLOAT, y: FLOAT) -> FLOAT {
|
||||
x.hypot(y)
|
||||
}
|
||||
@ -189,6 +225,7 @@ mod trig_functions {
|
||||
mod float_functions {
|
||||
use crate::FLOAT;
|
||||
|
||||
/// Return the natural number _e_.
|
||||
#[rhai_fn(name = "E")]
|
||||
pub fn e() -> FLOAT {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
@ -196,6 +233,7 @@ mod float_functions {
|
||||
#[cfg(feature = "f32_float")]
|
||||
return std::f32::consts::E;
|
||||
}
|
||||
/// Return the number π.
|
||||
#[rhai_fn(name = "PI")]
|
||||
pub fn pi() -> FLOAT {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
@ -203,60 +241,77 @@ mod float_functions {
|
||||
#[cfg(feature = "f32_float")]
|
||||
return std::f32::consts::PI;
|
||||
}
|
||||
/// Convert degrees to radians.
|
||||
pub fn to_radians(x: FLOAT) -> FLOAT {
|
||||
x.to_radians()
|
||||
}
|
||||
/// Convert radians to degrees.
|
||||
pub fn to_degrees(x: FLOAT) -> FLOAT {
|
||||
x.to_degrees()
|
||||
}
|
||||
/// Return the square root of the floating-point number.
|
||||
pub fn sqrt(x: FLOAT) -> FLOAT {
|
||||
x.sqrt()
|
||||
}
|
||||
/// Return the exponential of the floating-point number.
|
||||
pub fn exp(x: FLOAT) -> FLOAT {
|
||||
x.exp()
|
||||
}
|
||||
/// Return the natural log of the floating-point number.
|
||||
pub fn ln(x: FLOAT) -> FLOAT {
|
||||
x.ln()
|
||||
}
|
||||
/// Return the log of the floating-point number with `base`.
|
||||
pub fn log(x: FLOAT, base: FLOAT) -> FLOAT {
|
||||
x.log(base)
|
||||
}
|
||||
/// Return the log of the floating-point number with base 10.
|
||||
#[rhai_fn(name = "log")]
|
||||
pub fn log10(x: FLOAT) -> FLOAT {
|
||||
x.log10()
|
||||
}
|
||||
/// Return the largest whole number less than or equals to the floating-point number.
|
||||
#[rhai_fn(name = "floor", get = "floor")]
|
||||
pub fn floor(x: FLOAT) -> FLOAT {
|
||||
x.floor()
|
||||
}
|
||||
/// Return the smallest whole number larger than or equals to the floating-point number.
|
||||
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||
pub fn ceiling(x: FLOAT) -> FLOAT {
|
||||
x.ceil()
|
||||
}
|
||||
/// Return the nearest whole number closest to the floating-point number.
|
||||
/// Rounds away from zero.
|
||||
#[rhai_fn(name = "round", get = "round")]
|
||||
pub fn round(x: FLOAT) -> FLOAT {
|
||||
x.round()
|
||||
}
|
||||
/// Return the integral part of the floating-point number.
|
||||
#[rhai_fn(name = "int", get = "int")]
|
||||
pub fn int(x: FLOAT) -> FLOAT {
|
||||
x.trunc()
|
||||
}
|
||||
/// Return the fractional part of the floating-point number.
|
||||
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||
pub fn fraction(x: FLOAT) -> FLOAT {
|
||||
x.fract()
|
||||
}
|
||||
/// Return `true` if the floating-point number is `NaN` (Not A Number).
|
||||
#[rhai_fn(name = "is_nan", get = "is_nan")]
|
||||
pub fn is_nan(x: FLOAT) -> bool {
|
||||
x.is_nan()
|
||||
}
|
||||
/// Return `true` if the floating-point number is finite.
|
||||
#[rhai_fn(name = "is_finite", get = "is_finite")]
|
||||
pub fn is_finite(x: FLOAT) -> bool {
|
||||
x.is_finite()
|
||||
}
|
||||
/// Return `true` if the floating-point number is infinite.
|
||||
#[rhai_fn(name = "is_infinite", get = "is_infinite")]
|
||||
pub fn is_infinite(x: FLOAT) -> bool {
|
||||
x.is_infinite()
|
||||
}
|
||||
/// Return the integral part of the floating-point number.
|
||||
#[rhai_fn(name = "to_int", return_raw)]
|
||||
pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> {
|
||||
if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f32) {
|
||||
@ -268,6 +323,7 @@ mod float_functions {
|
||||
Ok(x.trunc() as INT)
|
||||
}
|
||||
}
|
||||
/// Return the integral part of the floating-point number.
|
||||
#[rhai_fn(name = "to_int", return_raw)]
|
||||
pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> {
|
||||
if cfg!(not(feature = "unchecked")) && x > (INT::MAX as f64) {
|
||||
@ -279,6 +335,15 @@ mod float_functions {
|
||||
Ok(x.trunc() as INT)
|
||||
}
|
||||
}
|
||||
/// Parse a string into a floating-point number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = parse_int("123.456");
|
||||
///
|
||||
/// print(x); // prints 123.456
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn parse_float(string: &str) -> RhaiResultOf<FLOAT> {
|
||||
string.trim().parse::<FLOAT>().map_err(|err| {
|
||||
@ -289,6 +354,7 @@ mod float_functions {
|
||||
.into()
|
||||
})
|
||||
}
|
||||
/// Convert the 32-bit floating-point number to 64-bit.
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
#[rhai_fn(name = "to_float")]
|
||||
pub fn f32_to_f64(x: f32) -> f64 {
|
||||
@ -306,36 +372,52 @@ mod decimal_functions {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Return the natural number _e_.
|
||||
#[cfg(feature = "no_float")]
|
||||
#[rhai_fn(name = "PI")]
|
||||
pub fn pi() -> Decimal {
|
||||
Decimal::PI
|
||||
}
|
||||
/// Return the number π.
|
||||
#[cfg(feature = "no_float")]
|
||||
#[rhai_fn(name = "E")]
|
||||
pub fn e() -> Decimal {
|
||||
Decimal::E
|
||||
}
|
||||
/// Parse a string into a decimal number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = parse_float("123.456");
|
||||
///
|
||||
/// print(x); // prints 123.456
|
||||
/// ```
|
||||
#[cfg(feature = "no_float")]
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn parse_float(s: &str) -> RhaiResultOf<Decimal> {
|
||||
parse_decimal(s)
|
||||
}
|
||||
|
||||
/// Return the sine of the decimal number in radians.
|
||||
pub fn sin(x: Decimal) -> Decimal {
|
||||
x.sin()
|
||||
}
|
||||
/// Return the cosine of the decimal number in radians.
|
||||
pub fn cos(x: Decimal) -> Decimal {
|
||||
x.cos()
|
||||
}
|
||||
/// Return the tangent of the decimal number in radians.
|
||||
pub fn tan(x: Decimal) -> Decimal {
|
||||
x.tan()
|
||||
}
|
||||
/// Return the square root of the decimal number.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn sqrt(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||
x.sqrt()
|
||||
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
|
||||
}
|
||||
/// Return the exponential of the decimal number.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn exp(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
@ -345,6 +427,7 @@ mod decimal_functions {
|
||||
Ok(x.exp())
|
||||
}
|
||||
}
|
||||
/// Return the natural log of the decimal number.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn ln(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
@ -354,6 +437,7 @@ mod decimal_functions {
|
||||
Ok(x.ln())
|
||||
}
|
||||
}
|
||||
/// Return the log of the decimal number with base 10.
|
||||
#[rhai_fn(name = "log", return_raw)]
|
||||
pub fn log10(x: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
@ -363,106 +447,131 @@ mod decimal_functions {
|
||||
Ok(x.log10())
|
||||
}
|
||||
}
|
||||
/// Return the largest whole number less than or equals to the decimal number.
|
||||
#[rhai_fn(name = "floor", get = "floor")]
|
||||
pub fn floor(x: Decimal) -> Decimal {
|
||||
x.floor()
|
||||
}
|
||||
/// Return the smallest whole number larger than or equals to the decimal number.
|
||||
#[rhai_fn(name = "ceiling", get = "ceiling")]
|
||||
pub fn ceiling(x: Decimal) -> Decimal {
|
||||
x.ceil()
|
||||
}
|
||||
/// Return the nearest whole number closest to the decimal number.
|
||||
/// Always round mid-point towards the closest even number.
|
||||
#[rhai_fn(name = "round", get = "round")]
|
||||
pub fn round(x: Decimal) -> Decimal {
|
||||
x.round()
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
/// Always round mid-point towards the closest even number.
|
||||
#[rhai_fn(name = "round", return_raw)]
|
||||
pub fn round_dp(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
||||
pub fn round_dp(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if dp < 0 {
|
||||
if digits < 0 {
|
||||
return Err(make_err(format!(
|
||||
"Invalid number of digits for rounding: {}",
|
||||
dp
|
||||
digits
|
||||
)));
|
||||
}
|
||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||
return Ok(x);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(x.round_dp(dp as u32))
|
||||
Ok(x.round_dp(digits as u32))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
/// Always round away from zero.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn round_up(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
||||
pub fn round_up(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if dp < 0 {
|
||||
if digits < 0 {
|
||||
return Err(make_err(format!(
|
||||
"Invalid number of digits for rounding: {}",
|
||||
dp
|
||||
digits
|
||||
)));
|
||||
}
|
||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||
return Ok(x);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero))
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::AwayFromZero))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
/// Always round towards zero.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn round_down(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
||||
pub fn round_down(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if dp < 0 {
|
||||
if digits < 0 {
|
||||
return Err(make_err(format!(
|
||||
"Invalid number of digits for rounding: {}",
|
||||
dp
|
||||
digits
|
||||
)));
|
||||
}
|
||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||
return Ok(x);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero))
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::ToZero))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
/// Always round mid-points away from zero.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn round_half_up(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
||||
pub fn round_half_up(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if dp < 0 {
|
||||
if digits < 0 {
|
||||
return Err(make_err(format!(
|
||||
"Invalid number of digits for rounding: {}",
|
||||
dp
|
||||
digits
|
||||
)));
|
||||
}
|
||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||
return Ok(x);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero))
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointAwayFromZero))
|
||||
}
|
||||
/// Round the decimal number to the specified number of `digits` after the decimal point and return it.
|
||||
/// Always round mid-points towards zero.
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn round_half_down(x: Decimal, dp: INT) -> RhaiResultOf<Decimal> {
|
||||
pub fn round_half_down(x: Decimal, digits: INT) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
if dp < 0 {
|
||||
if digits < 0 {
|
||||
return Err(make_err(format!(
|
||||
"Invalid number of digits for rounding: {}",
|
||||
dp
|
||||
digits
|
||||
)));
|
||||
}
|
||||
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
|
||||
if cfg!(not(feature = "only_i32")) && digits > (u32::MAX as INT) {
|
||||
return Ok(x);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointTowardZero))
|
||||
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointTowardZero))
|
||||
}
|
||||
/// Return the integral part of the decimal number.
|
||||
#[rhai_fn(name = "int", get = "int")]
|
||||
pub fn int(x: Decimal) -> Decimal {
|
||||
x.trunc()
|
||||
}
|
||||
/// Return the fractional part of the decimal number.
|
||||
#[rhai_fn(name = "fraction", get = "fraction")]
|
||||
pub fn fraction(x: Decimal) -> Decimal {
|
||||
x.fract()
|
||||
}
|
||||
/// Parse a string into a decimal number.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let x = parse_decimal("123.456");
|
||||
///
|
||||
/// print(x); // prints 123.456
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn parse_decimal(string: &str) -> RhaiResultOf<Decimal> {
|
||||
Decimal::from_str(string)
|
||||
@ -476,6 +585,7 @@ mod decimal_functions {
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert the floating-point number to decimal.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "to_decimal", return_raw)]
|
||||
pub fn f32_to_decimal(x: f32) -> RhaiResultOf<Decimal> {
|
||||
@ -487,6 +597,7 @@ mod decimal_functions {
|
||||
.into()
|
||||
})
|
||||
}
|
||||
/// Convert the floating-point number to decimal.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "to_decimal", return_raw)]
|
||||
pub fn f64_to_decimal(x: f64) -> RhaiResultOf<Decimal> {
|
||||
@ -498,6 +609,7 @@ mod decimal_functions {
|
||||
.into()
|
||||
})
|
||||
}
|
||||
/// Convert the decimal number to floating-point.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn to_float(x: Decimal) -> RhaiResultOf<FLOAT> {
|
||||
|
@ -46,56 +46,68 @@ pub fn print_with_func(
|
||||
mod print_debug_functions {
|
||||
use crate::ImmutableString;
|
||||
|
||||
/// Convert the value of the `item` into a string.
|
||||
#[rhai_fn(name = "print", pure)]
|
||||
pub fn print_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||
print_with_func(FUNC_TO_STRING, &ctx, item)
|
||||
}
|
||||
/// Convert the value of the `item` into a string.
|
||||
#[rhai_fn(name = "to_string", pure)]
|
||||
pub fn to_string_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||
ctx.engine().map_type_name(&item.to_string()).into()
|
||||
}
|
||||
/// Convert the value of the `item` into a string in debug format.
|
||||
#[rhai_fn(name = "debug", pure)]
|
||||
pub fn debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||
print_with_func(FUNC_TO_DEBUG, &ctx, item)
|
||||
}
|
||||
/// Convert the value of the `item` into a string in debug format.
|
||||
#[rhai_fn(name = "to_debug", pure)]
|
||||
pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||
ctx.engine().map_type_name(&format!("{:?}", item)).into()
|
||||
}
|
||||
/// Return the empty string.
|
||||
#[rhai_fn(name = "print", name = "debug")]
|
||||
pub fn print_empty_string(ctx: NativeCallContext) -> ImmutableString {
|
||||
ctx.engine().const_empty_string()
|
||||
}
|
||||
/// Return the `string`.
|
||||
#[rhai_fn(name = "print", name = "to_string")]
|
||||
pub fn print_string(s: ImmutableString) -> ImmutableString {
|
||||
s
|
||||
pub fn print_string(string: ImmutableString) -> ImmutableString {
|
||||
string
|
||||
}
|
||||
/// Convert the function pointer into a string in debug format.
|
||||
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
||||
pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString {
|
||||
f.to_string().into()
|
||||
}
|
||||
|
||||
/// Convert the value of `number` into a string.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "print", name = "to_string")]
|
||||
pub fn print_f64(number: f64) -> ImmutableString {
|
||||
crate::ast::FloatWrapper::new(number).to_string().into()
|
||||
}
|
||||
/// Convert the value of `number` into a string.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "print", name = "to_string")]
|
||||
pub fn print_f32(number: f32) -> ImmutableString {
|
||||
crate::ast::FloatWrapper::new(number).to_string().into()
|
||||
}
|
||||
/// Convert the value of `number` into a string.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||
pub fn debug_f64(number: f64) -> ImmutableString {
|
||||
format!("{:?}", crate::ast::FloatWrapper::new(number)).into()
|
||||
}
|
||||
/// Convert the value of `number` into a string.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[rhai_fn(name = "debug", name = "to_debug")]
|
||||
pub fn debug_f32(number: f32) -> ImmutableString {
|
||||
format!("{:?}", crate::ast::FloatWrapper::new(number)).into()
|
||||
}
|
||||
|
||||
/// Convert the array into a string.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[rhai_fn(
|
||||
name = "print",
|
||||
@ -119,6 +131,8 @@ mod print_debug_functions {
|
||||
result.push(']');
|
||||
result.into()
|
||||
}
|
||||
|
||||
/// Convert the object map into a string.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[rhai_fn(
|
||||
name = "print",
|
||||
@ -148,27 +162,27 @@ mod print_debug_functions {
|
||||
|
||||
#[export_module]
|
||||
mod number_formatting {
|
||||
#[rhai_fn(skip)]
|
||||
pub fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
|
||||
fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
|
||||
format!("{:x}", value).into()
|
||||
}
|
||||
#[rhai_fn(skip)]
|
||||
pub fn to_octal<T: Octal>(value: T) -> ImmutableString {
|
||||
fn to_octal<T: Octal>(value: T) -> ImmutableString {
|
||||
format!("{:o}", value).into()
|
||||
}
|
||||
#[rhai_fn(skip)]
|
||||
pub fn to_binary<T: Binary>(value: T) -> ImmutableString {
|
||||
fn to_binary<T: Binary>(value: T) -> ImmutableString {
|
||||
format!("{:b}", value).into()
|
||||
}
|
||||
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn int_to_hex(value: INT) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn int_to_octal(value: INT) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn int_to_binary(value: INT) -> ImmutableString {
|
||||
to_binary(value)
|
||||
@ -177,98 +191,122 @@ mod number_formatting {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
pub mod numbers {
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn u8_to_hex(value: u8) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn u16_to_hex(value: u16) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn u32_to_hex(value: u32) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn u64_to_hex(value: u64) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn i8_to_hex(value: i8) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn i16_to_hex(value: i16) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn i32_to_hex(value: i32) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn i64_to_hex(value: i64) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn u8_to_octal(value: u8) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn u16_to_octal(value: u16) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn u32_to_octal(value: u32) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn u64_to_octal(value: u64) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn i8_to_octal(value: i8) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn i16_to_octal(value: i16) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn i32_to_octal(value: i32) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn i64_to_octal(value: i64) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn u8_to_binary(value: u8) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn u16_to_binary(value: u16) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn u32_to_binary(value: u32) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn u64_to_binary(value: u64) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn i8_to_binary(value: i8) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn i16_to_binary(value: i16) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn i32_to_binary(value: i32) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn i64_to_binary(value: i64) -> ImmutableString {
|
||||
to_binary(value)
|
||||
@ -277,14 +315,32 @@ mod number_formatting {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
|
||||
pub mod num_128 {
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn u128_to_hex(value: u128) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in hex format.
|
||||
#[rhai_fn(name = "to_hex")]
|
||||
pub fn i128_to_hex(value: i128) -> ImmutableString {
|
||||
to_hex(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn u128_to_octal(value: u128) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in octal format.
|
||||
#[rhai_fn(name = "to_octal")]
|
||||
pub fn i128_to_octal(value: i128) -> ImmutableString {
|
||||
to_octal(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn u128_to_binary(value: u128) -> ImmutableString {
|
||||
to_binary(value)
|
||||
}
|
||||
/// Convert the `value` into a string in binary format.
|
||||
#[rhai_fn(name = "to_binary")]
|
||||
pub fn i128_to_binary(value: i128) -> ImmutableString {
|
||||
to_binary(value)
|
||||
|
@ -73,6 +73,15 @@ mod string_functions {
|
||||
string
|
||||
}
|
||||
|
||||
/// Return the length of the string, in number of characters.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "朝には紅顔ありて夕べには白骨となる";
|
||||
///
|
||||
/// print(text.len); // prints 17
|
||||
/// ```
|
||||
#[rhai_fn(name = "len", get = "len")]
|
||||
pub fn len(string: &str) -> INT {
|
||||
if string.is_empty() {
|
||||
@ -81,6 +90,15 @@ mod string_functions {
|
||||
string.chars().count() as INT
|
||||
}
|
||||
}
|
||||
/// Return the length of the string, in number of bytes used to store it in UTF-8 encoding.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "朝には紅顔ありて夕べには白骨となる";
|
||||
///
|
||||
/// print(text.bytes); // prints 51
|
||||
/// ```
|
||||
#[rhai_fn(name = "bytes", get = "bytes")]
|
||||
pub fn bytes(string: &str) -> INT {
|
||||
if string.is_empty() {
|
||||
@ -89,18 +107,59 @@ mod string_functions {
|
||||
string.len() as INT
|
||||
}
|
||||
}
|
||||
/// Remove all occurrences of a sub-string from the string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// text.remove("hello");
|
||||
///
|
||||
/// print(text); // prints ", world! , foobar!"
|
||||
/// ```
|
||||
pub fn remove(string: &mut ImmutableString, sub_string: ImmutableString) {
|
||||
*string -= sub_string;
|
||||
}
|
||||
/// Remove all occurrences of a character from the string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// text.remove("o");
|
||||
///
|
||||
/// print(text); // prints "hell, wrld! hell, fbar!"
|
||||
/// ```
|
||||
#[rhai_fn(name = "remove")]
|
||||
pub fn remove_char(string: &mut ImmutableString, character: char) {
|
||||
*string -= character;
|
||||
}
|
||||
/// Clear the string, making it empty.
|
||||
pub fn clear(string: &mut ImmutableString) {
|
||||
if !string.is_empty() {
|
||||
string.make_mut().clear();
|
||||
}
|
||||
}
|
||||
/// Cut off the string at the specified number of characters.
|
||||
///
|
||||
/// * If `len` ≤ 0, the string is cleared.
|
||||
/// * If `len` ≥ length of string, the string is not truncated.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// text.truncate(13);
|
||||
///
|
||||
/// print(text); // prints "hello, world!"
|
||||
///
|
||||
/// x.truncate(10);
|
||||
///
|
||||
/// print(text); // prints "hello, world!"
|
||||
/// ```
|
||||
pub fn truncate(string: &mut ImmutableString, len: INT) {
|
||||
if len > 0 {
|
||||
let chars: StaticVec<_> = string.chars().collect();
|
||||
@ -111,6 +170,15 @@ mod string_functions {
|
||||
string.make_mut().clear();
|
||||
}
|
||||
}
|
||||
/// Remove whitespace characters from both ends of the string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = " hello ";
|
||||
///
|
||||
/// print(text.trim()); // prints "hello"
|
||||
/// ```
|
||||
pub fn trim(string: &mut ImmutableString) {
|
||||
let trimmed = string.trim();
|
||||
|
||||
@ -118,6 +186,19 @@ mod string_functions {
|
||||
*string = trimmed.to_string().into();
|
||||
}
|
||||
}
|
||||
/// Remove the last character from the string and return it.
|
||||
///
|
||||
/// If the string is empty, `()` is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.pop()); // prints '!'
|
||||
///
|
||||
/// print(text); // prints "hello, world"
|
||||
/// ```
|
||||
pub fn pop(string: &mut ImmutableString) -> Dynamic {
|
||||
if string.is_empty() {
|
||||
Dynamic::UNIT
|
||||
@ -128,6 +209,21 @@ mod string_functions {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Remove the a specified number of characters from the end of the string and return it as a
|
||||
/// new string.
|
||||
///
|
||||
/// * If `len` ≤ 0, the string is not modified and an empty string is returned.
|
||||
/// * If `len` ≥ length of string, the string is cleared and the entire string returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.pop(4)); // prints "rld!"
|
||||
///
|
||||
/// print(text); // prints "hello, wo"
|
||||
/// ```
|
||||
#[rhai_fn(name = "pop")]
|
||||
pub fn pop_string(
|
||||
ctx: NativeCallContext,
|
||||
@ -150,6 +246,17 @@ mod string_functions {
|
||||
chars.into_iter().rev().collect::<SmartString>().into()
|
||||
}
|
||||
|
||||
/// Convert the string to all upper-case and return it as a new string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!"
|
||||
///
|
||||
/// print(text.to_upper()); // prints "HELLO, WORLD!"
|
||||
///
|
||||
/// print(text); // prints "hello, world!"
|
||||
/// ```
|
||||
pub fn to_upper(string: ImmutableString) -> ImmutableString {
|
||||
if string.is_empty() {
|
||||
string
|
||||
@ -157,11 +264,33 @@ mod string_functions {
|
||||
string.to_uppercase().into()
|
||||
}
|
||||
}
|
||||
/// Convert the string to all upper-case.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!"
|
||||
///
|
||||
/// text.make_upper();
|
||||
///
|
||||
/// print(text); // prints "HELLO, WORLD!";
|
||||
/// ```
|
||||
pub fn make_upper(string: &mut ImmutableString) {
|
||||
if !string.is_empty() {
|
||||
*string = string.to_uppercase().into();
|
||||
}
|
||||
}
|
||||
/// Convert the string to all lower-case and return it as a new string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "HELLO, WORLD!"
|
||||
///
|
||||
/// print(text.to_lower()); // prints "hello, world!"
|
||||
///
|
||||
/// print(text); // prints "HELLO, WORLD!"
|
||||
/// ```
|
||||
pub fn to_lower(string: ImmutableString) -> ImmutableString {
|
||||
if string.is_empty() {
|
||||
string
|
||||
@ -169,12 +298,34 @@ mod string_functions {
|
||||
string.to_lowercase().into()
|
||||
}
|
||||
}
|
||||
/// Convert the string to all lower-case.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "HELLO, WORLD!"
|
||||
///
|
||||
/// text.make_lower();
|
||||
///
|
||||
/// print(text); // prints "hello, world!";
|
||||
/// ```
|
||||
pub fn make_lower(string: &mut ImmutableString) {
|
||||
if !string.is_empty() {
|
||||
*string = string.to_lowercase().into();
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the character to upper-case and return it as a new character.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let ch = 'a';
|
||||
///
|
||||
/// print(ch.to_upper()); // prints 'A'
|
||||
///
|
||||
/// print(ch); // prints 'a'
|
||||
/// ```
|
||||
#[rhai_fn(name = "to_upper")]
|
||||
pub fn to_upper_char(character: char) -> char {
|
||||
let mut stream = character.to_uppercase();
|
||||
@ -185,10 +336,32 @@ mod string_functions {
|
||||
ch
|
||||
}
|
||||
}
|
||||
/// Convert the character to upper-case.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let ch = 'a';
|
||||
///
|
||||
/// ch.make_upper();
|
||||
///
|
||||
/// print(ch); // prints 'A'
|
||||
/// ```
|
||||
#[rhai_fn(name = "make_upper")]
|
||||
pub fn make_upper_char(character: &mut char) {
|
||||
*character = to_upper_char(*character)
|
||||
}
|
||||
/// Convert the character to lower-case and return it as a new character.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let ch = 'A';
|
||||
///
|
||||
/// print(ch.to_lower()); // prints 'a'
|
||||
///
|
||||
/// print(ch); // prints 'A'
|
||||
/// ```
|
||||
#[rhai_fn(name = "to_lower")]
|
||||
pub fn to_lower_char(character: char) -> char {
|
||||
let mut stream = character.to_lowercase();
|
||||
@ -199,11 +372,41 @@ mod string_functions {
|
||||
ch
|
||||
}
|
||||
}
|
||||
/// Convert the character to lower-case.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let ch = 'A';
|
||||
///
|
||||
/// ch.make_lower();
|
||||
///
|
||||
/// print(ch); // prints 'a'
|
||||
/// ```
|
||||
#[rhai_fn(name = "make_lower")]
|
||||
pub fn make_lower_char(character: &mut char) {
|
||||
*character = to_lower_char(*character)
|
||||
}
|
||||
|
||||
/// Find the specified `character` in the string, starting from the specified `start` position,
|
||||
/// and return the first index where it is found.
|
||||
/// If the `character` is not found, `-1` is returned.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||
/// * If `start` ≥ length of string, `-1` is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.index_of('l', 5)); // prints 10 (first index after 5)
|
||||
///
|
||||
/// print(text.index_of('o', -7)); // prints 8
|
||||
///
|
||||
/// print(text.index_of('x', 0)); // prints -1
|
||||
/// ```
|
||||
#[rhai_fn(name = "index_of")]
|
||||
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
||||
if string.is_empty() {
|
||||
@ -243,6 +446,18 @@ mod string_functions {
|
||||
.map(|index| string[0..start + index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
}
|
||||
/// Find the specified `character` in the string and return the first index where it is found.
|
||||
/// If the `character` is not found, `-1` is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.index_of('l')); // prints 2 (first index)
|
||||
///
|
||||
/// print(text.index_of('x')); // prints -1
|
||||
/// ```
|
||||
#[rhai_fn(name = "index_of")]
|
||||
pub fn index_of_char(string: &str, character: char) -> INT {
|
||||
if string.is_empty() {
|
||||
@ -254,6 +469,25 @@ mod string_functions {
|
||||
.unwrap_or(-1 as INT)
|
||||
}
|
||||
}
|
||||
/// Find the specified sub-string in the string, starting from the specified `start` position,
|
||||
/// and return the first index where it is found.
|
||||
/// If the sub-string is not found, `-1` is returned.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||
/// * If `start` ≥ length of string, `-1` is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// print(text.index_of("ll", 5)); // prints 16 (first index after 5)
|
||||
///
|
||||
/// print(text.index_of("ll", -15)); // prints 16
|
||||
///
|
||||
/// print(text.index_of("xx", 0)); // prints -1
|
||||
/// ```
|
||||
#[rhai_fn(name = "index_of")]
|
||||
pub fn index_of_string_starting_from(string: &str, find_string: &str, start: INT) -> INT {
|
||||
if string.is_empty() {
|
||||
@ -293,6 +527,18 @@ mod string_functions {
|
||||
.map(|index| string[0..start + index].chars().count() as INT)
|
||||
.unwrap_or(-1 as INT)
|
||||
}
|
||||
/// Find the specified `character` in the string and return the first index where it is found.
|
||||
/// If the `character` is not found, `-1` is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// print(text.index_of("ll")); // prints 2 (first index)
|
||||
///
|
||||
/// print(text.index_of("xx:)); // prints -1
|
||||
/// ```
|
||||
#[rhai_fn(name = "index_of")]
|
||||
pub fn index_of(string: &str, find_string: &str) -> INT {
|
||||
if string.is_empty() {
|
||||
@ -305,6 +551,15 @@ mod string_functions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy an exclusive range of characters from the string and return it as a new string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.sub_string(3..7)); // prints "lo, "
|
||||
/// ```
|
||||
#[rhai_fn(name = "sub_string")]
|
||||
pub fn sub_string_range(
|
||||
ctx: NativeCallContext,
|
||||
@ -315,6 +570,15 @@ mod string_functions {
|
||||
let end = INT::max(range.end, start);
|
||||
sub_string(ctx, string, start, end - start)
|
||||
}
|
||||
/// Copy an inclusive range of characters from the string and return it as a new string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.sub_string(3..=7)); // prints "lo, w"
|
||||
/// ```
|
||||
#[rhai_fn(name = "sub_string")]
|
||||
pub fn sub_string_inclusive_range(
|
||||
ctx: NativeCallContext,
|
||||
@ -325,6 +589,23 @@ mod string_functions {
|
||||
let end = INT::max(*range.end(), start);
|
||||
sub_string(ctx, string, start, end - start + 1)
|
||||
}
|
||||
/// Copy a portion of the string and return it as a new string.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||
/// * If `start` ≥ length of string, an empty string is returned.
|
||||
/// * If `len` ≤ 0, an empty string is returned.
|
||||
/// * If `start` position + `len` ≥ length of string, entire portion of the string after the `start` position is copied and returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.sub_string(3, 4)); // prints "lo, "
|
||||
///
|
||||
/// print(text.sub_string(-8, 3)); // prints ", w"
|
||||
/// ```
|
||||
pub fn sub_string(
|
||||
ctx: NativeCallContext,
|
||||
string: &str,
|
||||
@ -374,6 +655,22 @@ mod string_functions {
|
||||
.collect::<String>()
|
||||
.into()
|
||||
}
|
||||
/// Copy a portion of the string beginning at the `start` position till the end and return it as
|
||||
/// a new string.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||
/// * If `start` < -length of string, the entire string is copied and returned.
|
||||
/// * If `start` ≥ length of string, an empty string is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.sub_string(5)); // prints ", world!"
|
||||
///
|
||||
/// print(text.sub_string(-5)); // prints "orld!"
|
||||
/// ```
|
||||
#[rhai_fn(name = "sub_string")]
|
||||
pub fn sub_string_starting_from(
|
||||
ctx: NativeCallContext,
|
||||
@ -388,18 +685,62 @@ mod string_functions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove all characters from the string except those within an exclusive range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// text.crop(2..8);
|
||||
///
|
||||
/// print(text); // prints "llo, w"
|
||||
/// ```
|
||||
#[rhai_fn(name = "crop")]
|
||||
pub fn crop_range(string: &mut ImmutableString, range: ExclusiveRange) {
|
||||
let start = INT::max(range.start, 0);
|
||||
let end = INT::max(range.end, start);
|
||||
crop(string, start, end - start)
|
||||
}
|
||||
/// Remove all characters from the string except those within an inclusive range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// text.crop(2..=8);
|
||||
///
|
||||
/// print(text); // prints "llo, wo"
|
||||
/// ```
|
||||
#[rhai_fn(name = "crop")]
|
||||
pub fn crop_inclusive_range(string: &mut ImmutableString, range: InclusiveRange) {
|
||||
let start = INT::max(*range.start(), 0);
|
||||
let end = INT::max(*range.end(), start);
|
||||
crop(string, start, end - start + 1)
|
||||
}
|
||||
|
||||
/// Remove all characters from the string except those within a range.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||
/// * If `start` < -length of string, position counts from the beginning of the string.
|
||||
/// * If `start` ≥ length of string, the entire string is cleared.
|
||||
/// * If `len` ≤ 0, the entire string is cleared.
|
||||
/// * If `start` position + `len` ≥ length of string, only the portion of the string after the `start` position is retained.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// text.crop(2, 8);
|
||||
///
|
||||
/// print(text); // prints "llo, wor"
|
||||
///
|
||||
/// text.crop(-5, 3);
|
||||
///
|
||||
/// print(text); // prints ", w"
|
||||
/// ```
|
||||
#[rhai_fn(name = "crop")]
|
||||
pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
|
||||
if string.is_empty() {
|
||||
@ -443,17 +784,58 @@ mod string_functions {
|
||||
copy.clear();
|
||||
copy.extend(chars.iter().skip(offset).take(len));
|
||||
}
|
||||
/// Remove all characters from the string except until the `start` position.
|
||||
///
|
||||
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
|
||||
/// * If `start` < -length of string, the string is not modified.
|
||||
/// * If `start` ≥ length of string, the entire string is cleared.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// text.crop(5);
|
||||
///
|
||||
/// print(text); // prints ", world!"
|
||||
///
|
||||
/// text.crop(-3);
|
||||
///
|
||||
/// print(text); // prints "ld!"
|
||||
/// ```
|
||||
#[rhai_fn(name = "crop")]
|
||||
pub fn crop_string_starting_from(string: &mut ImmutableString, start: INT) {
|
||||
crop(string, start, string.len() as INT);
|
||||
}
|
||||
|
||||
/// Replace all occurrences of the specified sub-string in the string with another string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// text.replace("hello", "hey");
|
||||
///
|
||||
/// print(text); // prints "hey, world! hey, foobar!"
|
||||
/// ```
|
||||
#[rhai_fn(name = "replace")]
|
||||
pub fn replace(string: &mut ImmutableString, find_string: &str, substitute_string: &str) {
|
||||
if !string.is_empty() {
|
||||
*string = string.replace(find_string, substitute_string).into();
|
||||
}
|
||||
}
|
||||
/// Replace all occurrences of the specified sub-string in the string with the specified character.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// text.replace("hello", '*');
|
||||
///
|
||||
/// print(text); // prints "*, world! *, foobar!"
|
||||
/// ```
|
||||
#[rhai_fn(name = "replace")]
|
||||
pub fn replace_string_with_char(
|
||||
string: &mut ImmutableString,
|
||||
@ -466,6 +848,17 @@ mod string_functions {
|
||||
.into();
|
||||
}
|
||||
}
|
||||
/// Replace all occurrences of the specified character in the string with another string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// text.replace('l', "(^)");
|
||||
///
|
||||
/// print(text); // prints "he(^)(^)o, wor(^)d! he(^)(^)o, foobar!"
|
||||
/// ```
|
||||
#[rhai_fn(name = "replace")]
|
||||
pub fn replace_char_with_string(
|
||||
string: &mut ImmutableString,
|
||||
@ -478,6 +871,17 @@ mod string_functions {
|
||||
.into();
|
||||
}
|
||||
}
|
||||
/// Replace all occurrences of the specified character in the string with another character.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foobar!";
|
||||
///
|
||||
/// text.replace("l", '*');
|
||||
///
|
||||
/// print(text); // prints "he**o, wor*d! he**o, foobar!"
|
||||
/// ```
|
||||
#[rhai_fn(name = "replace")]
|
||||
pub fn replace_char(
|
||||
string: &mut ImmutableString,
|
||||
@ -494,6 +898,23 @@ mod string_functions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pad the string to at least the specified number of characters with the specified `character`.
|
||||
///
|
||||
/// If `len` ≤ length of string, no padding is done.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello";
|
||||
///
|
||||
/// text.pad(8, '!');
|
||||
///
|
||||
/// print(text); // prints "hello!!!"
|
||||
///
|
||||
/// text.pad(5, '*');
|
||||
///
|
||||
/// print(text); // prints "hello!!!"
|
||||
/// ```
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn pad(
|
||||
ctx: NativeCallContext,
|
||||
@ -538,6 +959,23 @@ mod string_functions {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Pad the string to at least the specified number of characters with the specified string.
|
||||
///
|
||||
/// If `len` ≤ length of string, no padding is done.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello";
|
||||
///
|
||||
/// text.pad(10, "(!)");
|
||||
///
|
||||
/// print(text); // prints "hello(!)(!)"
|
||||
///
|
||||
/// text.pad(8, '***');
|
||||
///
|
||||
/// print(text); // prints "hello(!)(!)"
|
||||
/// ```
|
||||
#[rhai_fn(name = "pad", return_raw)]
|
||||
pub fn pad_with_string(
|
||||
ctx: NativeCallContext,
|
||||
@ -594,6 +1032,14 @@ mod string_functions {
|
||||
pub mod arrays {
|
||||
use crate::{Array, ImmutableString};
|
||||
|
||||
/// Return an array containing all the characters of the string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello";
|
||||
///
|
||||
/// print(text.split()); // prints "['h', 'e', 'l', 'l', 'o']"
|
||||
#[rhai_fn(name = "split")]
|
||||
pub fn chars(string: &str) -> Array {
|
||||
if string.is_empty() {
|
||||
@ -602,10 +1048,32 @@ mod string_functions {
|
||||
string.chars().map(Into::into).collect()
|
||||
}
|
||||
}
|
||||
/// Split the string into two at the specified `index` position and return it both strings
|
||||
/// as an array.
|
||||
///
|
||||
/// The character at the `index` position (if any) is returned in the _second_ string.
|
||||
///
|
||||
/// * If `index` < 0, position counts from the end of the string (`-1` is the last character).
|
||||
/// * If `index` < -length of string, it is equivalent to cutting at position 0.
|
||||
/// * If `index` ≥ length of string, it is equivalent to cutting at the end of the string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.split(6)); // prints ["hello,", " world!"]
|
||||
///
|
||||
/// print(text.split(13)); // prints ["hello, world!", ""]
|
||||
///
|
||||
/// print(text.split(-6)); // prints ["hello, ", "world!"]
|
||||
///
|
||||
/// print(text.split(-99)); // prints ["", "hello, world!"]
|
||||
/// ```
|
||||
#[rhai_fn(name = "split")]
|
||||
pub fn split_at(ctx: NativeCallContext, string: ImmutableString, start: INT) -> Array {
|
||||
if start <= 0 {
|
||||
if let Some(n) = start.checked_abs() {
|
||||
pub fn split_at(ctx: NativeCallContext, string: ImmutableString, index: INT) -> Array {
|
||||
if index <= 0 {
|
||||
if let Some(n) = index.checked_abs() {
|
||||
let num_chars = string.chars().count();
|
||||
if n as usize > num_chars {
|
||||
vec![ctx.engine().const_empty_string().into(), string.into()]
|
||||
@ -618,41 +1086,127 @@ mod string_functions {
|
||||
vec![ctx.engine().const_empty_string().into(), string.into()]
|
||||
}
|
||||
} else {
|
||||
let prefix: String = string.chars().take(start as usize).collect();
|
||||
let prefix: String = string.chars().take(index as usize).collect();
|
||||
let prefix_len = prefix.len();
|
||||
vec![prefix.into(), string[prefix_len..].into()]
|
||||
}
|
||||
}
|
||||
/// Split the string into segments based on a `delimiter` string, returning an array of the segments.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split("ll")); // prints ["he", "o, world! he", "o, foo!"]
|
||||
/// ```
|
||||
pub fn split(string: &str, delimiter: &str) -> Array {
|
||||
string.split(delimiter).map(Into::into).collect()
|
||||
}
|
||||
/// Split the string into at most the specified number of `segments` based on a `delimiter` string,
|
||||
/// returning an array of the segments.
|
||||
///
|
||||
/// If `segments` < 1, only one segment is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split("ll", 2)); // prints ["he", "o, world! hello, foo!"]
|
||||
/// ```
|
||||
#[rhai_fn(name = "split")]
|
||||
pub fn splitn(string: &str, delimiter: &str, segments: INT) -> Array {
|
||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||
string.splitn(pieces, delimiter).map(Into::into).collect()
|
||||
}
|
||||
/// Split the string into segments based on a `delimiter` character, returning an array of the segments.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split('l')); // prints ["he", "", "o, wor", "d! he", "", "o, foo!"]
|
||||
/// ```
|
||||
#[rhai_fn(name = "split")]
|
||||
pub fn split_char(string: &str, delimiter: char) -> Array {
|
||||
string.split(delimiter).map(Into::into).collect()
|
||||
}
|
||||
/// Split the string into at most the specified number of `segments` based on a `delimiter` character,
|
||||
/// returning an array of the segments.
|
||||
///
|
||||
/// If `segments` < 1, only one segment is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split('l', 3)); // prints ["he", "", "o, world! hello, foo!"]
|
||||
/// ```
|
||||
#[rhai_fn(name = "split")]
|
||||
pub fn splitn_char(string: &str, delimiter: char, segments: INT) -> Array {
|
||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||
string.splitn(pieces, delimiter).map(Into::into).collect()
|
||||
}
|
||||
/// Split the string into segments based on a `delimiter` string, returning an array of the
|
||||
/// segments in _reverse_ order.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split_rev("ll")); // prints ["o, foo!", "o, world! he", "he"]
|
||||
/// ```
|
||||
#[rhai_fn(name = "split_rev")]
|
||||
pub fn rsplit(string: &str, delimiter: &str) -> Array {
|
||||
string.rsplit(delimiter).map(Into::into).collect()
|
||||
}
|
||||
/// Split the string into at most a specified number of `segments` based on a `delimiter` string,
|
||||
/// returning an array of the segments in _reverse_ order.
|
||||
///
|
||||
/// If `segments` < 1, only one segment is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split_rev("ll", 2)); // prints ["o, foo!", "hello, world! he"]
|
||||
/// ```
|
||||
#[rhai_fn(name = "split_rev")]
|
||||
pub fn rsplitn(string: &str, delimiter: &str, segments: INT) -> Array {
|
||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||
string.rsplitn(pieces, delimiter).map(Into::into).collect()
|
||||
}
|
||||
/// Split the string into segments based on a `delimiter` character, returning an array of
|
||||
/// the segments in _reverse_ order.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split_rev('l')); // prints ["o, foo!", "", "d! he", "o, wor", "", "he"]
|
||||
/// ```
|
||||
#[rhai_fn(name = "split_rev")]
|
||||
pub fn rsplit_char(string: &str, delimiter: char) -> Array {
|
||||
string.rsplit(delimiter).map(Into::into).collect()
|
||||
}
|
||||
/// Split the string into at most the specified number of `segments` based on a `delimiter` character,
|
||||
/// returning an array of the segments.
|
||||
///
|
||||
/// If `segments` < 1, only one segment is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world! hello, foo!";
|
||||
///
|
||||
/// print(text.split('l', 3)); // prints ["o, foo!", "", "hello, world! he"
|
||||
/// ```
|
||||
#[rhai_fn(name = "split_rev")]
|
||||
pub fn rsplitn_char(string: &str, delimiter: char, segments: INT) -> Array {
|
||||
let pieces: usize = if segments < 1 { 1 } else { segments as usize };
|
||||
|
@ -25,10 +25,12 @@ def_package! {
|
||||
|
||||
#[export_module]
|
||||
mod time_functions {
|
||||
/// Create a timestamp containing the current system time.
|
||||
pub fn timestamp() -> Instant {
|
||||
Instant::now()
|
||||
}
|
||||
|
||||
/// Return the number of seconds between the current system time and the timestamp.
|
||||
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
|
||||
pub fn elapsed(timestamp: Instant) -> RhaiResult {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -55,6 +57,7 @@ mod time_functions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of seconds between two timestamps.
|
||||
#[rhai_fn(return_raw, name = "-")]
|
||||
pub fn time_diff(timestamp1: Instant, timestamp2: Instant) -> RhaiResult {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -140,19 +143,23 @@ mod time_functions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the specified number of `seconds` to the timestamp and return it as a new timestamp.
|
||||
#[rhai_fn(return_raw, name = "+")]
|
||||
pub fn add(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
|
||||
add_impl(timestamp, seconds)
|
||||
}
|
||||
/// Add the specified number of `seconds` to the timestamp.
|
||||
#[rhai_fn(return_raw, name = "+=")]
|
||||
pub fn add_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
|
||||
*timestamp = add_impl(*timestamp, seconds)?;
|
||||
Ok(())
|
||||
}
|
||||
/// Subtract the specified number of `seconds` from the timestamp and return it as a new timestamp.
|
||||
#[rhai_fn(return_raw, name = "-")]
|
||||
pub fn subtract(timestamp: Instant, seconds: FLOAT) -> RhaiResultOf<Instant> {
|
||||
subtract_impl(timestamp, seconds)
|
||||
}
|
||||
/// Subtract the specified number of `seconds` from the timestamp.
|
||||
#[rhai_fn(return_raw, name = "-=")]
|
||||
pub fn subtract_assign(timestamp: &mut Instant, seconds: FLOAT) -> RhaiResultOf<()> {
|
||||
*timestamp = subtract_impl(*timestamp, seconds)?;
|
||||
@ -193,45 +200,55 @@ mod time_functions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the specified number of `seconds` to the timestamp and return it as a new timestamp.
|
||||
#[rhai_fn(return_raw, name = "+")]
|
||||
pub fn add(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
|
||||
add_impl(timestamp, seconds)
|
||||
}
|
||||
/// Add the specified number of `seconds` to the timestamp.
|
||||
#[rhai_fn(return_raw, name = "+=")]
|
||||
pub fn add_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
|
||||
*timestamp = add_impl(*timestamp, seconds)?;
|
||||
Ok(())
|
||||
}
|
||||
/// Subtract the specified number of `seconds` from the timestamp and return it as a new timestamp.
|
||||
#[rhai_fn(return_raw, name = "-")]
|
||||
pub fn subtract(timestamp: Instant, seconds: INT) -> RhaiResultOf<Instant> {
|
||||
subtract_impl(timestamp, seconds)
|
||||
}
|
||||
/// Subtract the specified number of `seconds` from the timestamp.
|
||||
#[rhai_fn(return_raw, name = "-=")]
|
||||
pub fn subtract_assign(timestamp: &mut Instant, seconds: INT) -> RhaiResultOf<()> {
|
||||
*timestamp = subtract_impl(*timestamp, seconds)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return `true` if two timestamps are equal.
|
||||
#[rhai_fn(name = "==")]
|
||||
pub fn eq(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||
timestamp1 == timestamp2
|
||||
}
|
||||
/// Return `true` if two timestamps are not equal.
|
||||
#[rhai_fn(name = "!=")]
|
||||
pub fn ne(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||
timestamp1 != timestamp2
|
||||
}
|
||||
/// Return `true` if the first timestamp is earlier than the second.
|
||||
#[rhai_fn(name = "<")]
|
||||
pub fn lt(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||
timestamp1 < timestamp2
|
||||
}
|
||||
/// Return `true` if the first timestamp is earlier than or equals to the second.
|
||||
#[rhai_fn(name = "<=")]
|
||||
pub fn lte(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||
timestamp1 <= timestamp2
|
||||
}
|
||||
/// Return `true` if the first timestamp is later than the second.
|
||||
#[rhai_fn(name = ">")]
|
||||
pub fn gt(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||
timestamp1 > timestamp2
|
||||
}
|
||||
/// Return `true` if the first timestamp is later than or equals to the second.
|
||||
#[rhai_fn(name = ">=")]
|
||||
pub fn gte(timestamp1: Instant, timestamp2: Instant) -> bool {
|
||||
timestamp1 >= timestamp2
|
||||
|
Loading…
Reference in New Issue
Block a user