diff --git a/README.md b/README.md index a127d857..f894ea7b 100644 --- a/README.md +++ b/README.md @@ -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 | -| [`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 | +| 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`](#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 | +| [`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> let engine = Engine::new(); let result = engine.eval::("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::(&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}; diff --git a/benches/eval_array.rs b/benches/eval_array.rs index d97e1164..0689bf4f 100644 --- a/benches/eval_array.rs +++ b/benches/eval_array.rs @@ -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()); +} diff --git a/scripts/for2.rhai b/scripts/for2.rhai new file mode 100644 index 00000000..8547e7d5 --- /dev/null +++ b/scripts/for2.rhai @@ -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."); diff --git a/scripts/strings_map.rhai b/scripts/strings_map.rhai new file mode 100644 index 00000000..04cd81db --- /dev/null +++ b/scripts/strings_map.rhai @@ -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."); diff --git a/src/optimize.rs b/src/optimize.rs index 20ccfa3c..78613ace 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -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))), },