Merge pull request #98 from schungx/master
A whole bunch of enhancements and bump to version 0.10.1.
This commit is contained in:
commit
659d1f2583
@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.9.1"
|
version = "0.10.1"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
homepage = "https://github.com/jonathandturner/rhai"
|
homepage = "https://github.com/jonathandturner/rhai"
|
||||||
repository = "https://github.com/jonathandturner/rhai"
|
repository = "https://github.com/jonathandturner/rhai"
|
||||||
|
383
README.md
383
README.md
@ -11,57 +11,76 @@ Rhai's current feature set:
|
|||||||
* Support for overloaded functions
|
* Support for overloaded functions
|
||||||
* No additional dependencies
|
* No additional dependencies
|
||||||
|
|
||||||
**Note:** Currently, the version is 0.9.1, so the language and APIs may change before they stabilize.*
|
**Note:** Currently, the version is 0.10.0-alpha1, so the language and APIs may change before they stabilize.*
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
You can install Rhai using crates by adding this line to your dependences:
|
You can install Rhai using crates by adding this line to your dependencies:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rhai = "0.8.1"
|
rhai = "0.10.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or simply:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
rhai = "*"
|
||||||
|
```
|
||||||
|
|
||||||
|
to use the latest version.
|
||||||
|
|
||||||
|
Beware that in order to use pre-releases (alpha and beta) you need to specify the exact version in your `Cargo.toml`.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
Other cool projects to check out:
|
Other cool projects to check out:
|
||||||
|
|
||||||
* [ChaiScript](http://chaiscript.com/) - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being lead by my cousin.
|
* [ChaiScript](http://chaiscript.com/) - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being lead by my cousin.
|
||||||
* You can also check out the list of [scripting languages for Rust](https://github.com/rust-unofficial/awesome-rust#scripting) on [awesome-rust](https://github.com/rust-unofficial/awesome-rust)
|
* You can also check out the list of [scripting languages for Rust](https://github.com/rust-unofficial/awesome-rust#scripting) on [awesome-rust](https://github.com/rust-unofficial/awesome-rust)
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
The repository contains several examples in the `examples` folder:
|
The repository contains several examples in the `examples` folder:
|
||||||
- `arrays_and_structs` demonstrates registering a new type to Rhai and the usage of arrays on it
|
|
||||||
- `custom_types_and_methods` shows how to register a type and methods for it
|
* `arrays_and_structs` demonstrates registering a new type to Rhai and the usage of arrays on it
|
||||||
- `hello` simple example that evaluates an expression and prints the result
|
* `custom_types_and_methods` shows how to register a type and methods for it
|
||||||
- `reuse_scope` evaluates two pieces of code in separate runs, but using a common scope
|
* `hello` simple example that evaluates an expression and prints the result
|
||||||
- `rhai_runner` runs each filename passed to it as a Rhai script
|
* `reuse_scope` evaluates two pieces of code in separate runs, but using a common scope
|
||||||
- `simple_fn` shows how to register a Rust function to a Rhai engine
|
* `rhai_runner` runs each filename passed to it as a Rhai script
|
||||||
- `repl` a simple REPL, see source code for what it can do at the moment
|
* `simple_fn` shows how to register a Rust function to a Rhai engine
|
||||||
|
* `repl` a simple REPL, see source code for what it can do at the moment
|
||||||
|
|
||||||
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 name
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example Scripts
|
## Example Scripts
|
||||||
|
|
||||||
We also have a few examples scripts that showcase Rhai's features, all stored in the `scripts` folder:
|
We also have a few examples scripts that showcase Rhai's features, all stored in the `scripts` folder:
|
||||||
- `array.rhai` - arrays in Rhai
|
|
||||||
- `assignment.rhai` - variable declarations
|
* `array.rhai` - arrays in Rhai
|
||||||
- `comments.rhai` - just comments
|
* `assignment.rhai` - variable declarations
|
||||||
- `function_decl1.rhai` - a function without parameters
|
* `comments.rhai` - just comments
|
||||||
- `function_decl2.rhai` - a function with two parameters
|
* `for1.rhai` - for loops
|
||||||
- `function_decl3.rhai` - a function with many parameters
|
* `function_decl1.rhai` - a function without parameters
|
||||||
- `if1.rhai` - if example
|
* `function_decl2.rhai` - a function with two parameters
|
||||||
- `loop.rhai` - endless loop in Rhai, this example emulates a do..while cycle
|
* `function_decl3.rhai` - a function with many parameters
|
||||||
- `op1.rhai` - just a simple addition
|
* `if1.rhai` - if example
|
||||||
- `op2.rhai` - simple addition and multiplication
|
* `loop.rhai` - endless loop in Rhai, this example emulates a do..while cycle
|
||||||
- `op3.rhai` - change evaluation order with parenthesis
|
* `op1.rhai` - just a simple addition
|
||||||
- `speed_test.rhai` - a simple program to measure the speed of Rhai's interpreter
|
* `op2.rhai` - simple addition and multiplication
|
||||||
- `string.rhai`- string operations
|
* `op3.rhai` - change evaluation order with parenthesis
|
||||||
- `while.rhai` - while loop
|
* `speed_test.rhai` - a simple program to measure the speed of Rhai's interpreter
|
||||||
|
* `string.rhai`- string operations
|
||||||
|
* `while.rhai` - while loop
|
||||||
|
|
||||||
To run the scripts, you can either make your own tiny program, or make use of the `rhai_runner`
|
To run the scripts, you can either make your own tiny program, or make use of the `rhai_runner`
|
||||||
example program:
|
example program:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run --example rhai_runner scripts/any_script.rhai
|
cargo run --example rhai_runner scripts/any_script.rhai
|
||||||
```
|
```
|
||||||
@ -89,18 +108,70 @@ You can also evaluate a script file:
|
|||||||
if let Ok(result) = engine.eval_file::<i64>("hello_world.rhai") { ... }
|
if let Ok(result) = engine.eval_file::<i64>("hello_world.rhai") { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to repeatedly evaluate a script, you can compile it first into an AST form:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Compile to an AST and store it for later evaluations
|
||||||
|
let ast = Engine::compile("40 + 2").unwrap();
|
||||||
|
|
||||||
|
for _ in 0..42 {
|
||||||
|
if let Ok(result) = engine.eval_ast::<i64>(&ast) {
|
||||||
|
println!("Answer: {}", result); // prints 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiling a script file into AST is also supported:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let ast = Engine::compile_file("hello_world.rhai").unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
# Values and types
|
||||||
|
|
||||||
|
The following primitive types are supported natively:
|
||||||
|
|
||||||
|
* Integer: `i32`, `u32`, `i64` (default), `u64`
|
||||||
|
* Floating-point: `f32`, `f64` (default)
|
||||||
|
* Character: `char`
|
||||||
|
* Boolean: `bool`
|
||||||
|
* Array: `rhai::Array`
|
||||||
|
* Dynamic (i.e. can be anything): `rhai::Dynamic`
|
||||||
|
|
||||||
|
# Value conversions
|
||||||
|
|
||||||
|
All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different; you cannot even add them together.
|
||||||
|
|
||||||
|
There is a `to_float` function to convert a supported number to an `f64`, and a `to_int` function to convert a supported number to `i64` and that's about it. For other conversions you can register your own conversion functions.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 42;
|
||||||
|
let y = x * 100.0; // error: cannot multiply i64 with f64
|
||||||
|
let y = x.to_float() * 100.0; // works
|
||||||
|
let z = y.to_int() + x; // works
|
||||||
|
|
||||||
|
let c = 'X'; // character
|
||||||
|
print("c is '" + c + "' and its code is " + c.to_int());
|
||||||
|
```
|
||||||
|
|
||||||
# Working with functions
|
# Working with functions
|
||||||
|
|
||||||
Rhai's scripting engine is very lightweight. It gets its ability from the functions in your program. To call these functions, you need to register them with the scripting engine.
|
Rhai's scripting engine is very lightweight. It gets its ability from the functions in your program. To call these functions, you need to register them with the scripting engine.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
extern crate rhai;
|
extern crate rhai;
|
||||||
use rhai::{Engine, RegisterFn};
|
use rhai::{Dynamic, Engine, RegisterFn};
|
||||||
|
|
||||||
|
// Normal function
|
||||||
fn add(x: i64, y: i64) -> i64 {
|
fn add(x: i64, y: i64) -> i64 {
|
||||||
x + y
|
x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function that returns a Dynamic value
|
||||||
|
fn get_an_any() -> Dynamic {
|
||||||
|
Box::new(42_i64)
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
@ -109,6 +180,25 @@ fn main() {
|
|||||||
if let Ok(result) = engine.eval::<i64>("add(40, 2)") {
|
if let Ok(result) = engine.eval::<i64>("add(40, 2)") {
|
||||||
println!("Answer: {}", result); // prints 42
|
println!("Answer: {}", result); // prints 42
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functions that return Dynamic values must use register_dynamic_fn()
|
||||||
|
engine.register_dynamic_fn("get_an_any", get_an_any);
|
||||||
|
|
||||||
|
if let Ok(result) = engine.eval::<i64>("get_an_any()") {
|
||||||
|
println!("Answer: {}", result); // prints 42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To return a `Dynamic` value, simply `Box` it and return it.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn decide(yes_no: bool) -> Dynamic {
|
||||||
|
if yes_no {
|
||||||
|
Box::new(42_i64)
|
||||||
|
} else {
|
||||||
|
Box::new("hello world!".to_string()) // remember &str is not supported
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -137,6 +227,19 @@ fn main() {
|
|||||||
|
|
||||||
You can also see in this example how you can register multiple functions (or in this case multiple instances of the same function) to the same name in script. This gives you a way to overload functions and call the correct one, based on the types of the arguments, from your script.
|
You can also see in this example how you can register multiple functions (or in this case multiple instances of the same function) to the same name in script. This gives you a way to overload functions and call the correct one, based on the types of the arguments, from your script.
|
||||||
|
|
||||||
|
# Override built-in functions
|
||||||
|
|
||||||
|
Any similarly-named function defined in a script overrides any built-in function.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Override the built-in function 'to_int'
|
||||||
|
fn to_int(num) {
|
||||||
|
print("Ha! Gotcha!" + num);
|
||||||
|
}
|
||||||
|
|
||||||
|
print(to_int(123)); // what will happen?
|
||||||
|
```
|
||||||
|
|
||||||
# Custom types and methods
|
# Custom types and methods
|
||||||
|
|
||||||
Here's an more complete example of working with Rust. First the example, then we'll break it into parts:
|
Here's an more complete example of working with Rust. First the example, then we'll break it into parts:
|
||||||
@ -184,6 +287,7 @@ struct TestStruct {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Next, we create a few methods that we'll later use in our scripts. Notice that we register our custom type with the engine.
|
Next, we create a few methods that we'll later use in our scripts. Notice that we register our custom type with the engine.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
impl TestStruct {
|
impl TestStruct {
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
@ -210,12 +314,28 @@ engine.register_fn("new_ts", TestStruct::new);
|
|||||||
```
|
```
|
||||||
|
|
||||||
Finally, we call our script. The script can see the function and method we registered earlier. We need to get the result back out from script land just as before, this time casting to our custom struct type.
|
Finally, we call our script. The script can see the function and method we registered earlier. We need to get the result back out from script land just as before, this time casting to our custom struct type.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
if let Ok(result) = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x") {
|
if let Ok(result) = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x") {
|
||||||
println!("result: {}", result.x); // prints 1001
|
println!("result: {}", result.x); // prints 1001
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In fact, any function with a first argument (either by copy or via a `&mut` reference) can be used as a method-call on that type because internally they are the same thing: methods on a type is implemented as a functions taking an first argument.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn foo(ts: &mut TestStruct) -> i64 {
|
||||||
|
ts.x
|
||||||
|
}
|
||||||
|
|
||||||
|
engine.register_fn("foo", foo);
|
||||||
|
|
||||||
|
if let Ok(result) = engine.eval::<i64>("let x = new_ts(); x.foo()") {
|
||||||
|
println!("result: {}", result); // prints 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# Getters and setters
|
# Getters and setters
|
||||||
|
|
||||||
Similarly, you can work with members of your custom types. This works by registering a 'get' or a 'set' function for working with your struct.
|
Similarly, you can work with members of your custom types. This works by registering a 'get' or a 'set' function for working with your struct.
|
||||||
@ -254,6 +374,23 @@ if let Ok(result) = engine.eval::<i64>("let a = new_ts(); a.x = 500; a.x") {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### WARNING: Gotcha's with Getters
|
||||||
|
|
||||||
|
When you _get_ a property, the value is cloned. Any update to it downstream will **NOT** be reflected back to the custom type.
|
||||||
|
|
||||||
|
This can introduce subtle bugs. For example:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn change(s) {
|
||||||
|
s = 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = new_ts();
|
||||||
|
a.x = 500;
|
||||||
|
a.x.change(); // Only a COPY of 'a.x' is changed. 'a.x' is NOT changed.
|
||||||
|
a.x == 500;
|
||||||
|
```
|
||||||
|
|
||||||
# Maintaining state
|
# Maintaining state
|
||||||
|
|
||||||
By default, Rhai treats each engine invocation as a fresh one, persisting only the functions that have been defined but no top-level state. This gives each one a fairly clean starting place. Sometimes, though, you want to continue using the same top-level state from one invocation to the next.
|
By default, Rhai treats each engine invocation as a fresh one, persisting only the functions that have been defined but no top-level state. This gives each one a fairly clean starting place. Sometimes, though, you want to continue using the same top-level state from one invocation to the next.
|
||||||
@ -284,35 +421,64 @@ fn main() {
|
|||||||
let x = 3;
|
let x = 3;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Operators
|
## Numeric operators
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = (1 + 2) * (6 - 4) / 2;
|
let x = (1 + 2) * (6 - 4) / 2;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Comparison operators
|
||||||
|
|
||||||
|
You can compare most values of the same data type. If you compare two values of _different_ data types, the result is always `false`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
42 == 42; // true
|
||||||
|
42 > 42; // false
|
||||||
|
"hello" > "foo"; // true
|
||||||
|
"42" == 42; // false
|
||||||
|
42 == 42.0; // false - i64 is different from f64
|
||||||
|
```
|
||||||
|
|
||||||
|
## Boolean operators
|
||||||
|
|
||||||
|
Double boolean operators `&&` and `||` _short-circuit_, meaning that the second operand will not be evaluated if the first one already proves the condition wrong.
|
||||||
|
|
||||||
|
Single boolean operators `&` and `|` always evaluate both operands.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
this() || that(); // that() is not evaluated if this() is true
|
||||||
|
this() && that(); // that() is not evaluated if this() is false
|
||||||
|
|
||||||
|
this() | that(); // both this() and that() are evaluated
|
||||||
|
this() & that(); // both this() and that() are evaluated
|
||||||
|
```
|
||||||
|
|
||||||
## If
|
## If
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
if true {
|
if true {
|
||||||
print("it's true!");
|
print("It's true!");
|
||||||
}
|
} else if true {
|
||||||
else {
|
print("It's true again!");
|
||||||
|
} else {
|
||||||
print("It's false!");
|
print("It's false!");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## While
|
## While
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = 10;
|
let x = 10;
|
||||||
|
|
||||||
while x > 0 {
|
while x > 0 {
|
||||||
print(x);
|
print(x);
|
||||||
if x == 5 {
|
if x == 5 { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
x = x - 1;
|
x = x - 1;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Loop
|
## Loop
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = 10;
|
let x = 10;
|
||||||
|
|
||||||
@ -344,15 +510,83 @@ fn add(x, y) {
|
|||||||
|
|
||||||
print(add(2, 3))
|
print(add(2, 3))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Arrays
|
## Arrays
|
||||||
|
|
||||||
You can create arrays of values, and then access them with numeric indices.
|
You can create arrays of values, and then access them with numeric indices.
|
||||||
|
|
||||||
```rust
|
The following standard functions operate on arrays:
|
||||||
let y = [1, 2, 3];
|
|
||||||
y[1] = 5;
|
|
||||||
|
|
||||||
print(y[1]);
|
* `push` - inserts an element at the end
|
||||||
|
* `pop` - removes the last element and returns it (() if empty)
|
||||||
|
* `shift` - removes the first element and returns it (() if empty)
|
||||||
|
* `len` - returns the number of elements
|
||||||
|
* `pad` - pads the array with an element until a specified length
|
||||||
|
* `clear` - empties the array
|
||||||
|
* `truncate` - cuts off the array at exactly a specified length (discarding all subsequent elements)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let y = [1, 2, 3]; // 3 elements
|
||||||
|
y[1] = 42;
|
||||||
|
|
||||||
|
print(y[1]); // prints 42
|
||||||
|
|
||||||
|
let foo = [1, 2, 3][0]; // a syntax error for now - cannot index into literals
|
||||||
|
let foo = ts.list[0]; // a syntax error for now - cannot index into properties
|
||||||
|
let foo = y[0]; // this works
|
||||||
|
|
||||||
|
y.push(4); // 4 elements
|
||||||
|
y.push(5); // 5 elements
|
||||||
|
|
||||||
|
print(y.len()); // prints 5
|
||||||
|
|
||||||
|
let first = y.shift(); // remove the first element, 4 elements remaining
|
||||||
|
first == 1;
|
||||||
|
|
||||||
|
let last = y.pop(); // remove the last element, 3 elements remaining
|
||||||
|
last == 5;
|
||||||
|
|
||||||
|
print(y.len()); // prints 3
|
||||||
|
|
||||||
|
y.pad(10, "hello"); // pad the array up to 10 elements
|
||||||
|
|
||||||
|
print(y.len()); // prints 10
|
||||||
|
|
||||||
|
y.truncate(5); // truncate the array to 5 elements
|
||||||
|
|
||||||
|
print(y.len()); // prints 5
|
||||||
|
|
||||||
|
y.clear(); // empty the array
|
||||||
|
|
||||||
|
print(y.len()); // prints 0
|
||||||
|
```
|
||||||
|
|
||||||
|
`push` and `pad` are only defined for standard built-in types. If you want to use them with
|
||||||
|
your own custom type, you need to define a specific override:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
engine.register_fn("push",
|
||||||
|
|list: &mut Array, item: MyType| list.push(Box::new(item))
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
The type of a Rhai array is `rhai::Array`.
|
||||||
|
|
||||||
|
## For loops
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let array = [1, 3, 5, 7, 9, 42];
|
||||||
|
|
||||||
|
for x in array {
|
||||||
|
print(x);
|
||||||
|
if x == 42 { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// The range function allows iterating from first..last-1
|
||||||
|
for x in range(0,50) {
|
||||||
|
print(x);
|
||||||
|
if x == 42 { break; }
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Members and methods
|
## Members and methods
|
||||||
@ -368,6 +602,80 @@ a.update();
|
|||||||
```rust
|
```rust
|
||||||
let name = "Bob";
|
let name = "Bob";
|
||||||
let middle_initial = 'C';
|
let middle_initial = 'C';
|
||||||
|
let last = 'Davis';
|
||||||
|
|
||||||
|
let full_name = name + " " + middle_initial + ". " + last;
|
||||||
|
full_name == "Bob C. Davis";
|
||||||
|
|
||||||
|
// String building with different types
|
||||||
|
let age = 42;
|
||||||
|
let record = full_name + ": age " + age;
|
||||||
|
record == "Bob C. Davis: age 42";
|
||||||
|
|
||||||
|
// Strings can be indexed to get a character
|
||||||
|
let c = record[4];
|
||||||
|
c == 'C';
|
||||||
|
|
||||||
|
let c = "foo"[0]; // a syntax error for now - cannot index into literals
|
||||||
|
let c = ts.s[0]; // a syntax error for now - cannot index into properties
|
||||||
|
let c = record[0]; // this works
|
||||||
|
|
||||||
|
// Unlike Rust, Rhai strings can be modified
|
||||||
|
record[4] = 'Z';
|
||||||
|
record == "Bob Z. Davis: age 42";
|
||||||
|
```
|
||||||
|
|
||||||
|
The following standard functions operate on strings:
|
||||||
|
|
||||||
|
* `len` - returns the number of characters (not number of bytes) in the string
|
||||||
|
* `pad` - pads the string with an character until a specified number of characters
|
||||||
|
* `clear` - empties the string
|
||||||
|
* `truncate` - cuts off the string at exactly a specified number of characters
|
||||||
|
* `contains` - checks if a certain character or sub-string occurs in the string
|
||||||
|
* `replace` - replaces a substring with another
|
||||||
|
* `trim` - trims the string
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let full_name == " Bob C. Davis ";
|
||||||
|
full_name.len() == 14;
|
||||||
|
|
||||||
|
full_name.trim();
|
||||||
|
full_name.len() == 12;
|
||||||
|
|
||||||
|
full_name.pad(15, '$');
|
||||||
|
full_name.len() == 15;
|
||||||
|
full_name == "Bob C. Davis$$$";
|
||||||
|
|
||||||
|
full_name.truncate(6);
|
||||||
|
full_name.len() == 6;
|
||||||
|
full_name == "Bob C.";
|
||||||
|
|
||||||
|
full_name.replace("Bob", "John");
|
||||||
|
full_name.len() == 7;
|
||||||
|
full_name = "John C.";
|
||||||
|
|
||||||
|
full_name.contains('C') == true;
|
||||||
|
full_name.contains("John") == true;
|
||||||
|
|
||||||
|
full_name.clear();
|
||||||
|
full_name.len() == 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Print and Debug
|
||||||
|
|
||||||
|
```rust
|
||||||
|
print("hello"); // prints hello to stdout
|
||||||
|
print(1 + 2 + 3); // prints 6 to stdout
|
||||||
|
print("hello" + 42); // prints hello42 to stdout
|
||||||
|
debug("world!"); // prints "world!" to stdout using debug formatting
|
||||||
|
```
|
||||||
|
|
||||||
|
### Overriding Print and Debug with Callback functions
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Any function that takes a &str argument can be used to override print and debug
|
||||||
|
engine.on_print(|x: &str| println!("hello: {}", x));
|
||||||
|
engine.on_debug(|x: &str| println!("DEBUG: {}", x));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Comments
|
## Comments
|
||||||
@ -412,6 +720,7 @@ The `+=` operator can also be used to build strings:
|
|||||||
```rust
|
```rust
|
||||||
let my_str = "abc";
|
let my_str = "abc";
|
||||||
my_str += "ABC";
|
my_str += "ABC";
|
||||||
|
my_str += 12345;
|
||||||
|
|
||||||
my_str == "abcABC"
|
my_str == "abcABC12345"
|
||||||
```
|
```
|
||||||
|
4
TODO
4
TODO
@ -1,11 +1,7 @@
|
|||||||
pre 1.0:
|
pre 1.0:
|
||||||
- binary ops
|
|
||||||
- basic threads
|
- basic threads
|
||||||
- stdlib
|
- stdlib
|
||||||
- floats
|
|
||||||
- REPL (consume functions)
|
|
||||||
1.0:
|
1.0:
|
||||||
- decide on postfix/prefix operators
|
- decide on postfix/prefix operators
|
||||||
- ranges, rustic for-loop
|
|
||||||
- advanced threads + actors
|
- advanced threads + actors
|
||||||
- more literals
|
- more literals
|
||||||
|
@ -1,34 +1,19 @@
|
|||||||
use rhai::{Engine, RegisterFn, Scope};
|
use rhai::{Engine, RegisterFn, Scope};
|
||||||
use std::fmt::Display;
|
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
fn showit<T: Display>(x: &mut T) -> () {
|
|
||||||
println!("{}", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn quit() {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut i32) -> ());
|
engine.register_fn("exit", || exit(0));
|
||||||
engine.register_fn("print", showit as fn(x: &mut i64) -> ());
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut u32) -> ());
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut u64) -> ());
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut f32) -> ());
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut f64) -> ());
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut bool) -> ());
|
|
||||||
engine.register_fn("print", showit as fn(x: &mut String) -> ());
|
|
||||||
engine.register_fn("exit", quit);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
print!("> ");
|
print!("> ");
|
||||||
|
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
stdout().flush().expect("couldn't flush stdout");
|
stdout().flush().expect("couldn't flush stdout");
|
||||||
|
|
||||||
if let Err(e) = stdin().read_line(&mut input) {
|
if let Err(e) = stdin().read_line(&mut input) {
|
||||||
println!("input error: {}", e);
|
println!("input error: {}", e);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
use rhai::{Engine, RegisterFn};
|
use rhai::{Engine, RegisterFn};
|
||||||
|
|
||||||
fn add(x: i64, y: i64) -> i64 {
|
|
||||||
x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
fn add(x: i64, y: i64) -> i64 {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
|
||||||
engine.register_fn("add", add);
|
engine.register_fn("add", add);
|
||||||
|
|
||||||
if let Ok(result) = engine.eval::<i64>("add(40, 2)") {
|
if let Ok(result) = engine.eval::<i64>("add(40, 2)") {
|
||||||
println!("Answer: {}", result); // prints 42
|
println!("Answer: {}", result); // prints 42
|
||||||
}
|
}
|
||||||
|
29
src/any.rs
29
src/any.rs
@ -1,12 +1,15 @@
|
|||||||
use std::any::{type_name, Any as StdAny, TypeId};
|
use std::any::{type_name, Any as StdAny, TypeId};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
pub type Variant = dyn Any;
|
||||||
|
pub type Dynamic = Box<Variant>;
|
||||||
|
|
||||||
pub trait Any: StdAny {
|
pub trait Any: StdAny {
|
||||||
fn type_id(&self) -> TypeId;
|
fn type_id(&self) -> TypeId;
|
||||||
|
|
||||||
fn type_name(&self) -> String;
|
fn type_name(&self) -> String;
|
||||||
|
|
||||||
fn box_clone(&self) -> Box<dyn Any>;
|
fn into_dynamic(&self) -> Dynamic;
|
||||||
|
|
||||||
/// This type may only be implemented by `rhai`.
|
/// This type may only be implemented by `rhai`.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -27,7 +30,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn box_clone(&self) -> Box<dyn Any> {
|
fn into_dynamic(&self) -> Dynamic {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,15 +39,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Any {
|
impl Variant {
|
||||||
//#[inline]
|
//#[inline]
|
||||||
// fn box_clone(&self) -> Box<dyn Any> {
|
// fn into_dynamic(&self) -> Box<Variant> {
|
||||||
// Any::box_clone(self)
|
// Any::into_dynamic(self)
|
||||||
// }
|
// }
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is<T: Any>(&self) -> bool {
|
pub fn is<T: Any>(&self) -> bool {
|
||||||
let t = TypeId::of::<T>();
|
let t = TypeId::of::<T>();
|
||||||
let boxed = <dyn Any as Any>::type_id(self);
|
let boxed = <Variant as Any>::type_id(self);
|
||||||
|
|
||||||
t == boxed
|
t == boxed
|
||||||
}
|
}
|
||||||
@ -52,7 +55,7 @@ impl dyn Any {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
||||||
if self.is::<T>() {
|
if self.is::<T>() {
|
||||||
unsafe { Some(&*(self as *const dyn Any as *const T)) }
|
unsafe { Some(&*(self as *const Variant as *const T)) }
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -61,20 +64,20 @@ impl dyn Any {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
|
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
|
||||||
if self.is::<T>() {
|
if self.is::<T>() {
|
||||||
unsafe { Some(&mut *(self as *mut dyn Any as *mut T)) }
|
unsafe { Some(&mut *(self as *mut Variant as *mut T)) }
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Box<dyn Any> {
|
impl Clone for Dynamic {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Any::box_clone(self.as_ref() as &dyn Any)
|
Any::into_dynamic(self.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for dyn Any {
|
impl fmt::Debug for Variant {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.pad("?")
|
f.pad("?")
|
||||||
}
|
}
|
||||||
@ -84,11 +87,11 @@ pub trait AnyExt: Sized {
|
|||||||
fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self>;
|
fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnyExt for Box<dyn Any> {
|
impl AnyExt for Dynamic {
|
||||||
fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self> {
|
fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self> {
|
||||||
if self.is::<T>() {
|
if self.is::<T>() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let raw: *mut dyn Any = Box::into_raw(self);
|
let raw: *mut Variant = Box::into_raw(self);
|
||||||
Ok(Box::from_raw(raw as *mut T))
|
Ok(Box::from_raw(raw as *mut T))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
176
src/api.rs
Normal file
176
src/api.rs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
use crate::any::{Any, AnyExt, Dynamic};
|
||||||
|
use crate::engine::{Engine, EvalAltResult, FnIntExt, FnSpec, Scope};
|
||||||
|
use crate::parser::{lex, parse, ParseError, Position, AST};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
impl Engine {
|
||||||
|
/// Compile a string into an AST
|
||||||
|
pub fn compile(input: &str) -> Result<AST, ParseError> {
|
||||||
|
let tokens = lex(input);
|
||||||
|
parse(&mut tokens.peekable())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile a file into an AST
|
||||||
|
pub fn compile_file(filename: &str) -> Result<AST, EvalAltResult> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut f = File::open(filename)
|
||||||
|
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))?;
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
|
||||||
|
f.read_to_string(&mut contents)
|
||||||
|
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))
|
||||||
|
.and_then(|_| Self::compile(&contents).map_err(EvalAltResult::ErrorParsing))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a file
|
||||||
|
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut f = File::open(filename)
|
||||||
|
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))?;
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
|
||||||
|
f.read_to_string(&mut contents)
|
||||||
|
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))
|
||||||
|
.and_then(|_| self.eval::<T>(&contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string
|
||||||
|
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
self.eval_with_scope(&mut scope, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string with own scope
|
||||||
|
pub fn eval_with_scope<T: Any + Clone>(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
input: &str,
|
||||||
|
) -> Result<T, EvalAltResult> {
|
||||||
|
let ast = Self::compile(input).map_err(EvalAltResult::ErrorParsing)?;
|
||||||
|
self.eval_ast_with_scope(scope, &ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate an AST
|
||||||
|
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
self.eval_ast_with_scope(&mut scope, ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate an AST with own scope
|
||||||
|
pub fn eval_ast_with_scope<T: Any + Clone>(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
ast: &AST,
|
||||||
|
) -> Result<T, EvalAltResult> {
|
||||||
|
let AST(os, fns) = ast;
|
||||||
|
|
||||||
|
fns.iter().for_each(|f| {
|
||||||
|
self.script_fns.insert(
|
||||||
|
FnSpec {
|
||||||
|
ident: f.name.clone(),
|
||||||
|
args: None,
|
||||||
|
},
|
||||||
|
Arc::new(FnIntExt::Int(f.clone())),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = os
|
||||||
|
.iter()
|
||||||
|
.try_fold(Box::new(()) as Dynamic, |_, o| self.eval_stmt(scope, o));
|
||||||
|
|
||||||
|
self.script_fns.clear(); // Clean up engine
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Err(EvalAltResult::Return(out, pos)) => Ok(*out.downcast::<T>().map_err(|a| {
|
||||||
|
let name = self.map_type_name((*a).type_name());
|
||||||
|
EvalAltResult::ErrorMismatchOutputType(name, pos)
|
||||||
|
})?),
|
||||||
|
|
||||||
|
Ok(out) => Ok(*out.downcast::<T>().map_err(|a| {
|
||||||
|
let name = self.map_type_name((*a).type_name());
|
||||||
|
EvalAltResult::ErrorMismatchOutputType(name, Position::eof())
|
||||||
|
})?),
|
||||||
|
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a file, but only return errors, if there are any.
|
||||||
|
/// Useful for when you don't need the result, but still need
|
||||||
|
/// to keep track of possible errors
|
||||||
|
pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut f = File::open(filename)
|
||||||
|
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))?;
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
|
||||||
|
f.read_to_string(&mut contents)
|
||||||
|
.map_err(|err| EvalAltResult::ErrorCantOpenScriptFile(filename.into(), err))
|
||||||
|
.and_then(|_| self.consume(&contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string, but only return errors, if there are any.
|
||||||
|
/// Useful for when you don't need the result, but still need
|
||||||
|
/// to keep track of possible errors
|
||||||
|
pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> {
|
||||||
|
self.consume_with_scope(&mut Scope::new(), input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string with own scope, but only return errors, if there are any.
|
||||||
|
/// Useful for when you don't need the result, but still need
|
||||||
|
/// to keep track of possible errors
|
||||||
|
pub fn consume_with_scope(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
input: &str,
|
||||||
|
) -> Result<(), EvalAltResult> {
|
||||||
|
let tokens = lex(input);
|
||||||
|
|
||||||
|
parse(&mut tokens.peekable())
|
||||||
|
.map_err(|err| EvalAltResult::ErrorParsing(err))
|
||||||
|
.and_then(|AST(ref os, ref fns)| {
|
||||||
|
for f in fns {
|
||||||
|
// FIX - Why are functions limited to 6 parameters?
|
||||||
|
if f.params.len() > 6 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.script_fns.insert(
|
||||||
|
FnSpec {
|
||||||
|
ident: f.name.clone(),
|
||||||
|
args: None,
|
||||||
|
},
|
||||||
|
Arc::new(FnIntExt::Int(f.clone())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = os
|
||||||
|
.iter()
|
||||||
|
.try_fold(Box::new(()) as Dynamic, |_, o| self.eval_stmt(scope, o))
|
||||||
|
.map(|_| ());
|
||||||
|
|
||||||
|
self.script_fns.clear(); // Clean up engine
|
||||||
|
|
||||||
|
val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides `on_print`
|
||||||
|
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
|
||||||
|
self.on_print = Box::new(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides `on_debug`
|
||||||
|
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
|
||||||
|
self.on_debug = Box::new(callback);
|
||||||
|
}
|
||||||
|
}
|
336
src/builtin.rs
Normal file
336
src/builtin.rs
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
use crate::{any::Any, Array, Dynamic, Engine, RegisterDynamicFn, RegisterFn};
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Shl, Shr, Sub};
|
||||||
|
|
||||||
|
macro_rules! reg_op {
|
||||||
|
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(x: $y, y: $y)->$y);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_un {
|
||||||
|
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(x: $y)->$y);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_cmp {
|
||||||
|
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(x: $y, y: $y)->bool);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_func1 {
|
||||||
|
($self:expr, $x:expr, $op:expr, $r:ty, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(x: $y)->$r);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_func2x {
|
||||||
|
($self:expr, $x:expr, $op:expr, $v:ty, $r:ty, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(x: $v, y: $y)->$r);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_func2y {
|
||||||
|
($self:expr, $x:expr, $op:expr, $v:ty, $r:ty, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(y: $y, x: $v)->$r);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_func3 {
|
||||||
|
($self:expr, $x:expr, $op:expr, $v:ty, $w:ty, $r:ty, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(x: $v, y: $w, z: $y)->$r);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Engine {
|
||||||
|
/// Register the built-in library.
|
||||||
|
pub(crate) fn register_builtins(&mut self) {
|
||||||
|
fn add<T: Add>(x: T, y: T) -> <T as Add>::Output {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
fn sub<T: Sub>(x: T, y: T) -> <T as Sub>::Output {
|
||||||
|
x - y
|
||||||
|
}
|
||||||
|
fn mul<T: Mul>(x: T, y: T) -> <T as Mul>::Output {
|
||||||
|
x * y
|
||||||
|
}
|
||||||
|
fn div<T: Div>(x: T, y: T) -> <T as Div>::Output {
|
||||||
|
x / y
|
||||||
|
}
|
||||||
|
fn neg<T: Neg>(x: T) -> <T as Neg>::Output {
|
||||||
|
-x
|
||||||
|
}
|
||||||
|
fn lt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x < y
|
||||||
|
}
|
||||||
|
fn lte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x <= y
|
||||||
|
}
|
||||||
|
fn gt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x > y
|
||||||
|
}
|
||||||
|
fn gte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x >= y
|
||||||
|
}
|
||||||
|
fn eq<T: PartialEq>(x: T, y: T) -> bool {
|
||||||
|
x == y
|
||||||
|
}
|
||||||
|
fn ne<T: PartialEq>(x: T, y: T) -> bool {
|
||||||
|
x != y
|
||||||
|
}
|
||||||
|
fn and(x: bool, y: bool) -> bool {
|
||||||
|
x && y
|
||||||
|
}
|
||||||
|
fn or(x: bool, y: bool) -> bool {
|
||||||
|
x || y
|
||||||
|
}
|
||||||
|
fn not(x: bool) -> bool {
|
||||||
|
!x
|
||||||
|
}
|
||||||
|
fn concat(x: String, y: String) -> String {
|
||||||
|
x + &y
|
||||||
|
}
|
||||||
|
fn binary_and<T: BitAnd>(x: T, y: T) -> <T as BitAnd>::Output {
|
||||||
|
x & y
|
||||||
|
}
|
||||||
|
fn binary_or<T: BitOr>(x: T, y: T) -> <T as BitOr>::Output {
|
||||||
|
x | y
|
||||||
|
}
|
||||||
|
fn binary_xor<T: BitXor>(x: T, y: T) -> <T as BitXor>::Output {
|
||||||
|
x ^ y
|
||||||
|
}
|
||||||
|
fn left_shift<T: Shl<T>>(x: T, y: T) -> <T as Shl<T>>::Output {
|
||||||
|
x.shl(y)
|
||||||
|
}
|
||||||
|
fn right_shift<T: Shr<T>>(x: T, y: T) -> <T as Shr<T>>::Output {
|
||||||
|
x.shr(y)
|
||||||
|
}
|
||||||
|
fn modulo<T: Rem<T>>(x: T, y: T) -> <T as Rem<T>>::Output {
|
||||||
|
x % y
|
||||||
|
}
|
||||||
|
fn pow_i64_i64(x: i64, y: i64) -> i64 {
|
||||||
|
x.pow(y as u32)
|
||||||
|
}
|
||||||
|
fn pow_f64_f64(x: f64, y: f64) -> f64 {
|
||||||
|
x.powf(y)
|
||||||
|
}
|
||||||
|
fn pow_f64_i64(x: f64, y: i64) -> f64 {
|
||||||
|
x.powi(y as i32)
|
||||||
|
}
|
||||||
|
fn unit_eq(_a: (), _b: ()) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_op!(self, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64);
|
||||||
|
reg_op!(self, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64);
|
||||||
|
reg_op!(self, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64);
|
||||||
|
reg_op!(self, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64);
|
||||||
|
|
||||||
|
reg_cmp!(self, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char);
|
||||||
|
reg_cmp!(self, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char);
|
||||||
|
reg_cmp!(self, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char);
|
||||||
|
reg_cmp!(self, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char);
|
||||||
|
reg_cmp!(
|
||||||
|
self, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, bool, f32, f64, String, char
|
||||||
|
);
|
||||||
|
reg_cmp!(
|
||||||
|
self, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, bool, f32, f64, String, char
|
||||||
|
);
|
||||||
|
|
||||||
|
//reg_op!(self, "||", or, bool);
|
||||||
|
//reg_op!(self, "&&", and, bool);
|
||||||
|
reg_op!(self, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64);
|
||||||
|
reg_op!(self, "|", or, bool);
|
||||||
|
reg_op!(self, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64);
|
||||||
|
reg_op!(self, "&", and, bool);
|
||||||
|
reg_op!(self, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64);
|
||||||
|
reg_op!(self, "<<", left_shift, i8, u8, i16, u16, i32, i64, u32, u64);
|
||||||
|
reg_op!(self, ">>", right_shift, i8, u8, i16, u16);
|
||||||
|
reg_op!(self, ">>", right_shift, i32, i64, u32, u64);
|
||||||
|
reg_op!(self, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64);
|
||||||
|
self.register_fn("~", pow_i64_i64);
|
||||||
|
self.register_fn("~", pow_f64_f64);
|
||||||
|
self.register_fn("~", pow_f64_i64);
|
||||||
|
|
||||||
|
reg_un!(self, "-", neg, i8, i16, i32, i64, f32, f64);
|
||||||
|
reg_un!(self, "!", not, bool);
|
||||||
|
|
||||||
|
self.register_fn("+", concat);
|
||||||
|
self.register_fn("==", unit_eq);
|
||||||
|
|
||||||
|
// self.register_fn("[]", idx);
|
||||||
|
// FIXME? Registering array lookups are a special case because we want to return boxes
|
||||||
|
// directly let ent = self.fns.entry("[]".to_string()).or_insert_with(Vec::new);
|
||||||
|
// (*ent).push(FnType::ExternalFn2(Box::new(idx)));
|
||||||
|
|
||||||
|
// Register conversion functions
|
||||||
|
self.register_fn("to_float", |x: i8| x as f64);
|
||||||
|
self.register_fn("to_float", |x: u8| x as f64);
|
||||||
|
self.register_fn("to_float", |x: i16| x as f64);
|
||||||
|
self.register_fn("to_float", |x: u16| x as f64);
|
||||||
|
self.register_fn("to_float", |x: i32| x as f64);
|
||||||
|
self.register_fn("to_float", |x: u32| x as f64);
|
||||||
|
self.register_fn("to_float", |x: i64| x as f64);
|
||||||
|
self.register_fn("to_float", |x: u64| x as f64);
|
||||||
|
self.register_fn("to_float", |x: f32| x as f64);
|
||||||
|
|
||||||
|
self.register_fn("to_int", |x: i8| x as i64);
|
||||||
|
self.register_fn("to_int", |x: u8| x as i64);
|
||||||
|
self.register_fn("to_int", |x: i16| x as i64);
|
||||||
|
self.register_fn("to_int", |x: u16| x as i64);
|
||||||
|
self.register_fn("to_int", |x: i32| x as i64);
|
||||||
|
self.register_fn("to_int", |x: u32| x as i64);
|
||||||
|
self.register_fn("to_int", |x: u64| x as i64);
|
||||||
|
self.register_fn("to_int", |x: f32| x as i64);
|
||||||
|
self.register_fn("to_int", |x: f64| x as i64);
|
||||||
|
|
||||||
|
self.register_fn("to_int", |ch: char| ch as i64);
|
||||||
|
|
||||||
|
// Register print and debug
|
||||||
|
fn print_debug<T: Debug>(x: T) -> String {
|
||||||
|
format!("{:?}", x)
|
||||||
|
}
|
||||||
|
fn print<T: Display>(x: T) -> String {
|
||||||
|
format!("{}", x)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_func1!(self, "print", print, String, i8, u8, i16, u16);
|
||||||
|
reg_func1!(self, "print", print, String, i32, i64, u32, u64);
|
||||||
|
reg_func1!(self, "print", print, String, f32, f64, bool, char, String);
|
||||||
|
reg_func1!(self, "print", print_debug, String, Array);
|
||||||
|
self.register_fn("print", || "".to_string());
|
||||||
|
self.register_fn("print", |_: ()| "".to_string());
|
||||||
|
|
||||||
|
reg_func1!(self, "debug", print_debug, String, i8, u8, i16, u16);
|
||||||
|
reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64);
|
||||||
|
reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char);
|
||||||
|
reg_func1!(self, "debug", print_debug, String, String, Array, ());
|
||||||
|
|
||||||
|
// Register array utility functions
|
||||||
|
fn push<T: Any>(list: &mut Array, item: T) {
|
||||||
|
list.push(Box::new(item));
|
||||||
|
}
|
||||||
|
fn pad<T: Any + Clone>(list: &mut Array, len: i64, item: T) {
|
||||||
|
if len >= 0 {
|
||||||
|
while list.len() < len as usize {
|
||||||
|
push(list, item.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_func2x!(self, "push", push, &mut Array, (), i8, u8, i16, u16);
|
||||||
|
reg_func2x!(self, "push", push, &mut Array, (), i32, i64, u32, u64);
|
||||||
|
reg_func2x!(self, "push", push, &mut Array, (), f32, f64, bool, char);
|
||||||
|
reg_func2x!(self, "push", push, &mut Array, (), String, Array, ());
|
||||||
|
reg_func3!(self, "pad", pad, &mut Array, i64, (), i8, u8, i16, u16);
|
||||||
|
reg_func3!(self, "pad", pad, &mut Array, i64, (), i32, u32, f32);
|
||||||
|
reg_func3!(self, "pad", pad, &mut Array, i64, (), i64, u64, f64);
|
||||||
|
reg_func3!(self, "pad", pad, &mut Array, i64, (), bool, char);
|
||||||
|
reg_func3!(self, "pad", pad, &mut Array, i64, (), String, Array, ());
|
||||||
|
|
||||||
|
self.register_dynamic_fn("pop", |list: &mut Array| list.pop().unwrap_or(Box::new(())));
|
||||||
|
self.register_dynamic_fn("shift", |list: &mut Array| {
|
||||||
|
if list.len() > 0 {
|
||||||
|
list.remove(0)
|
||||||
|
} else {
|
||||||
|
Box::new(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.register_fn("len", |list: &mut Array| list.len() as i64);
|
||||||
|
self.register_fn("clear", |list: &mut Array| list.clear());
|
||||||
|
self.register_fn("truncate", |list: &mut Array, len: i64| {
|
||||||
|
if len >= 0 {
|
||||||
|
list.truncate(len as usize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register string concatenate functions
|
||||||
|
fn prepend<T: Display>(x: T, y: String) -> String {
|
||||||
|
format!("{}{}", x, y)
|
||||||
|
}
|
||||||
|
fn append<T: Display>(x: String, y: T) -> String {
|
||||||
|
format!("{}{}", x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_func2x!(
|
||||||
|
self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
|
||||||
|
bool, char
|
||||||
|
);
|
||||||
|
self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y));
|
||||||
|
self.register_fn("+", |x: String, _: ()| format!("{}", x));
|
||||||
|
|
||||||
|
reg_func2y!(
|
||||||
|
self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64,
|
||||||
|
bool, char
|
||||||
|
);
|
||||||
|
self.register_fn("+", |x: Array, y: String| format!("{:?}{}", x, y));
|
||||||
|
self.register_fn("+", |_: (), y: String| format!("{}", y));
|
||||||
|
|
||||||
|
// Register string utility functions
|
||||||
|
self.register_fn("len", |s: &mut String| s.chars().count() as i64);
|
||||||
|
self.register_fn("contains", |s: &mut String, ch: char| s.contains(ch));
|
||||||
|
self.register_fn("contains", |s: &mut String, find: String| s.contains(&find));
|
||||||
|
self.register_fn("clear", |s: &mut String| s.clear());
|
||||||
|
self.register_fn("truncate", |s: &mut String, len: i64| {
|
||||||
|
if len >= 0 {
|
||||||
|
let chars: Vec<_> = s.chars().take(len as usize).collect();
|
||||||
|
s.clear();
|
||||||
|
chars.iter().for_each(|&ch| s.push(ch));
|
||||||
|
} else {
|
||||||
|
s.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.register_fn("pad", |s: &mut String, len: i64, ch: char| {
|
||||||
|
for _ in 0..s.chars().count() - len as usize {
|
||||||
|
s.push(ch);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.register_fn("replace", |s: &mut String, find: String, sub: String| {
|
||||||
|
let new_str = s.replace(&find, &sub);
|
||||||
|
s.clear();
|
||||||
|
s.push_str(&new_str);
|
||||||
|
});
|
||||||
|
self.register_fn("trim", |s: &mut String| {
|
||||||
|
let trimmed = s.trim();
|
||||||
|
|
||||||
|
if trimmed.len() < s.len() {
|
||||||
|
let chars: Vec<_> = trimmed.chars().collect();
|
||||||
|
s.clear();
|
||||||
|
chars.iter().for_each(|&ch| s.push(ch));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register array iterator
|
||||||
|
self.register_iterator::<Array, _>(|a| {
|
||||||
|
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register range function
|
||||||
|
self.register_iterator::<Range<i64>, _>(|a| {
|
||||||
|
Box::new(
|
||||||
|
a.downcast_ref::<Range<i64>>()
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.map(|n| Box::new(n) as Dynamic),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
self.register_fn("range", |i1: i64, i2: i64| (i1..i2));
|
||||||
|
}
|
||||||
|
}
|
12
src/call.rs
12
src/call.rs
@ -1,24 +1,22 @@
|
|||||||
//! Helper module which defines `FnArgs`
|
//! Helper module which defines `FnArgs`
|
||||||
//! to make function calling easier.
|
//! to make function calling easier.
|
||||||
|
|
||||||
use crate::any::Any;
|
use crate::any::{Any, Variant};
|
||||||
|
|
||||||
pub trait FunArgs<'a> {
|
pub trait FunArgs<'a> {
|
||||||
fn into_vec(self) -> Vec<&'a mut dyn Any>;
|
fn into_vec(self) -> Vec<&'a mut Variant>;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_args {
|
macro_rules! impl_args {
|
||||||
($($p:ident),*) => {
|
($($p:ident),*) => {
|
||||||
impl<'a, $($p),*> FunArgs<'a> for ($(&'a mut $p,)*)
|
impl<'a, $($p: Any + Clone),*> FunArgs<'a> for ($(&'a mut $p,)*)
|
||||||
where
|
|
||||||
$($p: Any + Clone),*
|
|
||||||
{
|
{
|
||||||
fn into_vec(self) -> Vec<&'a mut dyn Any> {
|
fn into_vec(self) -> Vec<&'a mut Variant> {
|
||||||
let ($($p,)*) = self;
|
let ($($p,)*) = self;
|
||||||
|
|
||||||
#[allow(unused_variables, unused_mut)]
|
#[allow(unused_variables, unused_mut)]
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
$(v.push($p as &mut dyn Any);)*
|
$(v.push($p as &mut Variant);)*
|
||||||
|
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
1359
src/engine.rs
1359
src/engine.rs
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,14 @@
|
|||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
use crate::any::Any;
|
use crate::any::{Any, Dynamic};
|
||||||
use crate::engine::{Engine, EvalAltResult};
|
use crate::engine::{Engine, EvalAltResult, FnCallArgs};
|
||||||
|
use crate::parser::Position;
|
||||||
|
|
||||||
pub trait RegisterFn<FN, ARGS, RET> {
|
pub trait RegisterFn<FN, ARGS, RET> {
|
||||||
fn register_fn(&mut self, name: &str, f: FN);
|
fn register_fn(&mut self, name: &str, f: FN);
|
||||||
}
|
}
|
||||||
pub trait RegisterBoxFn<FN, ARGS> {
|
pub trait RegisterDynamicFn<FN, ARGS> {
|
||||||
fn register_box_fn(&mut self, name: &str, f: FN);
|
fn register_dynamic_fn(&mut self, name: &str, f: FN);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Ref<A>(A);
|
pub struct Ref<A>(A);
|
||||||
@ -23,63 +24,69 @@ macro_rules! def_register {
|
|||||||
def_register!(imp);
|
def_register!(imp);
|
||||||
};
|
};
|
||||||
(imp $($par:ident => $mark:ty => $param:ty => $clone:expr),*) => {
|
(imp $($par:ident => $mark:ty => $param:ty => $clone:expr),*) => {
|
||||||
impl<$($par,)* FN, RET> RegisterFn<FN, ($($mark,)*), RET> for Engine
|
impl<
|
||||||
where
|
|
||||||
$($par: Any + Clone,)*
|
$($par: Any + Clone,)*
|
||||||
FN: Fn($($param),*) -> RET + 'static,
|
FN: Fn($($param),*) -> RET + 'static,
|
||||||
RET: Any,
|
RET: Any
|
||||||
|
> RegisterFn<FN, ($($mark,)*), RET> for Engine
|
||||||
{
|
{
|
||||||
fn register_fn(&mut self, name: &str, f: FN) {
|
fn register_fn(&mut self, name: &str, f: FN) {
|
||||||
let fun = move |mut args: Vec<&mut dyn Any>| {
|
let fn_name = name.to_string();
|
||||||
|
|
||||||
|
let fun = move |mut args: FnCallArgs, pos: Position| {
|
||||||
// Check for length at the beginning to avoid
|
// Check for length at the beginning to avoid
|
||||||
// per-element bound checks.
|
// per-element bound checks.
|
||||||
if args.len() != count_args!($($par)*) {
|
const NUM_ARGS: usize = count_args!($($par)*);
|
||||||
return Err(EvalAltResult::ErrorFunctionArgMismatch);
|
|
||||||
|
if args.len() != NUM_ARGS {
|
||||||
|
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables, unused_mut)]
|
#[allow(unused_variables, unused_mut)]
|
||||||
let mut drain = args.drain(..);
|
let mut drain = args.drain(..);
|
||||||
$(
|
$(
|
||||||
// Downcast every element, return in case of a type mismatch
|
// Downcast every element, return in case of a type mismatch
|
||||||
let $par = ((*drain.next().unwrap()).downcast_mut() as Option<&mut $par>)
|
let $par = ((*drain.next().unwrap()).downcast_mut() as Option<&mut $par>).unwrap();
|
||||||
.ok_or(EvalAltResult::ErrorFunctionArgMismatch)?;
|
|
||||||
)*
|
)*
|
||||||
|
|
||||||
// Call the user-supplied function using ($clone) to
|
// Call the user-supplied function using ($clone) to
|
||||||
// potentially clone the value, otherwise pass the reference.
|
// potentially clone the value, otherwise pass the reference.
|
||||||
let r = f($(($clone)($par)),*);
|
let r = f($(($clone)($par)),*);
|
||||||
Ok(Box::new(r) as Box<dyn Any>)
|
Ok(Box::new(r) as Dynamic)
|
||||||
};
|
};
|
||||||
self.register_fn_raw(name.to_owned(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
self.register_fn_raw(name.into(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<$($par,)* FN> RegisterBoxFn<FN, ($($mark,)*)> for Engine
|
impl<
|
||||||
where
|
|
||||||
$($par: Any + Clone,)*
|
$($par: Any + Clone,)*
|
||||||
FN: Fn($($param),*) -> Box<dyn Any> + 'static
|
FN: Fn($($param),*) -> Dynamic + 'static,
|
||||||
|
> RegisterDynamicFn<FN, ($($mark,)*)> for Engine
|
||||||
{
|
{
|
||||||
fn register_box_fn(&mut self, name: &str, f: FN) {
|
fn register_dynamic_fn(&mut self, name: &str, f: FN) {
|
||||||
let fun = move |mut args: Vec<&mut dyn Any>| {
|
let fn_name = name.to_string();
|
||||||
|
|
||||||
|
let fun = move |mut args: FnCallArgs, pos: Position| {
|
||||||
// Check for length at the beginning to avoid
|
// Check for length at the beginning to avoid
|
||||||
// per-element bound checks.
|
// per-element bound checks.
|
||||||
if args.len() != count_args!($($par)*) {
|
const NUM_ARGS: usize = count_args!($($par)*);
|
||||||
return Err(EvalAltResult::ErrorFunctionArgMismatch);
|
|
||||||
|
if args.len() != NUM_ARGS {
|
||||||
|
return Err(EvalAltResult::ErrorFunctionArgsMismatch(fn_name.clone(), NUM_ARGS, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables, unused_mut)]
|
#[allow(unused_variables, unused_mut)]
|
||||||
let mut drain = args.drain(..);
|
let mut drain = args.drain(..);
|
||||||
$(
|
$(
|
||||||
// Downcast every element, return in case of a type mismatch
|
// Downcast every element, return in case of a type mismatch
|
||||||
let $par = ((*drain.next().unwrap()).downcast_mut() as Option<&mut $par>)
|
let $par = ((*drain.next().unwrap()).downcast_mut() as Option<&mut $par>).unwrap();
|
||||||
.ok_or(EvalAltResult::ErrorFunctionArgMismatch)?;
|
|
||||||
)*
|
)*
|
||||||
|
|
||||||
// Call the user-supplied function using ($clone) to
|
// Call the user-supplied function using ($clone) to
|
||||||
// potentially clone the value, otherwise pass the reference.
|
// potentially clone the value, otherwise pass the reference.
|
||||||
Ok(f($(($clone)($par)),*))
|
Ok(f($(($clone)($par)),*))
|
||||||
};
|
};
|
||||||
self.register_fn_raw(name.to_owned(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
self.register_fn_raw(name.into(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/lib.rs
10
src/lib.rs
@ -25,7 +25,7 @@
|
|||||||
//!
|
//!
|
||||||
//! let mut engine = Engine::new();
|
//! let mut engine = Engine::new();
|
||||||
//! engine.register_fn("compute_something", compute_something);
|
//! engine.register_fn("compute_something", compute_something);
|
||||||
//! assert_eq!(engine.eval_file::<bool>("my_script.rhai"), Ok(true));
|
//! assert_eq!(engine.eval_file::<bool>("my_script.rhai").unwrap(), true);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [Check out the README on GitHub for more information!](https://github.com/jonathandturner/rhai)
|
//! [Check out the README on GitHub for more information!](https://github.com/jonathandturner/rhai)
|
||||||
@ -40,12 +40,14 @@ macro_rules! debug_println {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod any;
|
mod any;
|
||||||
|
mod api;
|
||||||
|
mod builtin;
|
||||||
mod call;
|
mod call;
|
||||||
mod engine;
|
mod engine;
|
||||||
mod fn_register;
|
mod fn_register;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
pub use any::Any;
|
pub use any::Dynamic;
|
||||||
pub use engine::{Engine, EvalAltResult, Scope};
|
pub use engine::{Array, Engine, EvalAltResult, Scope};
|
||||||
pub use fn_register::{RegisterBoxFn, RegisterFn};
|
pub use fn_register::{RegisterDynamicFn, RegisterFn};
|
||||||
pub use parser::{ParseError, ParseErrorType, AST};
|
pub use parser::{ParseError, ParseErrorType, AST};
|
||||||
|
656
src/parser.rs
656
src/parser.rs
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,17 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult, RegisterFn};
|
||||||
use rhai::RegisterFn;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_arrays() {
|
fn test_arrays() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = [1, 2, 3]; x[1]"), Ok(2));
|
assert_eq!(engine.eval::<i64>("let x = [1, 2, 3]; x[1]")?, 2);
|
||||||
assert_eq!(
|
assert_eq!(engine.eval::<i64>("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5);
|
||||||
engine.eval::<i64>("let y = [1, 2, 3]; y[1] = 5; y[1]"),
|
|
||||||
Ok(5)
|
Ok(())
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_with_structs() {
|
fn test_array_with_structs() -> Result<(), EvalAltResult> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: i64,
|
x: i64,
|
||||||
@ -45,7 +43,7 @@ fn test_array_with_structs() {
|
|||||||
engine.register_fn("update", TestStruct::update);
|
engine.register_fn("update", TestStruct::update);
|
||||||
engine.register_fn("new_ts", TestStruct::new);
|
engine.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let a = [new_ts()]; a[0].x"), Ok(1));
|
assert_eq!(engine.eval::<i64>("let a = [new_ts()]; a[0].x")?, 1);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<i64>(
|
engine.eval::<i64>(
|
||||||
@ -53,7 +51,9 @@ fn test_array_with_structs() {
|
|||||||
a[0].x = 100; \
|
a[0].x = 100; \
|
||||||
a[0].update(); \
|
a[0].update(); \
|
||||||
a[0].x",
|
a[0].x",
|
||||||
),
|
)?,
|
||||||
Ok(1100)
|
1100
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_binary_ops() {
|
fn test_binary_ops() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("10 % 4"), Ok(2));
|
assert_eq!(engine.eval::<i64>("10 % 4")?, 2);
|
||||||
assert_eq!(engine.eval::<i64>("10 << 4"), Ok(160));
|
assert_eq!(engine.eval::<i64>("10 << 4")?, 160);
|
||||||
assert_eq!(engine.eval::<i64>("10 >> 4"), Ok(0));
|
assert_eq!(engine.eval::<i64>("10 >> 4")?, 0);
|
||||||
assert_eq!(engine.eval::<i64>("10 & 4"), Ok(0));
|
assert_eq!(engine.eval::<i64>("10 & 4")?, 0);
|
||||||
assert_eq!(engine.eval::<i64>("10 | 4"), Ok(14));
|
assert_eq!(engine.eval::<i64>("10 | 4")?, 14);
|
||||||
assert_eq!(engine.eval::<i64>("10 ^ 4"), Ok(14));
|
assert_eq!(engine.eval::<i64>("10 ^ 4")?, 14);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<bool>("42 == 42")?, true);
|
||||||
|
assert_eq!(engine.eval::<bool>("42 > 42")?, false);
|
||||||
|
|
||||||
|
// Incompatible types compare to false
|
||||||
|
assert_eq!(engine.eval::<bool>("true == 42")?, false);
|
||||||
|
assert_eq!(engine.eval::<bool>(r#""42" == 42"#)?, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_left_shift() {
|
fn test_left_shift() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("4 << 2")?, 16);
|
||||||
assert_eq!(engine.eval::<i64>("4 << 2"), Ok(16));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_right_shift() {
|
fn test_right_shift() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("9 >> 1")?, 4);
|
||||||
assert_eq!(engine.eval::<i64>("9 >> 1"), Ok(4));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,104 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bool_op1() {
|
fn test_bool_op1() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<bool>("true && (false || true)"), Ok(true));
|
assert_eq!(engine.eval::<bool>("true && (false || true)")?, true);
|
||||||
|
assert_eq!(engine.eval::<bool>("true & (false | true)")?, true);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bool_op2() {
|
fn test_bool_op2() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<bool>("false && (false || true)"), Ok(false));
|
assert_eq!(engine.eval::<bool>("false && (false || true)")?, false);
|
||||||
|
assert_eq!(engine.eval::<bool>("false & (false | true)")?, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bool_op3() -> Result<(), EvalAltResult> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>("true && (false || 123)").is_err());
|
||||||
|
assert_eq!(engine.eval::<bool>("true && (true || 123)")?, true);
|
||||||
|
assert!(engine.eval::<bool>("123 && (false || true)").is_err());
|
||||||
|
assert_eq!(engine.eval::<bool>("false && (true || 123)")?, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<bool>(
|
||||||
|
r"
|
||||||
|
fn this() { true }
|
||||||
|
fn that() { 9/0 }
|
||||||
|
|
||||||
|
this() || that();
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<bool>(
|
||||||
|
r"
|
||||||
|
fn this() { false }
|
||||||
|
fn that() { 9/0 }
|
||||||
|
|
||||||
|
this() && that();
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_bool_op_no_short_circuit1() {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine
|
||||||
|
.eval::<bool>(
|
||||||
|
r"
|
||||||
|
fn this() { false }
|
||||||
|
fn that() { 9/0 }
|
||||||
|
|
||||||
|
this() | that();
|
||||||
|
"
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_bool_op_no_short_circuit2() {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine
|
||||||
|
.eval::<bool>(
|
||||||
|
r"
|
||||||
|
fn this() { false }
|
||||||
|
fn that() { 9/0 }
|
||||||
|
|
||||||
|
this() & that();
|
||||||
|
"
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_chars() {
|
fn test_chars() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<char>("'y'"), Ok('y'));
|
assert_eq!(engine.eval::<char>("'y'")?, 'y');
|
||||||
assert_eq!(engine.eval::<char>("'\\u2764'"), Ok('❤'));
|
assert_eq!(engine.eval::<char>("'\\u2764'")?, '❤');
|
||||||
|
assert_eq!(engine.eval::<char>(r#"let x="hello"; x[2]"#)?, 'l');
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(r#"let x="hello"; x[2]='$'; x"#)?,
|
||||||
|
"he$lo".to_string()
|
||||||
|
);
|
||||||
|
|
||||||
match engine.eval::<char>("''") {
|
assert!(engine.eval::<char>("'\\uhello'").is_err());
|
||||||
Err(_) => (),
|
assert!(engine.eval::<char>("''").is_err());
|
||||||
_ => assert!(false),
|
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,68 +1,66 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_or_equals() {
|
fn test_or_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 16; x |= 74; x"), Ok(90));
|
assert_eq!(engine.eval::<i64>("let x = 16; x |= 74; x")?, 90);
|
||||||
assert_eq!(engine.eval::<bool>("let x = true; x |= false; x"), Ok(true));
|
assert_eq!(engine.eval::<bool>("let x = true; x |= false; x")?, true);
|
||||||
assert_eq!(engine.eval::<bool>("let x = false; x |= true; x"), Ok(true));
|
assert_eq!(engine.eval::<bool>("let x = false; x |= true; x")?, true);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_and_equals() {
|
fn test_and_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 16; x &= 31; x"), Ok(16));
|
assert_eq!(engine.eval::<i64>("let x = 16; x &= 31; x")?, 16);
|
||||||
assert_eq!(
|
assert_eq!(engine.eval::<bool>("let x = true; x &= false; x")?, false);
|
||||||
engine.eval::<bool>("let x = true; x &= false; x"),
|
assert_eq!(engine.eval::<bool>("let x = false; x &= true; x")?, false);
|
||||||
Ok(false)
|
assert_eq!(engine.eval::<bool>("let x = true; x &= true; x")?, true);
|
||||||
);
|
|
||||||
assert_eq!(
|
Ok(())
|
||||||
engine.eval::<bool>("let x = false; x &= true; x"),
|
|
||||||
Ok(false)
|
|
||||||
);
|
|
||||||
assert_eq!(engine.eval::<bool>("let x = true; x &= true; x"), Ok(true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_xor_equals() {
|
fn test_xor_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("let x = 90; x ^= 12; x")?, 86);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 90; x ^= 12; x"), Ok(86));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multiply_equals() {
|
fn test_multiply_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("let x = 2; x *= 3; x")?, 6);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 2; x *= 3; x"), Ok(6));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_divide_equals() {
|
fn test_divide_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("let x = 6; x /= 2; x")?, 3);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 6; x /= 2; x"), Ok(3));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_left_shift_equals() {
|
fn test_left_shift_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("let x = 9; x >>=1; x")?, 4);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 9; x >>=1; x"), Ok(4));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_right_shift_equals() {
|
fn test_right_shift_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("let x = 4; x<<= 2; x")?, 16);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 4; x<<= 2; x"), Ok(16));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_modulo_equals() {
|
fn test_modulo_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<i64>("let x = 10; x %= 4; x")?, 2);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 10; x %= 4; x"), Ok(2));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
use rhai::EvalAltResult;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decrement() {
|
fn test_decrement() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 10; x -= 7; x"), Ok(3));
|
assert_eq!(engine.eval::<i64>("let x = 10; x -= 7; x")?, 3);
|
||||||
|
|
||||||
assert_eq!(
|
let r = engine.eval::<String>("let s = \"test\"; s -= \"ing\"; s");
|
||||||
engine.eval::<String>("let s = \"test\"; s -= \"ing\"; s"),
|
|
||||||
Err(EvalAltResult::ErrorFunctionNotFound(
|
match r {
|
||||||
"- (alloc::string::String, alloc::string::String)".to_string()
|
Err(EvalAltResult::ErrorFunctionNotFound(err, _)) if err == "- (string, string)" => (),
|
||||||
))
|
_ => panic!(),
|
||||||
);
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult, RegisterFn};
|
||||||
use rhai::RegisterFn;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_float() {
|
fn test_float() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<bool>("let x = 0.0; let y = 1.0; x < y"),
|
engine.eval::<bool>("let x = 0.0; let y = 1.0; x < y")?,
|
||||||
Ok(true)
|
true
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<bool>("let x = 0.0; let y = 1.0; x > y"),
|
engine.eval::<bool>("let x = 0.0; let y = 1.0; x > y")?,
|
||||||
Ok(false)
|
false
|
||||||
);
|
);
|
||||||
assert_eq!(engine.eval::<f64>("let x = 9.9999; x"), Ok(9.9999));
|
assert_eq!(engine.eval::<f64>("let x = 9.9999; x")?, 9.9999);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn struct_with_float() {
|
fn struct_with_float() -> Result<(), EvalAltResult> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: f64,
|
x: f64,
|
||||||
@ -50,11 +51,13 @@ fn struct_with_float() {
|
|||||||
engine.register_fn("new_ts", TestStruct::new);
|
engine.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<f64>("let ts = new_ts(); ts.update(); ts.x"),
|
engine.eval::<f64>("let ts = new_ts(); ts.update(); ts.x")?,
|
||||||
Ok(6.789)
|
6.789
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<f64>("let ts = new_ts(); ts.x = 10.1001; ts.x"),
|
engine.eval::<f64>("let ts = new_ts(); ts.x = 10.1001; ts.x")?,
|
||||||
Ok(10.1001)
|
10.1001
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_for() {
|
fn test_for() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
let script = r"
|
let script = r"
|
||||||
@ -20,5 +20,7 @@ fn test_for() {
|
|||||||
sum1 + sum2
|
sum1 + sum2
|
||||||
";
|
";
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>(script).unwrap(), 30);
|
assert_eq!(engine.eval::<i64>(script)?, 30);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult, RegisterFn};
|
||||||
use rhai::RegisterFn;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_set() {
|
fn test_get_set() -> Result<(), EvalAltResult> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: i64,
|
x: i64,
|
||||||
@ -29,14 +28,13 @@ fn test_get_set() {
|
|||||||
engine.register_get_set("x", TestStruct::get_x, TestStruct::set_x);
|
engine.register_get_set("x", TestStruct::get_x, TestStruct::set_x);
|
||||||
engine.register_fn("new_ts", TestStruct::new);
|
engine.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(engine.eval::<i64>("let a = new_ts(); a.x = 500; a.x")?, 500);
|
||||||
engine.eval::<i64>("let a = new_ts(); a.x = 500; a.x"),
|
|
||||||
Ok(500)
|
Ok(())
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_big_get_set() {
|
fn test_big_get_set() -> Result<(), EvalAltResult> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestChild {
|
struct TestChild {
|
||||||
x: i64,
|
x: i64,
|
||||||
@ -88,7 +86,9 @@ fn test_big_get_set() {
|
|||||||
engine.register_fn("new_tp", TestParent::new);
|
engine.register_fn("new_tp", TestParent::new);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<i64>("let a = new_tp(); a.child.x = 500; a.child.x"),
|
engine.eval::<i64>("let a = new_tp(); a.child.x = 500; a.child.x")?,
|
||||||
Ok(500)
|
500
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,29 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if() {
|
fn test_if() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("if true { 55 }"), Ok(55));
|
assert_eq!(engine.eval::<i64>("if true { 55 }")?, 55);
|
||||||
assert_eq!(engine.eval::<i64>("if false { 55 } else { 44 }"), Ok(44));
|
assert_eq!(engine.eval::<i64>("if false { 55 } else { 44 }")?, 44);
|
||||||
assert_eq!(engine.eval::<i64>("if true { 55 } else { 44 }"), Ok(55));
|
assert_eq!(engine.eval::<i64>("if true { 55 } else { 44 }")?, 55);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<i64>("if false { 55 } else if true { 33 } else { 44 }")?,
|
||||||
|
33
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<i64>(
|
||||||
|
r"
|
||||||
|
if false { 55 }
|
||||||
|
else if false { 33 }
|
||||||
|
else if false { 66 }
|
||||||
|
else if false { 77 }
|
||||||
|
else if false { 88 }
|
||||||
|
else { 44 }
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
44
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_increment() {
|
fn test_increment() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 1; x += 2; x"), Ok(3));
|
assert_eq!(engine.eval::<i64>("let x = 1; x += 2; x")?, 3);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>("let s = \"test\"; s += \"ing\"; s"),
|
engine.eval::<String>("let s = \"test\"; s += \"ing\"; s")?,
|
||||||
Ok("testing".to_string())
|
"testing".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,30 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_internal_fn() {
|
fn test_internal_fn() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(engine.eval::<i64>("fn addme(a, b) { a+b } addme(3, 4)")?, 7);
|
||||||
engine.eval::<i64>("fn addme(a, b) { a+b } addme(3, 4)"),
|
assert_eq!(engine.eval::<i64>("fn bob() { return 4; 5 } bob()")?, 4);
|
||||||
Ok(7)
|
|
||||||
);
|
Ok(())
|
||||||
assert_eq!(engine.eval::<i64>("fn bob() { return 4; 5 } bob()"), Ok(4));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_big_internal_fn() {
|
fn test_big_internal_fn() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<i64>(
|
engine.eval::<i64>(
|
||||||
"fn mathme(a, b, c, d, e, f) { a - b * c + d * e - f \
|
r"
|
||||||
} mathme(100, 5, 2, 9, 6, 32)",
|
fn mathme(a, b, c, d, e, f) {
|
||||||
),
|
a - b * c + d * e - f
|
||||||
Ok(112)
|
}
|
||||||
|
mathme(100, 5, 2, 9, 6, 32)
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
112
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_loop() {
|
fn test_loop() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert!(engine
|
assert!(engine.eval::<bool>(
|
||||||
.eval::<bool>(
|
r"
|
||||||
"
|
|
||||||
let x = 0;
|
let x = 0;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
@ -22,6 +21,7 @@ fn test_loop() {
|
|||||||
|
|
||||||
x == 45
|
x == 45
|
||||||
"
|
"
|
||||||
)
|
)?);
|
||||||
.unwrap())
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult, RegisterFn};
|
||||||
use rhai::RegisterFn;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_method_call() {
|
fn test_method_call() -> Result<(), EvalAltResult> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: i64,
|
x: i64,
|
||||||
@ -25,9 +24,9 @@ fn test_method_call() {
|
|||||||
engine.register_fn("update", TestStruct::update);
|
engine.register_fn("update", TestStruct::update);
|
||||||
engine.register_fn("new_ts", TestStruct::new);
|
engine.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
if let Ok(result) = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x") {
|
let ts = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
||||||
assert_eq!(result.x, 1001);
|
|
||||||
} else {
|
assert_eq!(ts.x, 1001);
|
||||||
assert!(false);
|
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ use rhai::{Engine, EvalAltResult, RegisterFn};
|
|||||||
fn test_mismatched_op() {
|
fn test_mismatched_op() {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
let r = engine.eval::<i64>("60 + \"hello\"");
|
||||||
engine.eval::<i64>("60 + \"hello\""),
|
|
||||||
Err(EvalAltResult::ErrorMismatchOutputType(
|
match r {
|
||||||
"alloc::string::String".into()
|
Err(EvalAltResult::ErrorMismatchOutputType(err, _)) if err == "string" => (),
|
||||||
))
|
_ => panic!(),
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -29,10 +29,14 @@ fn test_mismatched_op_custom_type() {
|
|||||||
engine.register_type::<TestStruct>();
|
engine.register_type::<TestStruct>();
|
||||||
engine.register_fn("new_ts", TestStruct::new);
|
engine.register_fn("new_ts", TestStruct::new);
|
||||||
|
|
||||||
assert_eq!(
|
let r = engine.eval::<i64>("60 + new_ts()");
|
||||||
engine.eval::<i64>("60 + new_ts()"),
|
|
||||||
Err(EvalAltResult::ErrorFunctionNotFound(
|
match r {
|
||||||
"+ (i64, mismatched_op::test_mismatched_op_custom_type::TestStruct)".into()
|
Err(EvalAltResult::ErrorFunctionNotFound(err, _))
|
||||||
))
|
if err == "+ (i64, mismatched_op::test_mismatched_op_custom_type::TestStruct)" =>
|
||||||
);
|
{
|
||||||
|
()
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
14
tests/not.rs
14
tests/not.rs
@ -1,16 +1,18 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_not() {
|
fn test_not() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<bool>("let not_true = !true; not_true"),
|
engine.eval::<bool>("let not_true = !true; not_true")?,
|
||||||
Ok(false)
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<bool>("fn not(x) { !x } not(false)"), Ok(true));
|
assert_eq!(engine.eval::<bool>("fn not(x) { !x } not(false)")?, true);
|
||||||
|
|
||||||
// TODO - do we allow stacking unary operators directly? e.g '!!!!!!!true'
|
// TODO - do we allow stacking unary operators directly? e.g '!!!!!!!true'
|
||||||
assert_eq!(engine.eval::<bool>("!(!(!(!(true))))"), Ok(true));
|
assert_eq!(engine.eval::<bool>("!(!(!(!(true))))")?, true);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,43 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_number_literal() {
|
fn test_number_literal() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("65"), Ok(65));
|
assert_eq!(engine.eval::<i64>("65")?, 65);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hex_literal() {
|
fn test_hex_literal() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 0xf; x"), Ok(15));
|
assert_eq!(engine.eval::<i64>("let x = 0xf; x")?, 15);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 0xff; x"), Ok(255));
|
assert_eq!(engine.eval::<i64>("let x = 0xff; x")?, 255);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_octal_literal() {
|
fn test_octal_literal() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 0o77; x"), Ok(63));
|
assert_eq!(engine.eval::<i64>("let x = 0o77; x")?, 63);
|
||||||
assert_eq!(engine.eval::<i64>("let x = 0o1234; x"), Ok(668));
|
assert_eq!(engine.eval::<i64>("let x = 0o1234; x")?, 668);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_binary_literal() {
|
fn test_binary_literal() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 0b1111; x"), Ok(15));
|
assert_eq!(engine.eval::<i64>("let x = 0b1111; x")?, 15);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<i64>("let x = 0b0011_1100_1010_0101; x"),
|
engine.eval::<i64>("let x = 0b0011_1100_1010_0101; x")?,
|
||||||
Ok(15525)
|
15525
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
18
tests/ops.rs
18
tests/ops.rs
@ -1,19 +1,23 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ops() {
|
fn test_ops() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("60 + 5"), Ok(65));
|
assert_eq!(engine.eval::<i64>("60 + 5")?, 65);
|
||||||
assert_eq!(engine.eval::<i64>("(1 + 2) * (6 - 4) / 2"), Ok(3));
|
assert_eq!(engine.eval::<i64>("(1 + 2) * (6 - 4) / 2")?, 3);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_op_prec() {
|
fn test_op_prec() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<i64>("let x = 0; if x == 10 || true { x = 1} x"),
|
engine.eval::<i64>("let x = 0; if x == 10 || true { x = 1} x")?,
|
||||||
Ok(1)
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,34 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_power_of() {
|
fn test_power_of() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("2 ~ 3"), Ok(8));
|
assert_eq!(engine.eval::<i64>("2 ~ 3")?, 8);
|
||||||
assert_eq!(engine.eval::<i64>("(-2 ~ 3)"), Ok(-8));
|
assert_eq!(engine.eval::<i64>("(-2 ~ 3)")?, -8);
|
||||||
assert_eq!(engine.eval::<f64>("2.2 ~ 3.3"), Ok(13.489468760533386_f64));
|
assert_eq!(engine.eval::<f64>("2.2 ~ 3.3")?, 13.489468760533386_f64);
|
||||||
assert_eq!(engine.eval::<f64>("2.0~-2.0"), Ok(0.25_f64));
|
assert_eq!(engine.eval::<f64>("2.0~-2.0")?, 0.25_f64);
|
||||||
assert_eq!(engine.eval::<f64>("(-2.0~-2.0)"), Ok(0.25_f64));
|
assert_eq!(engine.eval::<f64>("(-2.0~-2.0)")?, 0.25_f64);
|
||||||
assert_eq!(engine.eval::<f64>("(-2.0~-2)"), Ok(0.25_f64));
|
assert_eq!(engine.eval::<f64>("(-2.0~-2)")?, 0.25_f64);
|
||||||
assert_eq!(engine.eval::<i64>("4~3"), Ok(64));
|
assert_eq!(engine.eval::<i64>("4~3")?, 64);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_power_of_equals() {
|
fn test_power_of_equals() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = 2; x ~= 3; x"), Ok(8));
|
assert_eq!(engine.eval::<i64>("let x = 2; x ~= 3; x")?, 8);
|
||||||
assert_eq!(engine.eval::<i64>("let x = -2; x ~= 3; x"), Ok(-8));
|
assert_eq!(engine.eval::<i64>("let x = -2; x ~= 3; x")?, -8);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<f64>("let x = 2.2; x ~= 3.3; x"),
|
engine.eval::<f64>("let x = 2.2; x ~= 3.3; x")?,
|
||||||
Ok(13.489468760533386_f64)
|
13.489468760533386_f64
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(engine.eval::<f64>("let x = 2.0; x ~= -2.0; x")?, 0.25_f64);
|
||||||
engine.eval::<f64>("let x = 2.0; x ~= -2.0; x"),
|
assert_eq!(engine.eval::<f64>("let x = -2.0; x ~= -2.0; x")?, 0.25_f64);
|
||||||
Ok(0.25_f64)
|
assert_eq!(engine.eval::<f64>("let x = -2.0; x ~= -2; x")?, 0.25_f64);
|
||||||
);
|
assert_eq!(engine.eval::<i64>("let x =4; x ~= 3; x")?, 64);
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<f64>("let x = -2.0; x ~= -2.0; x"),
|
Ok(())
|
||||||
Ok(0.25_f64)
|
|
||||||
);
|
|
||||||
assert_eq!(engine.eval::<f64>("let x = -2.0; x ~= -2; x"), Ok(0.25_f64));
|
|
||||||
assert_eq!(engine.eval::<i64>("let x =4; x ~= 3; x"), Ok(64));
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string() {
|
fn test_string() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>("\"Test string: \\u2764\""),
|
engine.eval::<String>("\"Test string: \\u2764\"")?,
|
||||||
Ok("Test string: ❤".to_string())
|
"Test string: ❤".to_string()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>("\"foo\" + \"bar\""),
|
engine.eval::<String>("\"foo\" + \"bar\"")?,
|
||||||
Ok("foobar".to_string())
|
"foobar".to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// TODO also add test case for unary after compound
|
// TODO also add test case for unary after compound
|
||||||
// Hah, turns out unary + has a good use after all!
|
// Hah, turns out unary + has a good use after all!
|
||||||
fn test_unary_after_binary() {
|
fn test_unary_after_binary() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("10 % +4"), Ok(2));
|
assert_eq!(engine.eval::<i64>("10 % +4")?, 2);
|
||||||
assert_eq!(engine.eval::<i64>("10 << +4"), Ok(160));
|
assert_eq!(engine.eval::<i64>("10 << +4")?, 160);
|
||||||
assert_eq!(engine.eval::<i64>("10 >> +4"), Ok(0));
|
assert_eq!(engine.eval::<i64>("10 >> +4")?, 0);
|
||||||
assert_eq!(engine.eval::<i64>("10 & +4"), Ok(0));
|
assert_eq!(engine.eval::<i64>("10 & +4")?, 0);
|
||||||
assert_eq!(engine.eval::<i64>("10 | +4"), Ok(14));
|
assert_eq!(engine.eval::<i64>("10 | +4")?, 14);
|
||||||
assert_eq!(engine.eval::<i64>("10 ^ +4"), Ok(14));
|
assert_eq!(engine.eval::<i64>("10 ^ +4")?, 14);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unary_minus() {
|
fn test_unary_minus() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<i64>("let x = -5; x"), Ok(-5));
|
assert_eq!(engine.eval::<i64>("let x = -5; x")?, -5);
|
||||||
assert_eq!(engine.eval::<i64>("fn n(x) { -x } n(5)"), Ok(-5));
|
assert_eq!(engine.eval::<i64>("fn n(x) { -x } n(5)")?, -5);
|
||||||
assert_eq!(engine.eval::<i64>("5 - -(-5)"), Ok(0));
|
assert_eq!(engine.eval::<i64>("5 - -(-5)")?, 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,22 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unit() {
|
fn test_unit() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<()>("let x = (); x")?, ());
|
||||||
assert_eq!(engine.eval::<()>("let x = (); x"), Ok(()));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unit_eq() {
|
fn test_unit_eq() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<bool>("let x = (); let y = (); x == y")?, true);
|
||||||
assert_eq!(
|
Ok(())
|
||||||
engine.eval::<bool>("let x = (); let y = (); x == y"),
|
|
||||||
Ok(true)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unit_with_spaces() {
|
fn test_unit_with_spaces() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
assert_eq!(engine.eval::<()>("let x = ( ); x")?, ());
|
||||||
assert_eq!(engine.eval::<()>("let x = ( ); x"), Ok(()));
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,16 @@
|
|||||||
use rhai::{Engine, Scope};
|
use rhai::{Engine, EvalAltResult, Scope};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_var_scope() {
|
fn test_var_scope() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
assert_eq!(
|
engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5")?;
|
||||||
engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5"),
|
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x")?, 9);
|
||||||
Ok(())
|
engine.eval_with_scope::<()>(&mut scope, "x = x + 1; x = x + 2;")?;
|
||||||
);
|
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x")?, 12);
|
||||||
|
assert_eq!(engine.eval_with_scope::<()>(&mut scope, "{let x = 3}")?, ());
|
||||||
|
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x")?, 12);
|
||||||
|
|
||||||
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x"), Ok(9));
|
Ok(())
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval_with_scope::<()>(&mut scope, "x = x + 1; x = x + 2;"),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x"), Ok(12));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval_with_scope::<()>(&mut scope, "{let x = 3}"),
|
|
||||||
Ok(())
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x"), Ok(12));
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use rhai::Engine;
|
use rhai::{Engine, EvalAltResult};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_while() {
|
fn test_while() -> Result<(), EvalAltResult> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<i64>(
|
engine.eval::<i64>(
|
||||||
"let x = 0; while x < 10 { x = x + 1; if x > 5 { \
|
"let x = 0; while x < 10 { x = x + 1; if x > 5 { \
|
||||||
break } } x",
|
break } } x",
|
||||||
),
|
)?,
|
||||||
Ok(6)
|
6
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user