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 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`. 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 What Rhai doesn't do
-------------------- --------------------
@ -67,7 +67,7 @@ This is similar to some dynamic languages where most of the core functionalities
Installation 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 ```toml
[dependencies] [dependencies]
@ -191,33 +191,35 @@ A number of examples can be found in the `examples` folder:
Examples can be run with the following command: Examples can be run with the following command:
```bash ```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 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). 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: There are also a number of examples scripts that showcase Rhai's features, all in the `scripts` folder:
| Language feature scripts | Description | | Language feature scripts | Description |
| ---------------------------------------------------- | ------------------------------------------------------------- | | ---------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`array.rhai`](scripts/array.rhai) | [arrays] in Rhai | | [`array.rhai`](scripts/array.rhai) | [arrays] in Rhai |
| [`assignment.rhai`](scripts/assignment.rhai) | variable declarations | | [`assignment.rhai`](scripts/assignment.rhai) | variable declarations |
| [`comments.rhai`](scripts/comments.rhai) | just comments | | [`comments.rhai`](scripts/comments.rhai) | just comments |
| [`for1.rhai`](scripts/for1.rhai) | for loops | | [`for1.rhai`](scripts/for1.rhai) | [`for`](#for-loop) loops |
| [`function_decl1.rhai`](scripts/function_decl1.rhai) | a function without parameters | | [`for2.rhai`](scripts/for2.rhai) | [`for`](#for-loop) loops on [arrays] |
| [`function_decl2.rhai`](scripts/function_decl2.rhai) | a function with two parameters | | [`function_decl1.rhai`](scripts/function_decl1.rhai) | a [function] without parameters |
| [`function_decl3.rhai`](scripts/function_decl3.rhai) | a function with many parameters | | [`function_decl2.rhai`](scripts/function_decl2.rhai) | a [function] with two parameters |
| [`if1.rhai`](scripts/if1.rhai) | if example | | [`function_decl3.rhai`](scripts/function_decl3.rhai) | a [function] with many parameters |
| [`loop.rhai`](scripts/loop.rhai) | endless loop in Rhai, this example emulates a do..while cycle | | [`if1.rhai`](scripts/if1.rhai) | [`if`](#if-statement) example |
| [`op1.rhai`](scripts/op1.rhai) | just a simple addition | | [`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 | | [`op2.rhai`](scripts/op2.rhai) | simple addition and multiplication |
| [`op3.rhai`](scripts/op3.rhai) | change evaluation order with parenthesis | | [`op3.rhai`](scripts/op3.rhai) | change evaluation order with parenthesis |
| [`string.rhai`](scripts/string.rhai) | [string] operations | | [`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 | | Example scripts | Description |
| -------------------------------------------- | ---------------------------------------------------------------------------------- | | -------------------------------------------- | ---------------------------------------------------------------------------------- |
@ -247,6 +249,7 @@ fn main() -> Result<(), Box<EvalAltResult>>
let engine = Engine::new(); let engine = Engine::new();
let result = engine.eval::<i64>("40 + 2")?; let result = engine.eval::<i64>("40 + 2")?;
// ^^^^^^^ cast the result to an 'i64', this is required
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
@ -303,6 +306,8 @@ let ast = engine.compile_file("hello_world.rhai".into())?;
### Calling Rhai functions from Rust ### 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`. 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]). 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" my_str == "abcABC12345"
``` ```
`if` statements `if` statement
--------------- --------------
```rust ```rust
if foo(x) { if foo(x) {
@ -1906,8 +1911,8 @@ let x = if decision { 42 }; // no else branch defaults to '()'
x == (); x == ();
``` ```
`while` loops `while` loop
------------- ------------
```rust ```rust
let x = 10; 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. 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. 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. 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. 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. 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. Functions declared [`private`] are invisible to the outside.
Everything exported from a module is **constant** (**read-only**). 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`. 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 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 ```rust
use rhai::{Engine, Module}; 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.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 )
stmt => Expr::Stmt(Box::new((stmt, x.1))), stmt => Expr::Stmt(Box::new((stmt, x.1))),
}, },
// id = expr // id op= expr
Expr::Assignment(x) => match x.2 { Expr::Assignment(x) => match x.2 {
//id = id2 op= expr2 //id = id2 op= rhs
Expr::Assignment(x2) if x.1 == "=" => match (x.0, x2.0) { Expr::Assignment(x2) if x.1.is_empty() => match (x.0, &x2.0) {
// var = var op= expr2 -> var op= expr2 // var = var op= expr2 -> var op= expr2
(Expr::Variable(a), Expr::Variable(b)) (Expr::Variable(a), Expr::Variable(b))
if a.1.is_none() && b.1.is_none() && a.0 == b.0 && a.3 == b.3 => 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(); state.set_dirty();
Expr::Assignment(Box::new((Expr::Variable(a), x2.1, optimize_expr(x2.2, state), x.3))) Expr::Assignment(Box::new((Expr::Variable(a), x2.1, optimize_expr(x2.2, state), x.3)))
} }
// id1 = id2 op= expr2 // expr1 = expr2 op= rhs
(id1, id2) => { (expr1, _) => Expr::Assignment(Box::new((expr1, x.1, optimize_expr(Expr::Assignment(x2), state), x.3))),
Expr::Assignment(Box::new((
id1, x.1, Expr::Assignment(Box::new((id2, x2.1, optimize_expr(x2.2, state), x2.3))), x.3,
)))
}
}, },
// id op= expr // expr = rhs
expr => Expr::Assignment(Box::new((x.0, x.1, optimize_expr(expr, state), x.3))), expr => Expr::Assignment(Box::new((x.0, x.1, optimize_expr(expr, state), x.3))),
}, },