Correct speed claim and others in docs.
This commit is contained in:
parent
d73f3a1d60
commit
0182b2d3f4
@ -30,6 +30,7 @@ New features
|
||||
------------
|
||||
|
||||
* `AST::iter_functions` now returns `ScriptFnMetadata` which includes, among others, _doc-comments_ for functions prefixed by `///` or `/**`.
|
||||
* A functions lookup cache is added to make function call resolution faster.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
@ -27,7 +27,6 @@ Fast
|
||||
* Fairly low compile-time overhead.
|
||||
|
||||
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single core, 2.3 GHz Linux VM).
|
||||
An unofficial Fibonacci benchmark puts Rhai somewhere between Wren and Python.
|
||||
|
||||
* Scripts are [optimized][script optimization] (useful for template-based machine-generated scripts) for repeated evaluations.
|
||||
|
||||
|
@ -27,8 +27,8 @@ It doesn't attempt to be a new language. For example:
|
||||
There is, however, support for simulated [closures] via [currying] a [function pointer] with
|
||||
captured shared variables.
|
||||
|
||||
* **No byte-codes/JIT** - Rhai has an optimized AST-walking interpreter which is fast enough for most usage scenarios.
|
||||
Essential AST data structures are packed and kept together to maximize cache friendliness.
|
||||
* **No byte-codes/JIT** - Rhai has an optimized AST-walking interpreter which is fast enough for most casual
|
||||
usage scenarios. Essential AST data structures are packed and kept together to maximize cache friendliness.
|
||||
|
||||
Functions are dispatched based on pre-calculated hashes and accessing variables are mostly through pre-calculated
|
||||
offsets to the variables file (a [`Scope`]), so it is seldom necessary to look something up by text name.
|
||||
|
@ -52,7 +52,7 @@ the return value, are [`Dynamic`] the types are simply not shown.
|
||||
|
||||
A script-defined function always takes dynamic arguments, and the return type is also dynamic:
|
||||
|
||||
> `foo(x, y, z)`
|
||||
> `foo(x, y, z) -> Dynamic`
|
||||
|
||||
probably defined as:
|
||||
|
||||
@ -71,17 +71,17 @@ is the same as:
|
||||
Functions defined in [plugin modules] are the best. They contain all the metadata
|
||||
describing the functions.
|
||||
|
||||
A plugin function `merge`:
|
||||
For example, a plugin function `merge`:
|
||||
|
||||
> `merge(list: &mut MyStruct<i64>, num: usize, name: &str) -> Option<bool>`
|
||||
|
||||
Notice that function names do not need to be valid identifiers.
|
||||
|
||||
An operator defined as a [fallible function] in a [plugin module] via `#[rhai_fn(name="+=", return_raw)]`
|
||||
returns `Result<bool, Box<EvalAltResult>>`:
|
||||
For example, an operator defined as a [fallible function] in a [plugin module] via
|
||||
`#[rhai_fn(name="+=", return_raw)]` returns `Result<bool, Box<EvalAltResult>>`:
|
||||
|
||||
> `+=(list: &mut MyStruct<i64>, num: usize, name: &str) -> Result<bool, Box<EvalAltResult>>`
|
||||
|
||||
A [property getter][getters/setters] defined in a [plugin module]:
|
||||
For example, a [property getter][getters/setters] defined in a [plugin module]:
|
||||
|
||||
> `get$prop(obj: &mut MyStruct<i64>) -> String`
|
||||
|
@ -178,22 +178,21 @@ Use `switch` Through Arrays
|
||||
---------------------------
|
||||
|
||||
Another way to work with Rust enums in a `switch` expression is through exposing the internal data
|
||||
of each enum variant as a variable-length [array], usually with the name of the variant as
|
||||
the first item for convenience:
|
||||
(or at least those that act as effective _discriminants_) of each enum variant as a variable-length
|
||||
[array], usually with the name of the variant as the first item for convenience:
|
||||
|
||||
```rust
|
||||
use rhai::Array;
|
||||
|
||||
engine.register_get("enum_data", |x: &mut Enum| {
|
||||
match x {
|
||||
Enum::Foo => vec![
|
||||
"Foo".into()
|
||||
] as Array,
|
||||
Enum::Foo => vec![ "Foo".into() ] as Array,
|
||||
|
||||
Enum::Bar(value) => vec![
|
||||
"Bar".into(), (*value).into()
|
||||
] as Array,
|
||||
// Say, skip the data field because it is not
|
||||
// used as a discriminant
|
||||
Enum::Bar(value) => vec![ "Bar".into() ] as Array,
|
||||
|
||||
// Say, all fields act as discriminants
|
||||
Enum::Baz(val1, val2) => vec![
|
||||
"Baz".into(), val1.clone().into(), (*val2).into()
|
||||
] as Array
|
||||
@ -208,8 +207,7 @@ Then it is a simple matter to match an enum via the `switch` expression:
|
||||
// 'enum_data' creates a variable-length array with 'MyEnum' data
|
||||
let x = switch value.enum_data {
|
||||
["Foo"] => 1,
|
||||
["Bar", 42] => 2,
|
||||
["Bar", 123] => 3,
|
||||
["Bar"] => value.field_1,
|
||||
["Baz", "hello", false] => 4,
|
||||
["Baz", "hello", true] => 5,
|
||||
_ => 9
|
||||
@ -220,10 +218,20 @@ x == 5;
|
||||
// Which is essentially the same as:
|
||||
let x = switch [value.type, value.field_0, value.field_1] {
|
||||
["Foo", (), ()] => 1,
|
||||
["Bar", 42, ()] => 2,
|
||||
["Bar", 123, ()] => 3,
|
||||
["Bar", 42, ()] => 42,
|
||||
["Bar", 123, ()] => 123,
|
||||
:
|
||||
["Baz", "hello", false] => 4,
|
||||
["Baz", "hello", true] => 5,
|
||||
_ => 9
|
||||
}
|
||||
```
|
||||
|
||||
Usually, a helper method returns an array of values that can uniquely determine
|
||||
the switch case based on actual usage requirements - which means that it probably
|
||||
skips fields that contain data instead of discriminants.
|
||||
|
||||
Then `switch` is used to very quickly match through a large number of array shapes
|
||||
and jump to the appropriate case implementation.
|
||||
|
||||
Data fields can then be extracted from the enum independently.
|
||||
|
@ -26,10 +26,10 @@ Use Closures to Define Methods
|
||||
-----------------------------
|
||||
|
||||
[Anonymous functions] or [closures] defined as values for [object map] properties take on
|
||||
a syntactic shape that resembles very closely that of class methods in an OOP language.
|
||||
a syntactic shape which resembles very closely that of class methods in an OOP language.
|
||||
|
||||
Closures also _[capture][automatic currying]_ variables from the defining environment, which is a very
|
||||
common OOP pattern. Capturing is accomplished via a feature called _[automatic currying]_ and
|
||||
common language feature. Capturing is accomplished via a feature called _[automatic currying]_ and
|
||||
can be turned off via the [`no_closure`] feature.
|
||||
|
||||
|
||||
@ -59,3 +59,51 @@ factor = 2;
|
||||
obj.update(42);
|
||||
obj.action(); // prints 84
|
||||
```
|
||||
|
||||
|
||||
Simulating Inheritance With Mixin
|
||||
--------------------------------
|
||||
|
||||
The `fill_with` method of [object maps] can be conveniently used to _polyfill_ default
|
||||
method implementations from a _base class_, as per OOP lingo.
|
||||
|
||||
Do not use the `mixin` method because it _overwrites_ existing fields.
|
||||
|
||||
```rust
|
||||
// Define base class
|
||||
let BaseClass = #{
|
||||
factor: 1,
|
||||
data: 42,
|
||||
|
||||
get_data: || this.data * 2,
|
||||
update: |x| this.data += x * this.factor
|
||||
};
|
||||
|
||||
let obj = #{
|
||||
// Override base class field
|
||||
factor: 100,
|
||||
|
||||
// Override base class method
|
||||
// Notice that the base class can also be accessed, if in scope
|
||||
get_data: || this.call(BaseClass.get_data) * 999,
|
||||
}
|
||||
|
||||
// Polyfill missing fields/methods
|
||||
obj.fill_with(BaseClass);
|
||||
|
||||
// By this point, 'obj' has the following:
|
||||
//
|
||||
// #{
|
||||
// factor: 100
|
||||
// data: 42,
|
||||
// get_data: || this.call(BaseClass.get_data) * 999,
|
||||
// update: |x| this.data += x * this.factor
|
||||
// }
|
||||
|
||||
// obj.get_data() => (this.data (42) * 2) * 999
|
||||
obj.get_data() == 83916;
|
||||
|
||||
obj.update(1);
|
||||
|
||||
obj.data == 142
|
||||
```
|
||||
|
@ -649,7 +649,7 @@ impl Engine {
|
||||
state: &mut State,
|
||||
lib: &[&Module],
|
||||
script: &str,
|
||||
pos: Position,
|
||||
_pos: Position,
|
||||
_level: usize,
|
||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
self.inc_operations(state)?;
|
||||
@ -663,7 +663,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if _level > self.max_call_levels() {
|
||||
return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos)));
|
||||
return Err(Box::new(EvalAltResult::ErrorStackOverflow(_pos)));
|
||||
}
|
||||
|
||||
// Compile the script text
|
||||
|
Loading…
Reference in New Issue
Block a user