Merge pull request #200 from schungx/master

Documentation improvements and syncing up.
This commit is contained in:
Stephen Chung 2020-07-25 16:19:48 +08:00 committed by GitHub
commit fc891dcdac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 298 additions and 48 deletions

29
.github/workflows/benchmark.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Benchmark
on:
push:
branches:
- master
jobs:
benchmark:
name: Run Rust benchmark
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: rustup toolchain update nightly && rustup default nightly
- name: Run benchmark
run: cargo +nightly bench | tee output.txt
- name: Store benchmark result
uses: rhysd/github-action-benchmark@v1
with:
name: Rust Benchmark
tool: 'cargo'
output-file-path: output.txt
# Use personal access token instead of GITHUB_TOKEN due to https://github.community/t5/GitHub-Actions/Github-action-not-triggering-gh-pages-upon-push/td-p/26869/highlight/false
github-token: ${{ secrets.RHAI }}
auto-push: true
# Show alert with commit comment on detecting possible performance regression
alert-threshold: '200%'
comment-on-alert: true
fail-on-alert: true
alert-comment-cc-users: '@schungx'

View File

@ -75,3 +75,6 @@ optional = true
[target.'cfg(target_arch = "wasm32")'.dependencies]
instant= { version = "0.1.4", features = ["wasm-bindgen"] } # WASM implementation of std::time::Instant
[package.metadata.docs.rs]
features = [ "serde", "internals" ]

View File

