Refine code and docs.

This commit is contained in:
Stephen Chung 2020-11-03 21:50:14 +08:00
parent f74d947c6b
commit 32f41c69bd
11 changed files with 67 additions and 57 deletions

View File

@ -19,6 +19,10 @@ Breaking changes
----------------
* Custom syntax can no longer start with a keyword (even a _reserved_ one), even if it has been disabled. That is to avoid breaking scripts later when the keyword is no longer disabled.
Changes to Error Handling
------------------------
* `EvalAltResult::ErrorAssignmentToUnknownLHS` is moved to `ParseError::AssignmentToInvalidLHS`. `ParseError::AssignmentToCopy` is removed.
* `EvalAltResult::ErrorDataTooLarge` is simplified.
* `Engine::on_progress` closure signature now returns `Option<Dynamic>` with the termination value passed on to `EvalAltResult::ErrorTerminated`.
@ -30,11 +34,13 @@ New features
* `f32_float` feature to set `FLOAT` to `f32`.
* Low-level API for custom syntax allowing more flexibility in designing the syntax.
* `Module::fill_with` to poly-fill a module with another.
* Scripts terminated via `Engine::on_progress` can now pass on a value as a termination token.
Enhancements
------------
* Essential AST structures like `Expr` and `Stmt` are packed into smaller sizes (16 bytes and 32 bytes on 64-bit), stored inline for more cache friendliness, and de-`Box`ed as much as possible.
* `Scope` is optimized for cache friendliness.
Version 0.19.3

View File

