Use chars() to iterate strings.
This commit is contained in:
parent
859a18c6fd
commit
989cb702c0
@ -9,10 +9,16 @@ Bug fixes
|
|||||||
|
|
||||||
* Fixed incorrect optimization regarding chain-indexing with non-numeric index.
|
* Fixed incorrect optimization regarding chain-indexing with non-numeric index.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* To keep the API consistent, strings are no longer iterable by default. Use the `chars` method to iterator the characters in a string.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* An integer value can now be indexed to get/set a single bit.
|
* An integer value can now be indexed to get/set a single bit.
|
||||||
|
* The `bits` method of an integer can be used to iterate through its bits.
|
||||||
|
|
||||||
|
|
||||||
Version 0.20.2
|
Version 0.20.2
|
||||||
|
@ -10,7 +10,7 @@ use num_traits::{CheckedAdd as Add, CheckedSub as Sub};
|
|||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
// Register range function with step
|
// Range iterator with step
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
struct StepRange<T>(T, T, T)
|
struct StepRange<T>(T, T, T)
|
||||||
where
|
where
|
||||||
@ -126,7 +126,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register range function with step
|
// Bit-field iterator with step
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
struct BitRange(INT, INT, usize);
|
struct BitRange(INT, INT, usize);
|
||||||
|
|
||||||
@ -191,6 +191,64 @@ impl Iterator for BitRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String iterator over characters
|
||||||
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
|
struct CharsStream(Vec<char>, usize);
|
||||||
|
|
||||||
|
impl CharsStream {
|
||||||
|
pub fn new(string: &str, from: INT, len: INT) -> Self {
|
||||||
|
if len <= 0 {
|
||||||
|
return Self(Default::default(), 0);
|
||||||
|
}
|
||||||
|
if from >= 0 {
|
||||||
|
return Self(
|
||||||
|
string
|
||||||
|
.chars()
|
||||||
|
.skip(from as usize)
|
||||||
|
.take(len as usize)
|
||||||
|
.collect(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
return if let Some(abs_from) = from.checked_abs() {
|
||||||
|
let num_chars = string.chars().count();
|
||||||
|
let offset = if num_chars < (abs_from as usize) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
num_chars - (abs_from as usize)
|
||||||
|
};
|
||||||
|
Self(string.chars().skip(offset).take(len as usize).collect(), 0)
|
||||||
|
} else {
|
||||||
|
Self(string.chars().skip(0).take(len as usize).collect(), 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
return Self(
|
||||||
|
string
|
||||||
|
.chars()
|
||||||
|
.skip(from as usize)
|
||||||
|
.take(len as usize)
|
||||||
|
.collect,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for CharsStream {
|
||||||
|
type Item = char;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.1 >= self.0.len() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let ch = self.0[self.1];
|
||||||
|
self.1 += 1;
|
||||||
|
Some(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! reg_range {
|
macro_rules! reg_range {
|
||||||
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
||||||
$(
|
$(
|
||||||
@ -370,15 +428,31 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register string iterator
|
||||||
|
lib.set_iterator::<CharsStream>();
|
||||||
|
|
||||||
|
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>"]);
|
||||||
|
|
||||||
|
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>"]);
|
||||||
|
|
||||||
|
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>"]);
|
||||||
|
|
||||||
|
// Register bit-field iterator
|
||||||
lib.set_iterator::<BitRange>();
|
lib.set_iterator::<BitRange>();
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value, from, len| BitRange::new(value, from, len));
|
let _hash = lib.set_native_fn("bits", |value, from, len| BitRange::new(value, from, len));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "from: Decimal", "len: Decimal", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
|
let _hash = lib.set_native_fn("bits", |value, from| BitRange::new(value, from, INT::MAX));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "from: Decimal", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator<Item=bool>"]);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX));
|
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, Dynamic, ImmutableString, StaticVec, INT};
|
use crate::{def_package, Dynamic, StaticVec, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::TypeId, mem};
|
use std::{any::TypeId, mem};
|
||||||
@ -10,12 +10,6 @@ use super::string_basic::{print_with_func, FUNC_TO_STRING};
|
|||||||
|
|
||||||
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, {
|
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, {
|
||||||
combine_with_exported_module!(lib, "string", string_functions);
|
combine_with_exported_module!(lib, "string", string_functions);
|
||||||
|
|
||||||
// Register string iterator
|
|
||||||
lib.set_iter(
|
|
||||||
TypeId::of::<ImmutableString>(),
|
|
||||||
|string| Box::new(string.cast::<ImmutableString>().chars().collect::<Vec<_>>().into_iter().map(Into::into))
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
|
@ -113,7 +113,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorStringBounds(0, _, _) => "Empty string has nothing to index",
|
Self::ErrorStringBounds(0, _, _) => "Empty string has nothing to index",
|
||||||
Self::ErrorStringBounds(_, _, _) => "String index out of bounds",
|
Self::ErrorStringBounds(_, _, _) => "String index out of bounds",
|
||||||
Self::ErrorBitFieldBounds(_, _, _) => "Bit-field index out of bounds",
|
Self::ErrorBitFieldBounds(_, _, _) => "Bit-field index out of bounds",
|
||||||
Self::ErrorFor(_) => "For loop expects an array, object map, or range",
|
Self::ErrorFor(_) => "For loop expects a type with an iterator defined",
|
||||||
Self::ErrorVariableNotFound(_, _) => "Variable not found",
|
Self::ErrorVariableNotFound(_, _) => "Variable not found",
|
||||||
Self::ErrorModuleNotFound(_, _) => "Module not found",
|
Self::ErrorModuleNotFound(_, _) => "Module not found",
|
||||||
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
|
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
|
||||||
|
@ -235,7 +235,7 @@ fn test_for_string() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let s = "hello";
|
let s = "hello";
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
|
|
||||||
for ch in s {
|
for ch in s.chars() {
|
||||||
sum += to_int(ch);
|
sum += to_int(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user