@ -68,9 +68,13 @@ Scripts can be evaluated directly from the editor.
License
-------
Licensed under either of <a href="LICENSE-APACHE.txt">Apache License, Version
2.0</a> or <a href="LICENSE-MIT.txt">MIT license</a> at your option.
Licensed under either:
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
* [Apache License, Version 2.0](https://github.com/jonathandturner/rhai/blob/master/LICENSE-APACHE.txt), or
* [MIT license](https://github.com/jonathandturner/rhai/blob/master/LICENSE-MIT.txt)
at your option.
Unless explicitly stated otherwise, any contribution intentionally submitted
for inclusion in this crate, as defined in the Apache-2.0 license, shall
be dual-licensed as above, without any additional terms or conditions.

View File

@ -18,6 +18,7 @@ New features
* Anonymous functions in the syntax of a closure, e.g. `|x, y, z| x + y - z`.
* Custom syntax now works even without the `internals` feature.
* Currying of function pointers is supported via the `curry` keyword.
* `Module::set_indexer_get_set_fn` is added as a shorthand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`.
Breaking changes
----------------

View File

@ -34,9 +34,9 @@ mod private {
use crate::fn_native::SendSync;
use crate::stdlib::any::Any;
/// A sealed trait that prevents other crates from implementing [Variant].
/// A sealed trait that prevents other crates from implementing [`Variant`].
///
/// [Variant]: super::Variant
/// [`Variant`]: super::Variant
pub trait Sealed {}
impl<T: Any + Clone + SendSync> Sealed for T {}
@ -810,8 +810,3 @@ impl From<Box<FnPtr>> for Dynamic {
Self(Union::FnPtr(value))
}
}
/// Private type which ensures that `rhai::Any` and `rhai::AnyExt` can only
/// be implemented by this crate.
#[doc(hidden)]
pub struct _Private;

View File

@ -11,7 +11,7 @@ use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, St
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::syntax::{CustomSyntax, EvalContext, Expression};
use crate::syntax::{CustomSyntax, EvalContext};
use crate::token::Position;
use crate::utils::StaticVec;
@ -38,7 +38,12 @@ pub type Array = Vec<Dynamic>;
#[cfg(not(feature = "no_object"))]
pub type Map = HashMap<ImmutableString, Dynamic>;
/// A stack of imported modules.
/// [INTERNALS] A stack of imported modules.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
pub type Imports<'a> = Vec<(Cow<'a, str>, Module)>;
#[cfg(not(feature = "unchecked"))]
@ -189,12 +194,17 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
}
}
/// A type that holds all the current states of the Engine.
/// [INTERNALS] A type that holds all the current states of the Engine.
/// Exported under the `internals` feature only.
///
/// # Safety
///
/// This type uses some unsafe code, mainly for avoiding cloning of local variable names via
/// direct lifetime casting.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct State {
/// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup.
@ -1020,7 +1030,7 @@ impl Engine {
map.entry(index).or_insert(Default::default()).into()
} else {
let index = idx
.downcast_ref::<String>()
.downcast_ref::<ImmutableString>()
.ok_or_else(|| EvalAltResult::ErrorStringIndexExpr(idx_pos))?;
map.get_mut(index.as_str())
@ -1050,22 +1060,20 @@ impl Engine {
}
}
#[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))]
_ => {
let val_type_name = val.type_name();
let type_name = val.type_name();
let args = &mut [val, &mut idx];
self.exec_fn_call(
state, lib, FN_IDX_GET, true, 0, args, is_ref, true, None, level,
)
.map(|(v, _)| v.into())
.map_err(|e| match *e {
EvalAltResult::ErrorFunctionNotFound(..) => {
Box::new(EvalAltResult::ErrorIndexingType(
self.map_type_name(val_type_name).into(),
Position::none(),
))
}
_ => e,
.map_err(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(_, _) => Box::new(
EvalAltResult::ErrorIndexingType(type_name.into(), Position::none()),
),
_ => err,
})
}

View File

@ -10,7 +10,12 @@ use crate::stdlib::{
string::{String, ToString},
};
/// Error when tokenizing the script text.
/// [INTERNALS] Error encountered when tokenizing the script text.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
#[non_exhaustive]
pub enum LexError {

View File

@ -5,12 +5,13 @@ use crate::engine::Engine;
use crate::module::{FuncReturn, Module};
use crate::parser::ScriptFnDef;
use crate::result::EvalAltResult;
use crate::stdlib::vec::Vec;
use crate::token::{is_valid_identifier, Position};
use crate::utils::{ImmutableString, StaticVec};
use crate::Scope;
use crate::stdlib::{boxed::Box, convert::TryFrom, fmt, mem, rc::Rc, string::String, sync::Arc};
use crate::stdlib::{
boxed::Box, convert::TryFrom, fmt, mem, rc::Rc, string::String, sync::Arc, vec::Vec,
};
/// Trait that maps to `Send + Sync` only under the `sync` feature.
#[cfg(feature = "sync")]

View File

@ -166,7 +166,7 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]
pub use parser::{CustomExpr, Expr, ReturnType, ScriptFnDef, Stmt};
pub use parser::{CustomExpr, Expr, FloatWrapper, ReturnType, ScriptFnDef, Stmt};
#[cfg(feature = "internals")]
#[deprecated(note = "this type is volatile and may change")]

View File

@ -758,6 +758,40 @@ impl Module {
)
}
/// Set a pair of Rust index getter and setter functions, returning both hash keys.
/// This is a shorthand for `set_indexer_get_fn` and `set_indexer_set_fn`.
///
/// If there are similar existing Rust functions, they are replaced.
///
/// # Examples
///
/// ```
/// use rhai::{Module, ImmutableString};
///
/// let mut module = Module::new();
/// let (hash_get, hash_set) = module.set_indexer_get_set_fn(
/// |x: &mut i64, y: ImmutableString| {
/// Ok(*x + y.len() as i64)
/// },
/// |x: &mut i64, y: ImmutableString, value: i64| {
/// *x = y.len() as i64 + value;
/// Ok(())
/// }
/// );
/// assert!(module.contains_fn(hash_get));
/// assert!(module.contains_fn(hash_set));
/// ```
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>(
&mut self,
getter: impl Fn(&mut A, B) -> FuncReturn<T> + SendSync + 'static,
setter: impl Fn(&mut A, B, T) -> FuncReturn<()> + SendSync + 'static,
) -> (u64, u64) {
(
self.set_indexer_get_fn(getter),
self.set_indexer_set_fn(setter),
)
}
/// Set a Rust function taking four parameters into the module, returning a hash key.
///
/// If there is a similar existing Rust function, it is replaced.
@ -1094,11 +1128,17 @@ impl Module {
}
}
/// A chain of module names to qualify a variable or function call.
/// A `u64` hash key is kept for quick search purposes.
/// [INTERNALS] A chain of module names to qualify a variable or function call.
/// Exported under the `internals` feature only.
///
/// A `u64` hash key is cached for quick search purposes.
///
/// A `StaticVec` is used because most module-level access contains only one level,
/// and it is wasteful to always allocate a `Vec` with one element.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Clone, Eq, PartialEq, Default, Hash)]
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);

View File