@ -24,7 +24,21 @@ engine.on_progress(|&count| { // parameter is '&u64' - number of operations al
The closure passed to `Engine::on_progress` will be called once for every operation.
Return `Some(token)` to terminate the script immediately, with the provided value
(any [`Dynamic`] value) passed to `EvalAltResult::ErrorTerminated` as a termination token.
(any [`Dynamic`]) acting as a termination token.
Termination Token
-----------------
The [`Dynamic`] value returned by the closure for `Engine::on_progress` is a _termination token_.
A script that is manually terminated returns with `Err(EvalAltResult::ErrorTerminated)`
wrapping this value.
The termination token is commonly used to provide information on the _reason_ or _source_
behind the termination decision.
If the termination token is not needed, simply return `Some(().into())` to terminate the script
run with [`()`] as the token.
Operations Count vs. Progress Percentage

View File

@ -29,8 +29,9 @@ due to 64-bit arithmetic requiring more CPU cycles to complete.
Minimize Size of `Dynamic`
-------------------------
Turning on [`no_float`] and [`only_i32`] on 32-bit targets makes the critical [`Dynamic`] data type only 8 bytes long.
Normally [`Dynamic`] can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`.
Turning on [`no_float`] or [`f32_float`] and [`only_i32`] on 32-bit targets makes the critical [`Dynamic`]
data type only 8 bytes long.
Normally [`Dynamic`] can be up to 12-16 bytes in order to hold an `i64` or `f64`.
A small [`Dynamic`] helps performance due to better cache efficiency.

View File

@ -42,13 +42,14 @@ are typically used for a WASM build:
| Feature | Description |
| :-----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`unchecked`] | When a WASM module panics, it doesn't crash the entire web app; however this also disables [maximum number of operations] and [progress] tracking so a script can still run indefinitely - the web app must terminate it itself. |
| [`only_i32`] | JavaScript has only one `number` type and we're only supporting `wasm32` here (so far). |
| [`only_i32`] | WASM supports 32-bit and 64-bit integers, but most scripts will only need 32-bit. |
| [`f32_float`] | WASM supports 32-bit single-precision and 64-bit double-precision floating-point numbers, but single-precision is usually fine for most uses. |
| [`no_module`] | A WASM module cannot load modules from the file system, so usually this is not needed, but the savings are minimal; alternatively, a custom [module resolver] can be provided that loads other Rhai scripts. |
The following features are typically _not_ used because they don't make sense in a WASM build:
| Feature | Why unnecessary |
| :-----------: | ------------------------------------------------------------------ |
| [`sync`] | WASM is single-threaded. |
| [`no_std`] | `std` lib works fine with WASM. |
| [`internals`] | WASM usually doesn't need to access Rhai internal data structures. |
| Feature | Why unnecessary |
| :-----------: | ------------------------------------------------------------------------------------------------------ |
| [`sync`] | WASM is single-threaded. |
| [`no_std`] | `std` lib works fine with WASM. |
| [`internals`] | WASM usually doesn't need to access Rhai internal data structures, unless you are walking the [`AST`]. |

View File

@ -17,7 +17,7 @@ more control over what a script can (or cannot) do.
| `sync` | no | restricts all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and [`AST`], are all `Send + Sync` |
| `no_optimize` | no | disables [script optimization] |
| `no_float` | no | disables floating-point numbers and math |
| `f32_float` | no | sets the system floating-point type to `f32` instead of `f64` |
| `f32_float` | no | sets the system floating-point type to `f32` instead of `f64`. `FLOAT` is set to `f32` |
| `only_i32` | no | sets the system integer type to `i32` and disable all other integer types. `INT` is set to `i32` |
| `only_i64` | no | sets the system integer type to `i64` and disable all other integer types. `INT` is set to `i64` |
| `no_index` | no | disables [arrays] and indexing features |

View File

@ -115,8 +115,8 @@ pub struct FnPtr(ImmutableString, StaticVec<Dynamic>);
impl FnPtr {
/// Create a new function pointer.
#[inline(always)]
pub(crate) fn new_unchecked<S: Into<ImmutableString>>(
name: S,
pub(crate) fn new_unchecked(
name: impl Into<ImmutableString>,
curry: StaticVec<Dynamic>,
) -> Self {
Self(name.into(), curry)
@ -147,7 +147,6 @@ impl FnPtr {
pub fn is_anonymous(&self) -> bool {
self.0.starts_with(FN_ANONYMOUS)
}
/// Call the function pointer with curried arguments (if any).
///
/// If this function is a script-defined function, it must not be marked private.

View File

@ -144,7 +144,7 @@ macro_rules! make_func {
/// To Dynamic mapping function.
#[inline(always)]
pub fn map_dynamic<T: Variant + Clone>(data: T) -> Result<Dynamic, Box<EvalAltResult>> {
pub fn map_dynamic(data: impl Variant + Clone) -> Result<Dynamic, Box<EvalAltResult>> {
Ok(data.into_dynamic())
}

View File

@ -48,7 +48,7 @@ impl StaticModuleResolver {
}
/// Add a module keyed by its path.
#[inline(always)]
pub fn insert<S: Into<String>>(&mut self, path: S, module: Module) {
pub fn insert(&mut self, path: impl Into<String>, module: Module) {
self.0.insert(path.into(), module);
}
/// Remove a module given its path.

View File

@ -168,10 +168,10 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ```
#[inline(always)]
pub fn push<K: Into<Cow<'a, str>>, T: Variant + Clone>(
pub fn push(
&mut self,
name: K,
value: T,
name: impl Into<Cow<'a, str>>,
value: impl Variant + Clone,
) -> &mut Self {
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
}
@ -189,7 +189,7 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ```
#[inline(always)]
pub fn push_dynamic<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Dynamic) -> &mut Self {
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
self.push_dynamic_value(name, EntryType::Normal, value)
}
@ -212,10 +212,10 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ```
#[inline(always)]
pub fn push_constant<K: Into<Cow<'a, str>>, T: Variant + Clone>(
pub fn push_constant(
&mut self,
name: K,
value: T,
name: impl Into<Cow<'a, str>>,
value: impl Variant + Clone,
) -> &mut Self {
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
}
@ -240,9 +240,9 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ```
#[inline(always)]
pub fn push_constant_dynamic<K: Into<Cow<'a, str>>>(
pub fn push_constant_dynamic(
&mut self,
name: K,
name: impl Into<Cow<'a, str>>,
value: Dynamic,
) -> &mut Self {
self.push_dynamic_value(name, EntryType::Constant, value)
@ -250,9 +250,9 @@ impl<'a> Scope<'a> {
/// Add (push) a new entry with a `Dynamic` value to the Scope.
#[inline]
pub(crate) fn push_dynamic_value<K: Into<Cow<'a, str>>>(
pub(crate) fn push_dynamic_value(
&mut self,
name: K,
name: impl Into<Cow<'a, str>>,
entry_type: EntryType,
value: Dynamic,
) -> &mut Self {
@ -377,7 +377,7 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
/// ```
#[inline(always)]
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) -> &mut Self {
pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self {
match self.get_index(name) {
None => {
self.push(name, value);

View File

@ -452,28 +452,19 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
}
/// `SeqAccess` implementation for arrays.
struct IterateArray<'a, ITER>
where
ITER: Iterator<Item = &'a Dynamic>,
{
struct IterateArray<'a, ITER: Iterator<Item = &'a Dynamic>> {
/// Iterator for a stream of `Dynamic` values.
iter: ITER,
}
#[cfg(not(feature = "no_index"))]
impl<'a, ITER> IterateArray<'a, ITER>
where
ITER: Iterator<Item = &'a Dynamic>,
{
impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateArray<'a, ITER> {
pub fn new(iter: ITER) -> Self {
Self { iter }
}
}
impl<'a: 'de, 'de, ITER> SeqAccess<'de> for IterateArray<'a, ITER>
where
ITER: Iterator<Item = &'a Dynamic>,
{
impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> SeqAccess<'de> for IterateArray<'a, ITER> {
type Error = Box<EvalAltResult>;
fn next_element_seed<T: DeserializeSeed<'de>>(
@ -555,10 +546,10 @@ impl<'t, 'de> EnumAccess<'de> for EnumDeserializer<'t, 'de> {
type Error = Box<EvalAltResult>;
type Variant = Self;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'de>,
{
fn variant_seed<V: DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error> {
seed.deserialize(self.tag.into_deserializer())
.map(|v| (v, self))
}
@ -572,28 +563,26 @@ impl<'t, 'de> VariantAccess<'de> for EnumDeserializer<'t, 'de> {
Deserialize::deserialize(&mut self.content)
}
fn newtype_variant_seed<T>(mut self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'de>,
{
fn newtype_variant_seed<T: DeserializeSeed<'de>>(
mut self,
seed: T,
) -> Result<T::Value, Self::Error> {
seed.deserialize(&mut self.content)
}
fn tuple_variant<V>(mut self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
fn tuple_variant<V: Visitor<'de>>(
mut self,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error> {
self.content.deserialize_tuple(len, visitor)
}
fn struct_variant<V>(
fn struct_variant<V: Visitor<'de>>(
mut self,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
) -> Result<V::Value, Self::Error> {
self.content.deserialize_struct("", fields, visitor)
}
}

View File

@ -66,7 +66,7 @@ macro_rules! reg_functions {
}
}
fn make_greeting<T: std::fmt::Display>(n: T) -> String {
fn make_greeting(n: impl std::fmt::Display) -> String {
format!("{} kitties", n)
}