Add no_function
feature to disable script-defined functions.
This commit is contained in:
parent
047f064cd1
commit
7c4d22d98a
18
Cargo.toml
18
Cargo.toml
@ -18,16 +18,18 @@ include = [
|
||||
num-traits = "*"
|
||||
|
||||
[features]
|
||||
#default = ["no_index", "no_float", "only_i32", "no_stdlib", "unchecked"]
|
||||
#default = ["no_function", "no_index", "no_float", "only_i32", "no_stdlib", "unchecked"]
|
||||
default = []
|
||||
debug_msgs = []
|
||||
unchecked = []
|
||||
no_stdlib = []
|
||||
no_index = []
|
||||
no_float = []
|
||||
only_i32 = []
|
||||
only_i64 = []
|
||||
debug_msgs = [] # print debug messages on function registrations and calls
|
||||
unchecked = [] # unchecked arithmetic
|
||||
no_stdlib = [] # no standard library of utility functions
|
||||
no_index = [] # no arrays and indexing
|
||||
no_float = [] # no floating-point
|
||||
no_function = [] # no script-defined functions
|
||||
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
||||
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
#opt-level = "z" # optimize for size
|
||||
|
67
README.md
67
README.md
@ -10,7 +10,7 @@ Rhai's current feature set:
|
||||
* Low compile-time overhead (~0.6 sec debug/~3 sec release for script runner app)
|
||||
* Easy-to-use language similar to JS+Rust
|
||||
* Support for overloaded functions
|
||||
* Very few additional dependencies (right now only `num-traits` to do checked arithmetic operations)
|
||||
* Very few additional dependencies (right now only [`num-traits`] to do checked arithmetic operations)
|
||||
|
||||
**Note:** Currently, the version is 0.10.2, so the language and API's may change before they stabilize.
|
||||
|
||||
@ -39,10 +39,11 @@ Optional features
|
||||
-----------------
|
||||
|
||||
| Feature | Description |
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `debug_msgs` | Print debug messages to stdout related to function registrations and calls. |
|
||||
| `no_stdlib` | Exclude the standard library of utility functions in the build, and only include the minimum necessary functionalities. Standard types are not affected. |
|
||||
| `unchecked` | Exclude arithmetic checking (such as overflows and division by zero). Beware that a bad script may panic the entire system! |
|
||||
| `no_function` | Disable script-defined functions if you don't need them. |
|
||||
| `no_index` | Disable arrays and indexing features if you don't need them. |
|
||||
| `no_float` | Disable floating-point numbers and math if you don't need them. |
|
||||
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. |
|
||||
@ -56,8 +57,8 @@ Related
|
||||
|
||||
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.
|
||||
* 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)
|
||||
* [ChaiScript] - 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] on [awesome-rust].
|
||||
|
||||
Examples
|
||||
--------
|
||||
@ -196,23 +197,23 @@ Values and types
|
||||
The following primitive types are supported natively:
|
||||
|
||||
| Category | Types |
|
||||
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **Integer** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`](#optional-features)),<br/>`u64`, `i64` _(default)_ |
|
||||
| **Floating-point** (disabled with [`no_float`](#optional-features)) | `f32`, `f64` _(default)_ |
|
||||
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| **Integer** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ |
|
||||
| **Floating-point** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ |
|
||||
| **Character** | `char` |
|
||||
| **Boolean** | `bool` |
|
||||
| **Array** (disabled with [`no_index`](#optional-features)) | `rhai::Array` |
|
||||
| **Array** (disabled with [`no_index`]) | `rhai::Array` |
|
||||
| **Dynamic** (i.e. can be anything) | `rhai::Dynamic` |
|
||||
| **System** (current configuration) | `rhai::INT` (`i32` or `i64`),<br/>`rhai::FLOAT` (`f32` or `f64`) |
|
||||
|
||||
All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different; you cannot even add them together.
|
||||
|
||||
The default integer type is `i64`. If you do not need any other integer type, you can enable the [`only_i64`](#optional-features) feature.
|
||||
The default integer type is `i64`. If you do not need any other integer type, you can enable the [`only_i64`] feature.
|
||||
|
||||
If you only need 32-bit integers, you can enable the [`only_i32`](#optional-features) feature and remove support for all integer types other than `i32` including `i64`.
|
||||
If you only need 32-bit integers, you can enable the [`only_i32`] feature and remove support for all integer types other than `i32` including `i64`.
|
||||
This is useful on 32-bit systems where using 64-bit integers incurs a performance penalty.
|
||||
|
||||
If you do not need floating-point, enable the [`no_float`](#optional-features) feature to remove support.
|
||||
If you do not need floating-point, enable the [`no_float`] feature to remove support.
|
||||
|
||||
Value conversions
|
||||
-----------------
|
||||
@ -303,17 +304,17 @@ use std::fmt::Display;
|
||||
|
||||
use rhai::{Engine, RegisterFn};
|
||||
|
||||
fn showit<T: Display>(x: &mut T) -> () {
|
||||
println!("{}", x)
|
||||
fn show_it<T: Display>(x: &mut T) -> () {
|
||||
println!("put up a good show: {}!", x)
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.register_fn("print", showit as fn(x: &mut i64)->());
|
||||
engine.register_fn("print", showit as fn(x: &mut bool)->());
|
||||
engine.register_fn("print", showit as fn(x: &mut String)->());
|
||||
engine.register_fn("print", show_it as fn(x: &mut i64)->());
|
||||
engine.register_fn("print", show_it as fn(x: &mut bool)->());
|
||||
engine.register_fn("print", show_it as fn(x: &mut String)->());
|
||||
}
|
||||
```
|
||||
|
||||
@ -615,24 +616,23 @@ Unary operators
|
||||
```rust
|
||||
let number = -5;
|
||||
number = -5 - +5;
|
||||
let booly = !true;
|
||||
let boolean = !true;
|
||||
```
|
||||
|
||||
Numeric functions
|
||||
-----------------
|
||||
|
||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`]) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
||||
|
||||
| Function | Description |
|
||||
| ---------- | ----------------------------------- |
|
||||
| ---------- | --------------------------------- |
|
||||
| `abs` | absolute value |
|
||||
| `to_int` | converts an `f32` or `f64` to `i64` |
|
||||
| `to_float` | converts an integer type to `f64` |
|
||||
|
||||
Floating-point functions
|
||||
------------------------
|
||||
|
||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on `f64` only:
|
||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`]) operate on `f64` only:
|
||||
|
||||
| Category | Functions |
|
||||
| ---------------- | ------------------------------------------------------------ |
|
||||
@ -642,7 +642,8 @@ The following standard functions (defined in the standard library but excluded i
|
||||
| Exponential | `exp` (base _e_) |
|
||||
| Logarithmic | `ln` (base _e_), `log10` (base 10), `log` (any base) |
|
||||
| Rounding | `floor`, `ceiling`, `round`, `int`, `fraction` |
|
||||
| Tests | `is_nan`, `is_finite`, `is_infinite` |
|
||||
| Conversion | `to_int` |
|
||||
| Testing | `is_nan`, `is_finite`, `is_infinite` |
|
||||
|
||||
Strings and Chars
|
||||
-----------------
|
||||
@ -684,7 +685,7 @@ record[4] = '\x58'; // 0x58 = 'X'
|
||||
record == "Bob X. Davis: age 42 ❤\n";
|
||||
```
|
||||
|
||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on strings:
|
||||
The following standard functions (defined in the standard library but excluded if [`no_stdlib`]) operate on strings:
|
||||
|
||||
| Function | Description |
|
||||
| ---------- | ------------------------------------------------------------------------ |
|
||||
@ -731,7 +732,7 @@ Arrays
|
||||
|
||||
You can create arrays of values, and then access them with numeric indices.
|
||||
|
||||
The following functions (defined in the standard library but excluded if [`no_stdlib`](#optional-features)) operate on arrays:
|
||||
The following functions (defined in the standard library but excluded if [`no_stdlib`]) operate on arrays:
|
||||
|
||||
| Function | Description |
|
||||
| ---------- | ------------------------------------------------------------------------------------- |
|
||||
@ -803,7 +804,7 @@ engine.register_fn("push",
|
||||
|
||||
The type of a Rhai array is `rhai::Array`. `type_of()` returns `"array"`.
|
||||
|
||||
Arrays are disabled via the [`no_index`](#optional-features) feature.
|
||||
Arrays are disabled via the [`no_index`] feature.
|
||||
|
||||
Comparison operators
|
||||
--------------------
|
||||
@ -954,7 +955,7 @@ println!(result); // prints "Runtime error: 42 is too large! (line 5, position
|
||||
Functions
|
||||
---------
|
||||
|
||||
Rhai supports defining functions in script:
|
||||
Rhai supports defining functions in script (unless disabled with [`no_function`]):
|
||||
|
||||
```rust
|
||||
fn add(x, y) {
|
||||
@ -1048,3 +1049,17 @@ for entry in log {
|
||||
println!("{}", entry);
|
||||
}
|
||||
```
|
||||
|
||||
[ChaiScript]: http://chaiscript.com/
|
||||
[scripting languages for Rust]: https://github.com/rust-unofficial/awesome-rust#scripting
|
||||
[awesome-rust]: https://github.com/rust-unofficial/awesome-rust
|
||||
|
||||
[`num-traits`]: https://crates.io/crates/num-traits/
|
||||
[`debug_msgs`]: #optional-features
|
||||
[`unchecked`]: #optional-features
|
||||
[`no_stdlib`]: #optional-features
|
||||
[`no_index`]: #optional-features
|
||||
[`no_float`]: #optional-features
|
||||
[`no_function`]: #optional-features
|
||||
[`only_i32`]: #optional-features
|
||||
[`only_i64`]: #optional-features
|
||||
|
26
src/api.rs
26
src/api.rs
@ -168,6 +168,11 @@ impl<'e> Engine<'e> {
|
||||
retain_functions: bool,
|
||||
ast: &AST,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
#[cfg(feature = "no_function")]
|
||||
let AST(statements) = ast;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let statements = {
|
||||
let AST(statements, functions) = ast;
|
||||
|
||||
functions.iter().for_each(|f| {
|
||||
@ -180,6 +185,9 @@ impl<'e> Engine<'e> {
|
||||
);
|
||||
});
|
||||
|
||||
statements
|
||||
};
|
||||
|
||||
let result = statements
|
||||
.iter()
|
||||
.try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt));
|
||||
@ -244,8 +252,15 @@ impl<'e> Engine<'e> {
|
||||
|
||||
parse(&mut tokens.peekable(), self.optimize)
|
||||
.map_err(|err| EvalAltResult::ErrorParsing(err))
|
||||
.and_then(|AST(ref statements, ref functions)| {
|
||||
for f in functions {
|
||||
.and_then(|ast| {
|
||||
#[cfg(feature = "no_function")]
|
||||
let AST(statements) = ast;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let statements = {
|
||||
let AST(ref statements, ref functions) = ast;
|
||||
|
||||
functions.iter().for_each(|f| {
|
||||
self.script_functions.insert(
|
||||
FnSpec {
|
||||
name: f.name.clone().into(),
|
||||
@ -253,7 +268,10 @@ impl<'e> Engine<'e> {
|
||||
},
|
||||
Arc::new(FnIntExt::Int(f.clone())),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
statements
|
||||
};
|
||||
|
||||
let val = statements
|
||||
.iter()
|
||||
@ -275,6 +293,7 @@ impl<'e> Engine<'e> {
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
||||
/// # #[cfg(not(feature = "no_stdlib"))]
|
||||
/// # #[cfg(not(feature = "no_function"))]
|
||||
/// # {
|
||||
/// use rhai::Engine;
|
||||
///
|
||||
@ -289,6 +308,7 @@ impl<'e> Engine<'e> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub fn call_fn<A: FuncArgs, T: Any + Clone>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
@ -4,6 +4,8 @@ use crate::any::{Any, AnyExt, Dynamic, Variant};
|
||||
use crate::parser::{Expr, FnDef, Position, Stmt};
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::scope::Scope;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::INT;
|
||||
|
||||
use std::{
|
||||
|
@ -139,7 +139,10 @@ impl fmt::Debug for Position {
|
||||
}
|
||||
|
||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||
pub struct AST(pub(crate) Vec<Stmt>, pub(crate) Vec<FnDef<'static>>);
|
||||
pub struct AST(
|
||||
pub(crate) Vec<Stmt>,
|
||||
#[cfg(not(feature = "no_function"))] pub(crate) Vec<FnDef<'static>>,
|
||||
);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FnDef<'a> {
|
||||
@ -1820,6 +1823,7 @@ fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, Parse
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef<'static>, ParseError> {
|
||||
let pos = match input.next() {
|
||||
Some((_, tok_pos)) => tok_pos,
|
||||
@ -1892,11 +1896,14 @@ fn parse_top_level<'a>(
|
||||
input: &mut Peekable<TokenIterator<'a>>,
|
||||
optimize_ast: bool,
|
||||
) -> Result<AST, ParseError> {
|
||||
let mut statements = Vec::new();
|
||||
let mut functions = Vec::new();
|
||||
let mut statements = Vec::<Stmt>::new();
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let mut functions = Vec::<FnDef>::new();
|
||||
|
||||
while input.peek().is_some() {
|
||||
match input.peek() {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Some(&(Token::Fn, _)) => functions.push(parse_fn(input)?),
|
||||
_ => statements.push(parse_stmt(input)?),
|
||||
}
|
||||
@ -1910,6 +1917,7 @@ fn parse_top_level<'a>(
|
||||
return Ok(if optimize_ast {
|
||||
AST(
|
||||
optimize(statements),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
functions
|
||||
.into_iter()
|
||||
.map(|mut fn_def| {
|
||||
@ -1920,7 +1928,11 @@ fn parse_top_level<'a>(
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
AST(statements, functions)
|
||||
AST(
|
||||
statements,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
functions,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -39,10 +39,9 @@ fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
|
||||
assert_eq!(
|
||||
engine.eval::<bool>(
|
||||
r"
|
||||
fn this() { true }
|
||||
fn that() { 9/0 }
|
||||
let this = true;
|
||||
|
||||
this() || that();
|
||||
this || { throw; };
|
||||
"
|
||||
)?,
|
||||
true
|
||||
@ -51,10 +50,9 @@ fn test_bool_op_short_circuit() -> Result<(), EvalAltResult> {
|
||||
assert_eq!(
|
||||
engine.eval::<bool>(
|
||||
r"
|
||||
fn this() { false }
|
||||
fn that() { 9/0 }
|
||||
let this = false;
|
||||
|
||||
this() && that();
|
||||
this && { throw; };
|
||||
"
|
||||
)?,
|
||||
false
|
||||
@ -72,10 +70,9 @@ fn test_bool_op_no_short_circuit1() {
|
||||
engine
|
||||
.eval::<bool>(
|
||||
r"
|
||||
fn this() { false }
|
||||
fn that() { 9/0 }
|
||||
let this = true;
|
||||
|
||||
this() | that();
|
||||
this | { throw; }
|
||||
"
|
||||
)
|
||||
.unwrap(),
|
||||
@ -92,10 +89,9 @@ fn test_bool_op_no_short_circuit2() {
|
||||
engine
|
||||
.eval::<bool>(
|
||||
r"
|
||||
fn this() { false }
|
||||
fn that() { 9/0 }
|
||||
let this = false;
|
||||
|
||||
this() & that();
|
||||
this & { throw; }
|
||||
"
|
||||
)
|
||||
.unwrap(),
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![cfg(not(feature = "no_stdlib"))]
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![cfg(not(feature = "no_function"))]
|
||||
|
||||
use rhai::{Engine, EvalAltResult, INT};
|
||||
|
||||
#[test]
|
||||
|
@ -9,6 +9,7 @@ fn test_not() -> Result<(), EvalAltResult> {
|
||||
false
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
assert_eq!(engine.eval::<bool>("fn not(x) { !x } not(false)")?, true);
|
||||
|
||||
// TODO - do we allow stacking unary operators directly? e.g '!!!!!!!true'
|
||||
|
@ -5,7 +5,10 @@ fn test_unary_minus() -> Result<(), EvalAltResult> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = -5; x")?, -5);
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
assert_eq!(engine.eval::<INT>("fn neg(x) { -x } neg(5)")?, -5);
|
||||
|
||||
assert_eq!(engine.eval::<INT>("5 - -+++--+-5")?, 0);
|
||||
|
||||
Ok(())
|
||||
|
Loading…
Reference in New Issue
Block a user