@ -456,7 +456,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
// "xxx" in "xxxxx"
(Expr::StringConstant(a), Expr::StringConstant(b)) => {
state.set_dirty();
if b.0.contains(a.0.as_ref()) { Expr::True(a.1) } else { Expr::False(a.1) }
if b.0.contains(a.0.as_str()) { Expr::True(a.1) } else { Expr::False(a.1) }
}
// 'x' in "xxxxx"
(Expr::CharConstant(a), Expr::StringConstant(b)) => {
@ -560,7 +560,7 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
let has_script_fn = state.lib.iter_fn().find(|(_, _, _, f)| {
if !f.is_script() { return false; }
let fn_def = f.get_fn_def();
&fn_def.name == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len())
fn_def.name.as_str() == name && (args.len()..=args.len() + 1).contains(&fn_def.params.len())
}).is_some();
#[cfg(feature = "no_function")]

View File

@ -342,11 +342,16 @@ impl fmt::Display for FnAccess {
}
}
/// A scripted function definition.
/// [INTERNALS] A type containing information on a scripted function.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Hash)]
pub struct ScriptFnDef {
/// Function name.
pub name: String,
pub name: ImmutableString,
/// Function access mode.
pub access: FnAccess,
/// Names of function parameters.
@ -376,7 +381,12 @@ impl fmt::Display for ScriptFnDef {
}
}
/// `return`/`throw` statement.
/// [INTERNALS] A type encapsulating the mode of a `return`/`throw` statement.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
pub enum ReturnType {
/// `return` statement.
@ -477,7 +487,8 @@ impl ParseSettings {
}
}
/// A statement.
/// [INTERNALS] A Rhai statement.
/// Exported under the `internals` feature only.
///
/// Each variant is at most one pointer in size (for speed),
/// with everything being allocated together in one single tuple.
@ -582,6 +593,12 @@ impl Stmt {
}
}
/// [INTERNALS] A type wrapping a custom syntax definition.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Clone)]
pub struct CustomExpr(pub StaticVec<Expr>, pub Shared<FnCustomSyntaxEval>);
@ -597,6 +614,15 @@ impl Hash for CustomExpr {
}
}
/// [INTERNALS] A type wrapping a floating-point number.
/// Exported under the `internals` feature only.
///
/// This type is mainly used to provide a standard `Hash` implementation
/// to floating-point numbers, allowing `Expr` to derive `Hash` automatically.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[cfg(not(feature = "no_float"))]
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct FloatWrapper(pub FLOAT, pub Position);
@ -609,10 +635,15 @@ impl Hash for FloatWrapper {
}
}
/// An expression.
/// [INTERNALS] An expression sub-tree.
/// Exported under the `internals` feature only.
///
/// Each variant is at most one pointer in size (for speed),
/// with everything being allocated together in one single tuple.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Hash)]
pub enum Expr {
/// Integer constant.
@ -2852,7 +2883,7 @@ fn parse_fn(
let params = params.into_iter().map(|(p, _)| p).collect();
Ok(ScriptFnDef {
name,
name: name.into(),
access,
params,
body,
@ -2940,7 +2971,7 @@ fn parse_anon_fn(
let hash = s.finish();
// Create unique function name
let fn_name = format!("{}{}", FN_ANONYMOUS, hash);
let fn_name: ImmutableString = format!("{}{:16x}", FN_ANONYMOUS, hash).into();
let script = ScriptFnDef {
name: fn_name.clone(),
@ -2950,7 +2981,7 @@ fn parse_anon_fn(
pos: settings.pos,
};
let expr = Expr::FnPointer(Box::new((fn_name.into(), settings.pos)));
let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
Ok((expr, script))
}

View File

@ -136,89 +136,181 @@ impl fmt::Debug for Position {
}
}
/// Tokens.
/// [INTERNALS] A Rhai language token.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
/// An `INT` constant.
IntegerConstant(INT),
/// A `FLOAT` constaint.
///
/// Never appears under the `no_float` feature.
#[cfg(not(feature = "no_float"))]
FloatConstant(FLOAT),
/// An identifier.
Identifier(String),
/// A character constant.
CharConstant(char),
/// A string constant.
StringConstant(String),
/// `{`
LeftBrace,
/// `}`
RightBrace,
/// `(`
LeftParen,
/// `)`
RightParen,
/// `[`
LeftBracket,
/// `]`
RightBracket,
/// `+`
Plus,
/// `+` (unary)
UnaryPlus,
/// `-`
Minus,
/// `-` (unary)
UnaryMinus,
/// `*`
Multiply,
/// `/`
Divide,
/// `%`
Modulo,
/// `~`
PowerOf,
/// `<<`
LeftShift,
/// `>>`
RightShift,
/// `;`
SemiColon,
/// `:`
Colon,
/// `::`
DoubleColon,
/// `,`
Comma,
/// `.`
Period,
/// `#{`
MapStart,
/// `=`
Equals,
/// `true`
True,
/// `false`
False,
/// `let`
Let,
/// `const`
Const,
/// `if`
If,
/// `else`
Else,
/// `while`
While,
/// `loop`
Loop,
/// `for`
For,
/// `in`
In,
/// `<`
LessThan,
/// `>`
GreaterThan,
/// `<=`
LessThanEqualsTo,
/// `>=`
GreaterThanEqualsTo,
/// `==`
EqualsTo,
/// `!=`
NotEqualsTo,
/// `!`
Bang,
/// `|`
Pipe,
/// `||`
Or,
/// `^`
XOr,
/// `&`
Ampersand,
/// `&&`
And,
/// `fn`
///
/// Never appears under the `no_function` feature.
#[cfg(not(feature = "no_function"))]
Fn,
/// `continue`
Continue,
/// `break`
Break,
/// `return`
Return,
/// `throw`
Throw,
/// `+=`
PlusAssign,
/// `-=`
MinusAssign,
/// `*=`
MultiplyAssign,
/// `/=`
DivideAssign,
/// `<<=`
LeftShiftAssign,
/// `>>=`
RightShiftAssign,
/// `&=`
AndAssign,
/// `|=`
OrAssign,
/// `^=`
XOrAssign,
/// `%=`
ModuloAssign,
/// `~=`
PowerOfAssign,
/// `private`
///
/// Never appears under the `no_function` feature.
#[cfg(not(feature = "no_function"))]
Private,
/// `import`
///
/// Never appears under the `no_module` feature.
#[cfg(not(feature = "no_module"))]
Import,
/// `export`
///
/// Never appears under the `no_module` feature.
#[cfg(not(feature = "no_module"))]
Export,
/// `as`
///
/// Never appears under the `no_module` feature.
#[cfg(not(feature = "no_module"))]
As,
/// A lexer error.
LexError(Box<LexError>),
/// A comment block.
Comment(String),
/// A reserved symbol.
Reserved(String),
/// A custom keyword.
Custom(String),
/// End of the input stream.
EOF,
}
@ -566,7 +658,7 @@ impl Token {
}
}
/// Is this token a reserved keyword?
/// Is this token a reserved symbol?
pub fn is_reserved(&self) -> bool {
match self {
Self::Reserved(_) => true,
@ -589,7 +681,12 @@ impl From<Token> for String {
}
}
/// State of the tokenizer.
/// [INTERNALS] State of the tokenizer.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct TokenizeState {
/// Maximum length of a string (0 = unlimited).
@ -604,7 +701,12 @@ pub struct TokenizeState {
pub include_comments: bool,
}
/// Trait that encapsulates a peekable character input stream.
/// [INTERNALS] Trait that encapsulates a peekable character input stream.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This trait is volatile and may change.
pub trait InputStream {
/// Get the next character
fn get_next(&mut self) -> Option<char>;
@ -628,7 +730,12 @@ pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
first_alphabetic
}
/// Parse a string literal wrapped by `enclosing_char`.
/// [INTERNALS] Parse a string literal wrapped by `enclosing_char`.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
pub fn parse_string_literal(
stream: &mut impl InputStream,
state: &mut TokenizeState,
@ -794,7 +901,12 @@ fn scan_comment(
}
}
/// Get the next token.
/// [INTERNALS] Get the next token from the `InputStream`.
/// Exported under the `internals` feature only.
///
/// ## WARNING
///
/// This type is volatile and may change.
pub fn get_next_token(
stream: &mut impl InputStream,
state: &mut TokenizeState,

View File

@ -92,9 +92,12 @@ pub fn calc_fn_spec<'a>(
s.finish()
}
/// A type to hold a number of values in static storage for no-allocation, quick access.
/// [INTERNALS] An array-like type that holds a number of values in static storage for no-allocation, quick access.
/// Exported under the `internals` feature only.
///
/// If too many items are stored, it converts into using a `Vec`.
///
///
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
/// This simplified implementation here is to avoid pulling in another crate.
///
@ -130,6 +133,10 @@ pub fn calc_fn_spec<'a>(
/// # Safety
///
/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
///
/// ## WARNING
///
/// This type is volatile and may change.
//
// TODO - remove unsafe code
pub struct StaticVec<T> {

View File

@ -104,5 +104,19 @@ fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
42
);
#[cfg(not(feature = "no_object"))]
assert_eq!(
engine.eval::<INT>(
r#"
fn foo(x) { this.data += x; }
let x = #{ data: 40, action: Fn("foo") };
x.action(2);
x.data
"#
)?,
42
);
Ok(())
}