BLOB's and strings.
This commit is contained in:
parent
83e1e88531
commit
664e3d31e5
@ -27,6 +27,8 @@ Script-breaking changes
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
* For consistency with the `import` statement, the `export` statement no longer exports multiple variables.
|
* For consistency with the `import` statement, the `export` statement no longer exports multiple variables.
|
||||||
|
* Appending a BLOB to a string (via `+`, `+=`, `append` or string interpolation) now treats the BLOB as a UTF-8 encoded string.
|
||||||
|
* Appending a string/character to a BLOB (via `+=` or `append`) now adds the string/character as a UTF-8 encoded byte stream.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -52,6 +54,8 @@ Enhancements
|
|||||||
* `StmtBlock` and `Stmt::Block` now keep the position of the closing `}` as well.
|
* `StmtBlock` and `Stmt::Block` now keep the position of the closing `}` as well.
|
||||||
* `EvalAltResult::unwrap_inner` is added to access the base error inside multiple layers of wrappings (e.g. `EvalAltResult::ErrorInFunction`).
|
* `EvalAltResult::unwrap_inner` is added to access the base error inside multiple layers of wrappings (e.g. `EvalAltResult::ErrorInFunction`).
|
||||||
* Yet another new syntax is introduced for `def_package!` that further simplifies the old syntax.
|
* Yet another new syntax is introduced for `def_package!` that further simplifies the old syntax.
|
||||||
|
* A new method `to_blob` is added to convert a string into a BLOB as UTF-8 encoded bytes.
|
||||||
|
* A new method `to_array` is added to convert a BLOB into array of integers.
|
||||||
|
|
||||||
REPL tool changes
|
REPL tool changes
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -682,19 +682,77 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// blob op= int
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<INT>()) {
|
{
|
||||||
use crate::Blob;
|
// string op= blob
|
||||||
|
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<crate::Blob>()) {
|
||||||
|
return match op {
|
||||||
|
"+=" => Some(|_, args| {
|
||||||
|
let buf = {
|
||||||
|
let x = args[1].read_lock::<crate::Blob>().expect(BUILTIN);
|
||||||
|
if x.is_empty() {
|
||||||
|
return Ok(Dynamic::UNIT);
|
||||||
|
}
|
||||||
|
let s = args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
|
let mut buf = crate::SmartString::from(s.as_str());
|
||||||
|
buf.push_str(&String::from_utf8_lossy(&x));
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
let mut s = args[0].write_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
|
*s = buf.into();
|
||||||
|
Ok(Dynamic::UNIT)
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// blob op= int
|
||||||
|
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<INT>()) {
|
||||||
|
use crate::Blob;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8;
|
let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8;
|
||||||
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
|
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
Ok(blob.push(x).into())
|
Ok(blob.push(x).into())
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// blob op= char
|
||||||
|
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<char>()) {
|
||||||
|
use crate::Blob;
|
||||||
|
|
||||||
|
return match op {
|
||||||
|
"+=" => Some(|_, args| {
|
||||||
|
let mut buf = [0_u8; 4];
|
||||||
|
let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf);
|
||||||
|
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
|
Ok(blob.extend(x.as_bytes()).into())
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// blob op= string
|
||||||
|
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<ImmutableString>()) {
|
||||||
|
use crate::Blob;
|
||||||
|
|
||||||
|
return match op {
|
||||||
|
"+=" => Some(|_, args| {
|
||||||
|
let s: crate::Blob = {
|
||||||
|
let s = args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||||
|
if s.is_empty() {
|
||||||
|
return Ok(Dynamic::UNIT);
|
||||||
|
}
|
||||||
|
s.as_bytes().into()
|
||||||
|
};
|
||||||
|
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
|
Ok(blob.extend(s).into())
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No built-in op-assignments for different types.
|
// No built-in op-assignments for different types.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::eval::{calc_index, calc_offset_len};
|
use crate::eval::{calc_index, calc_offset_len};
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
def_package, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
|
def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
|
||||||
RhaiResultOf, INT,
|
RhaiResultOf, INT,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -94,12 +94,36 @@ pub mod blob_functions {
|
|||||||
blob.resize(len, (value & 0x000000ff) as u8);
|
blob.resize(len, (value & 0x000000ff) as u8);
|
||||||
Ok(blob)
|
Ok(blob)
|
||||||
}
|
}
|
||||||
|
/// Convert the BLOB into an array of integers.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let b = blob(5, 0x42);
|
||||||
|
///
|
||||||
|
/// let x = b.to_array();
|
||||||
|
///
|
||||||
|
/// print(x); // prints "[66, 66, 66, 66, 66]"
|
||||||
|
/// ```
|
||||||
|
#[rhai_fn(pure)]
|
||||||
|
pub fn to_array(blob: &mut Blob) -> Array {
|
||||||
|
blob.iter().map(|&ch| (ch as INT).into()).collect()
|
||||||
|
}
|
||||||
/// Return the length of the BLOB.
|
/// Return the length of the BLOB.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let b = blob(10, 0x42);
|
||||||
|
///
|
||||||
|
/// print(b); // prints "[4242424242424242 4242]"
|
||||||
|
///
|
||||||
|
/// print(b.len()); // prints 10
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "len", get = "len", pure)]
|
#[rhai_fn(name = "len", get = "len", pure)]
|
||||||
pub fn len(blob: &mut Blob) -> INT {
|
pub fn len(blob: &mut Blob) -> INT {
|
||||||
blob.len() as INT
|
blob.len() as INT
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the byte value at the `index` position in the BLOB.
|
/// Get the byte value at the `index` position in the BLOB.
|
||||||
///
|
///
|
||||||
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element).
|
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element).
|
||||||
@ -196,15 +220,49 @@ pub mod blob_functions {
|
|||||||
///
|
///
|
||||||
/// print(b1); // prints "[4242424242111111]"
|
/// print(b1); // prints "[4242424242111111]"
|
||||||
/// ```
|
/// ```
|
||||||
pub fn append(blob: &mut Blob, y: Blob) {
|
pub fn append(blob1: &mut Blob, blob2: Blob) {
|
||||||
if !y.is_empty() {
|
if !blob2.is_empty() {
|
||||||
if blob.is_empty() {
|
if blob1.is_empty() {
|
||||||
*blob = y;
|
*blob1 = blob2;
|
||||||
} else {
|
} else {
|
||||||
blob.extend(y);
|
blob1.extend(blob2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Add a string (as UTF-8 encoded byte-stream) to the end of the BLOB
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let b = blob(5, 0x42);
|
||||||
|
///
|
||||||
|
/// b.append("hello");
|
||||||
|
///
|
||||||
|
/// print(b); // prints "[424242424268656c 6c6f]"
|
||||||
|
/// ```
|
||||||
|
#[rhai_fn(name = "+=", name = "append")]
|
||||||
|
pub fn append_str(blob: &mut Blob, string: ImmutableString) {
|
||||||
|
if !string.is_empty() {
|
||||||
|
blob.extend(string.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Add a string (as UTF-8 encoded byte-stream) to the end of the BLOB
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let b = blob(5, 0x42);
|
||||||
|
///
|
||||||
|
/// b.append('!');
|
||||||
|
///
|
||||||
|
/// print(b); // prints "[424242424221]"
|
||||||
|
/// ```
|
||||||
|
#[rhai_fn(name = "+=", name = "append")]
|
||||||
|
pub fn append_char(blob: &mut Blob, character: char) {
|
||||||
|
let mut buf = [0_u8; 4];
|
||||||
|
let x = character.encode_utf8(&mut buf);
|
||||||
|
blob.extend(x.as_bytes());
|
||||||
|
}
|
||||||
/// Add another BLOB to the end of the BLOB, returning it as a new BLOB.
|
/// Add another BLOB to the end of the BLOB, returning it as a new BLOB.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -6,6 +6,9 @@ use std::{any::TypeId, mem};
|
|||||||
|
|
||||||
use super::string_basic::{print_with_func, FUNC_TO_STRING};
|
use super::string_basic::{print_with_func, FUNC_TO_STRING};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
use crate::Blob;
|
||||||
|
|
||||||
def_package! {
|
def_package! {
|
||||||
/// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage]
|
/// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage]
|
||||||
pub MoreStringPackage(lib) {
|
pub MoreStringPackage(lib) {
|
||||||
@ -19,7 +22,7 @@ def_package! {
|
|||||||
mod string_functions {
|
mod string_functions {
|
||||||
use crate::{ImmutableString, SmartString};
|
use crate::{ImmutableString, SmartString};
|
||||||
|
|
||||||
#[rhai_fn(name = "+", name = "append")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add_append(
|
pub fn add_append(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
string: ImmutableString,
|
string: ImmutableString,
|
||||||
@ -33,6 +36,14 @@ mod string_functions {
|
|||||||
format!("{}{}", string, s).into()
|
format!("{}{}", string, s).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "+=", name = "append")]
|
||||||
|
pub fn add(ctx: NativeCallContext, string: &mut ImmutableString, mut item: Dynamic) {
|
||||||
|
let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item);
|
||||||
|
|
||||||
|
if !s.is_empty() {
|
||||||
|
*string = format!("{}{}", string, s).into();
|
||||||
|
}
|
||||||
|
}
|
||||||
#[rhai_fn(name = "+", pure)]
|
#[rhai_fn(name = "+", pure)]
|
||||||
pub fn add_prepend(
|
pub fn add_prepend(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
@ -48,11 +59,13 @@ mod string_functions {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+", name = "append")]
|
// The following are needed in order to override the generic versions with `Dynamic` parameters.
|
||||||
|
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add_append_str(string1: ImmutableString, string2: ImmutableString) -> ImmutableString {
|
pub fn add_append_str(string1: ImmutableString, string2: ImmutableString) -> ImmutableString {
|
||||||
string1 + string2
|
string1 + string2
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+", name = "append")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add_append_char(string: ImmutableString, character: char) -> ImmutableString {
|
pub fn add_append_char(string: ImmutableString, character: char) -> ImmutableString {
|
||||||
string + character
|
string + character
|
||||||
}
|
}
|
||||||
@ -61,7 +74,7 @@ mod string_functions {
|
|||||||
format!("{}{}", character, string).into()
|
format!("{}{}", character, string).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+", name = "append")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add_append_unit(string: ImmutableString, item: ()) -> ImmutableString {
|
pub fn add_append_unit(string: ImmutableString, item: ()) -> ImmutableString {
|
||||||
let _item = item;
|
let _item = item;
|
||||||
string
|
string
|
||||||
@ -71,6 +84,30 @@ mod string_functions {
|
|||||||
string
|
string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
pub mod blob_functions {
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
|
pub fn add_append_blob(string: ImmutableString, utf8: Blob) -> ImmutableString {
|
||||||
|
if utf8.is_empty() {
|
||||||
|
string
|
||||||
|
} else if string.is_empty() {
|
||||||
|
String::from_utf8_lossy(&utf8).into_owned().into()
|
||||||
|
} else {
|
||||||
|
let mut s = crate::SmartString::from(string);
|
||||||
|
s.push_str(&String::from_utf8_lossy(&utf8));
|
||||||
|
s.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "append")]
|
||||||
|
pub fn add_blob(string: &mut ImmutableString, utf8: Blob) {
|
||||||
|
let mut s = crate::SmartString::from(string.as_str());
|
||||||
|
if !utf8.is_empty() {
|
||||||
|
s.push_str(&String::from_utf8_lossy(&utf8));
|
||||||
|
*string = s.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the length of the string, in number of characters.
|
/// Return the length of the string, in number of characters.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -105,6 +142,25 @@ mod string_functions {
|
|||||||
string.len() as INT
|
string.len() as INT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Convert the string into an UTF-8 encoded byte-stream as a BLOB.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let text = "朝には紅顔ありて夕べには白骨となる";
|
||||||
|
///
|
||||||
|
/// let bytes = text.to_blob();
|
||||||
|
///
|
||||||
|
/// print(bytes.len()); // prints 51
|
||||||
|
/// ```
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
pub fn to_blob(string: &str) -> crate::Blob {
|
||||||
|
if string.is_empty() {
|
||||||
|
crate::Blob::new()
|
||||||
|
} else {
|
||||||
|
string.as_bytes().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Remove all occurrences of a sub-string from the string.
|
/// Remove all occurrences of a sub-string from the string.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
Loading…
Reference in New Issue
Block a user