More benchmarks and examples.

This commit is contained in:
Stephen Chung 2020-05-31 23:44:49 +08:00
parent 331513f5e0
commit c6e5f672c9
5 changed files with 189 additions and 39 deletions

View File

@ -37,7 +37,7 @@ Features
to do checked arithmetic operations); for [`no-std`](#optional-features) builds, a number of additional dependencies are
pulled in to provide for functionalities that used to be in `std`.
**Note:** Currently, the version is 0.15.0, so the language and API's may change before they stabilize.
**Note:** Currently, the version is `0.15.0`, so the language and API's may change before they stabilize.
What Rhai doesn't do
--------------------
@ -67,7 +67,7 @@ This is similar to some dynamic languages where most of the core functionalities
Installation
------------
Install the Rhai crate by adding this line to `dependencies`:
Install the Rhai crate on [`crates.io`](https::/crates.io/crates/rhai/) by adding this line to `dependencies`:
```toml
[dependencies]
@ -191,33 +191,35 @@ A number of examples can be found in the `examples` folder:
Examples can be run with the following command:
```bash
cargo run --example name
cargo run --example {example_name}
```
The `repl` example is a particularly good one as it allows one to interactively try out Rhai's
language features in a standard REPL (**R**ead-**E**val-**P**rint **L**oop).
Example Scripts
Example scripts
---------------
There are also a number of examples scripts that showcase Rhai's features, all in the `scripts` folder:
| Language feature scripts | Description |
| ---------------------------------------------------- | ------------------------------------------------------------- |
| ---------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`array.rhai`](scripts/array.rhai) | [arrays] in Rhai |
| [`assignment.rhai`](scripts/assignment.rhai) | variable declarations |
| [`comments.rhai`](scripts/comments.rhai) | just comments |
| [`for1.rhai`](scripts/for1.rhai) | for loops |
| [`function_decl1.rhai`](scripts/function_decl1.rhai) | a function without parameters |
| [`function_decl2.rhai`](scripts/function_decl2.rhai) | a function with two parameters |
| [`function_decl3.rhai`](scripts/function_decl3.rhai) | a function with many parameters |
| [`if1.rhai`](scripts/if1.rhai) | if example |
| [`loop.rhai`](scripts/loop.rhai) | endless loop in Rhai, this example emulates a do..while cycle |
| [`op1.rhai`](scripts/op1.rhai) | just a simple addition |
| [`for1.rhai`](scripts/for1.rhai) | [`for`](#for-loop) loops |
| [`for2.rhai`](scripts/for2.rhai) | [`for`](#for-loop) loops on [arrays] |
| [`function_decl1.rhai`](scripts/function_decl1.rhai) | a [function] without parameters |
| [`function_decl2.rhai`](scripts/function_decl2.rhai) | a [function] with two parameters |
| [`function_decl3.rhai`](scripts/function_decl3.rhai) | a [function] with many parameters |
| [`if1.rhai`](scripts/if1.rhai) | [`if`](#if-statement) example |
| [`loop.rhai`](scripts/loop.rhai) | count-down [`loop`](#infinite-loop) in Rhai, emulating a `do` .. `while` loop |
| [`op1.rhai`](scripts/op1.rhai) | just simple addition |
| [`op2.rhai`](scripts/op2.rhai) | simple addition and multiplication |
| [`op3.rhai`](scripts/op3.rhai) | change evaluation order with parenthesis |
| [`string.rhai`](scripts/string.rhai) | [string] operations |
| [`while.rhai`](scripts/while.rhai) | while loop |
| [`strings_map.rhai`](scripts/strings_map.rhai) | [string] and [object map] operations |
| [`while.rhai`](scripts/while.rhai) | [`while`](#while-loop) loop |
| Example scripts | Description |
| -------------------------------------------- | ---------------------------------------------------------------------------------- |
@ -247,6 +249,7 @@ fn main() -> Result<(), Box<EvalAltResult>>
let engine = Engine::new();
let result = engine.eval::<i64>("40 + 2")?;
// ^^^^^^^ cast the result to an 'i64', this is required
println!("Answer: {}", result); // prints 42
@ -303,6 +306,8 @@ let ast = engine.compile_file("hello_world.rhai".into())?;
### Calling Rhai functions from Rust
[`private`]: #calling-rhai-functions-from-rust
Rhai also allows working _backwards_ from the other direction - i.e. calling a Rhai-scripted function from Rust via `Engine::call_fn`.
Functions declared with `private` are hidden and cannot be called from Rust (see also [modules]).
@ -1870,8 +1875,8 @@ my_str += 12345;
my_str == "abcABC12345"
```
`if` statements
---------------
`if` statement
--------------
```rust
if foo(x) {
@ -1906,8 +1911,8 @@ let x = if decision { 42 }; // no else branch defaults to '()'
x == ();
```
`while` loops
-------------
`while` loop
------------
```rust
let x = 10;
@ -1934,8 +1939,8 @@ loop {
}
```
`for` loops
-----------
`for` loop
----------
Iterating through a range or an [array] is provided by the `for` ... `in` loop.
@ -2206,8 +2211,8 @@ Modules can be disabled via the [`no_module`] feature.
A _module_ is a single script (or pre-compiled `AST`) containing global variables and functions.
The `export` statement, which can only be at global level, exposes selected variables as members of a module.
Variables not exported are _private_ and invisible to the outside.
On the other hand, all functions are automatically exported, _unless_ it is explicitly opt-out with the `private` prefix.
Functions declared `private` are invisible to the outside.
On the other hand, all functions are automatically exported, _unless_ it is explicitly opt-out with the [`private`] prefix.
Functions declared [`private`] are invisible to the outside.
Everything exported from a module is **constant** (**read-only**).
@ -2301,7 +2306,7 @@ engine.eval_expression_with_scope::<i64>(&scope, "question::inc(question::answer
It is easy to convert a pre-compiled `AST` into a module: just use `Module::eval_ast_as_new`.
Don't forget the `export` statement, otherwise there will be no variables exposed by the module
other than non-`private` functions (unless that's intentional).
other than non-[`private`] functions (unless that's intentional).
```rust
use rhai::{Engine, Module};

View File

@ -61,3 +61,27 @@ fn bench_eval_array_large_set(bench: &mut Bencher) {
bench.iter(|| engine.consume_ast(&ast).unwrap());
}
#[bench]
fn bench_eval_array_loop(bench: &mut Bencher) {
let script = r#"
let list = [];
for i in range(0, 10_000) {
list.push(i);
}
let sum = 0;
for i in list {
sum += i;
}
"#;
let mut engine = Engine::new();
engine.set_optimization_level(OptimizationLevel::None);
let ast = engine.compile(script).unwrap();
bench.iter(|| engine.consume_ast(&ast).unwrap());
}

22
scripts/for2.rhai Normal file
View File

@ -0,0 +1,22 @@
const MAX = 1_000_000;
print("Iterating an array with " + MAX + " items...");
print("Ready... Go!");
let now = timestamp();
let list = [];
for i in range(0, MAX) {
list.push(i);
}
let sum = 0;
for i in list {
sum += i;
}
print("Sum = " + sum);
print("Finished. Run time = " + now.elapsed + " seconds.");

103
scripts/strings_map.rhai Normal file
View File

@ -0,0 +1,103 @@
print("Ready... Go!");
let now = timestamp();
let adverbs = [ "moderately", "really", "slightly", "very" ];
let adjectives = [
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
"common", "compassionate", "competent", "complete", "complex", "complicated",
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
"constant", "content", "conventional", "cooked", "cool", "cooperative",
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
"crafty"
];
let animals = [
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
"wallaby", "wildebeest"
];
let keys = [];
for animal in animals {
for adjective in adjectives {
for adverb in adverbs {
keys.push(adverb + " " + adjective + " " + animal)
}
}
}
let map = #{};
let i = 0;
for key in keys {
map[key] = i;
i += 1;
}
let sum = 0;
for key in keys {
sum += map[key];
}
for key in keys {
map.remove(key);
}
print("Sum = " + sum);
print("Finished. Run time = " + now.elapsed + " seconds.");

View File

@ -385,10 +385,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
// ( stmt )
stmt => Expr::Stmt(Box::new((stmt, x.1))),
},
// id = expr
// id op= expr
Expr::Assignment(x) => match x.2 {
//id = id2 op= expr2
Expr::Assignment(x2) if x.1 == "=" => match (x.0, x2.0) {
//id = id2 op= rhs
Expr::Assignment(x2) if x.1.is_empty() => match (x.0, &x2.0) {
// var = var op= expr2 -> var op= expr2
(Expr::Variable(a), Expr::Variable(b))
if a.1.is_none() && b.1.is_none() && a.0 == b.0 && a.3 == b.3 =>
@ -397,14 +397,10 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
state.set_dirty();
Expr::Assignment(Box::new((Expr::Variable(a), x2.1, optimize_expr(x2.2, state), x.3)))
}
// id1 = id2 op= expr2
(id1, id2) => {
Expr::Assignment(Box::new((
id1, x.1, Expr::Assignment(Box::new((id2, x2.1, optimize_expr(x2.2, state), x2.3))), x.3,
)))
}
// expr1 = expr2 op= rhs
(expr1, _) => Expr::Assignment(Box::new((expr1, x.1, optimize_expr(Expr::Assignment(x2), state), x.3))),
},
// id op= expr
// expr = rhs
expr => Expr::Assignment(Box::new((x.0, x.1, optimize_expr(expr, state), x.3))),
},