Split core and standard libraries into packages.
This commit is contained in:
parent
976f3a7f6d
commit
0306d15c04
86
README.md
86
README.md
@ -13,19 +13,20 @@ to add scripting to any application.
|
|||||||
|
|
||||||
Rhai's current features set:
|
Rhai's current features set:
|
||||||
|
|
||||||
* `no-std` support
|
* Easy-to-use language similar to JS+Rust
|
||||||
* Easy integration with Rust native functions and types, including getter/setter/methods
|
* Easy integration with Rust [native functions](#working-with-functions) and [types](#custom-types-and-methods),
|
||||||
* Easily call a script-defined function from Rust
|
including [getter/setter](#getters-and-setters)/[methods](#members-and-methods)
|
||||||
|
* Easily [call a script-defined function](#calling-rhai-functions-from-rust) from Rust
|
||||||
* Freely pass variables/constants into a script via an external [`Scope`]
|
* Freely pass variables/constants into a script via an external [`Scope`]
|
||||||
* Fairly efficient (1 million iterations in 0.75 sec on my 5 year old laptop)
|
* Fairly efficient (1 million iterations in 0.75 sec on my 5 year old laptop)
|
||||||
* Low compile-time overhead (~0.6 sec debug/~3 sec release for script runner app)
|
* Low compile-time overhead (~0.6 sec debug/~3 sec release for script runner app)
|
||||||
* Easy-to-use language similar to JS+Rust
|
* [`no-std`](#optional-features) support
|
||||||
* Support for function overloading
|
* Support for [function overloading](#function-overloading)
|
||||||
* Support for operator overloading
|
* Support for [operator overloading](#operator-overloading)
|
||||||
* Compiled script is optimized for repeat evaluations
|
* Compiled script is [optimized](#script-optimization) for repeat evaluations
|
||||||
* Support for minimal builds by excluding unneeded language features
|
* Support for [minimal builds](#minimal-builds) by excluding unneeded language [features](#optional-features)
|
||||||
* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/)
|
* Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/)
|
||||||
to do checked arithmetic operations); for [`no_std`] builds, a number of additional dependencies are
|
to do checked arithmetic operations); for [`no-std`](#optional-features) builds, a number of additional dependencies are
|
||||||
pulled in to provide for functionalities that used to be in `std`.
|
pulled in to provide for functionalities that used to be in `std`.
|
||||||
|
|
||||||
**Note:** Currently, the version is 0.13.0, so the language and API's may change before they stabilize.
|
**Note:** Currently, the version is 0.13.0, so the language and API's may change before they stabilize.
|
||||||
@ -116,10 +117,13 @@ Opt out of as many features as possible, if they are not needed, to reduce code
|
|||||||
all code is compiled in as what a script requires cannot be predicted. If a language feature is not needed,
|
all code is compiled in as what a script requires cannot be predicted. If a language feature is not needed,
|
||||||
omitting them via special features is a prudent strategy to optimize the build for size.
|
omitting them via special features is a prudent strategy to optimize the build for size.
|
||||||
|
|
||||||
Start by using [`Engine::new_raw`](#raw-engine) to create a _raw_ engine which does not register the standard library of utility
|
Omitting arrays (`no_index`) yields the most code-size savings, followed by floating-point support
|
||||||
functions. Secondly, omitting arrays (`no_index`) yields the most code-size savings, followed by floating-point support
|
(`no_float`), checked arithmetic (`unchecked`) and finally object maps and custom types (`no_object`).
|
||||||
(`no_float`), checked arithmetic (`unchecked`) and finally object maps and custom types (`no_object`). Disable script-defined
|
Disable script-defined functions (`no_function`) only when the feature is not needed because code size savings is minimal.
|
||||||
functions (`no_function`) only when the feature is not needed because code size savings is minimal.
|
|
||||||
|
[`Engine::new_raw`](#raw-engine) creates a _raw_ engine which does not register _any_ utility functions.
|
||||||
|
This makes the scripting language quite useless as even basic arithmetic operators are not supported.
|
||||||
|
Selectively include the necessary operators by loading specific [packages](#packages) while minimizing the code footprint.
|
||||||
|
|
||||||
Related
|
Related
|
||||||
-------
|
-------
|
||||||
@ -349,17 +353,41 @@ Raw `Engine`
|
|||||||
`Engine::new` creates a scripting [`Engine`] with common functionalities (e.g. printing to the console via `print`).
|
`Engine::new` creates a scripting [`Engine`] with common functionalities (e.g. printing to the console via `print`).
|
||||||
In many controlled embedded environments, however, these are not needed.
|
In many controlled embedded environments, however, these are not needed.
|
||||||
|
|
||||||
Use `Engine::new_raw` to create a _raw_ `Engine`, in which:
|
Use `Engine::new_raw` to create a _raw_ `Engine`, in which _nothing_ is added, not even basic arithmetic and logic operators!
|
||||||
|
|
||||||
* the `print` and `debug` statements do nothing instead of displaying to the console (see [`print` and `debug`](#print-and-debug) below)
|
### Packages
|
||||||
* the _standard library_ of utility functions is _not_ loaded by default (load it using the `register_stdlib` method).
|
|
||||||
|
Rhai functional features are provided in different _packages_ that can be loaded via a call to `load_package`.
|
||||||
|
Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package` must be imported in order for
|
||||||
|
packages to be used.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let mut engine = Engine::new_raw(); // create a 'raw' Engine
|
use rhai::Engine;
|
||||||
|
use rhai::packages::Package // load the 'Package' trait to use packages
|
||||||
|
use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic)
|
||||||
|
|
||||||
engine.register_stdlib(); // register the standard library manually
|
let mut engine = Engine::new_raw(); // create a 'raw' Engine
|
||||||
|
let package = CorePackage::new(); // create a package
|
||||||
|
|
||||||
|
engine.load_package(package.get()); // load the package manually
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The follow packages are available:
|
||||||
|
|
||||||
|
| Package | Description | In `CorePackage` | In `StandardPackage` |
|
||||||
|
| ------------------------ | ----------------------------------------------- | :--------------: | :------------------: |
|
||||||
|
| `BasicArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) | Yes | Yes |
|
||||||
|
| `BasicIteratorPackage` | Numeric ranges | Yes | Yes |
|
||||||
|
| `LogicPackage` | Logic and comparison operators (e.g. `==`, `>`) | Yes | Yes |
|
||||||
|
| `BasicStringPackage` | Basic string functions | Yes | Yes |
|
||||||
|
| `BasicTimePackage` | Basic time functions (e.g. `Instant`) | Yes | Yes |
|
||||||
|
| `MoreStringPackage` | Additional string functions | No | Yes |
|
||||||
|
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
|
||||||
|
| `BasicArrayPackage` | Basic [array] functions | No | Yes |
|
||||||
|
| `BasicMapPackage` | Basic [object map] functions | No | Yes |
|
||||||
|
| `CorePackage` | Basic essentials | | |
|
||||||
|
| `StandardPackage` | Standard library | | |
|
||||||
|
|
||||||
Evaluate expressions only
|
Evaluate expressions only
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
@ -401,7 +429,7 @@ The following primitive types are supported natively:
|
|||||||
| **Unicode string** | `String` (_not_ `&str`) | `"string"` | `"hello"` etc. |
|
| **Unicode string** | `String` (_not_ `&str`) | `"string"` | `"hello"` etc. |
|
||||||
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ? ? ? ]"` |
|
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ? ? ? ]"` |
|
||||||
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
|
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
|
||||||
| **Timestamp** (implemented in standard library) | `std::time::Instant` | `"timestamp"` | _not supported_ |
|
| **Timestamp** (implemented in the [`BasicTimePackage`](#packages)) | `std::time::Instant` | `"timestamp"` | _not supported_ |
|
||||||
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
||||||
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
||||||
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
||||||
@ -1116,7 +1144,7 @@ number = -5 - +5;
|
|||||||
Numeric functions
|
Numeric functions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The following standard functions (defined in the standard library but excluded if using a [raw `Engine`]) operate on
|
The following standard functions (defined in the [`BasicMathPackage`] but excluded if using a [raw `Engine`]) operate on
|
||||||
`i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
`i8`, `i16`, `i32`, `i64`, `f32` and `f64` only:
|
||||||
|
|
||||||
| Function | Description |
|
| Function | Description |
|
||||||
@ -1127,7 +1155,7 @@ The following standard functions (defined in the standard library but excluded i
|
|||||||
Floating-point functions
|
Floating-point functions
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
The following standard functions (defined in the standard library but excluded if using a [raw `Engine`]) operate on `f64` only:
|
The following standard functions (defined in the [`BasicMathPackage`](#packages) but excluded if using a [raw `Engine`]) operate on `f64` only:
|
||||||
|
|
||||||
| Category | Functions |
|
| Category | Functions |
|
||||||
| ---------------- | ------------------------------------------------------------ |
|
| ---------------- | ------------------------------------------------------------ |
|
||||||
@ -1174,8 +1202,8 @@ Unicode characters.
|
|||||||
Individual characters within a Rhai string can also be replaced just as if the string is an array of Unicode characters.
|
Individual characters within a Rhai string can also be replaced just as if the string is an array of Unicode characters.
|
||||||
In Rhai, there is also no separate concepts of `String` and `&str` as in Rust.
|
In Rhai, there is also no separate concepts of `String` and `&str` as in Rust.
|
||||||
|
|
||||||
Strings can be built up from other strings and types via the `+` operator (provided by the standard library but excluded
|
Strings can be built up from other strings and types via the `+` operator (provided by the [`MoreStringPackage`](#packages)
|
||||||
if using a [raw `Engine`]). This is particularly useful when printing output.
|
but excluded if using a [raw `Engine`]). This is particularly useful when printing output.
|
||||||
|
|
||||||
[`type_of()`] a string returns `"string"`.
|
[`type_of()`] a string returns `"string"`.
|
||||||
|
|
||||||
@ -1225,7 +1253,7 @@ record == "Bob X. Davis: age 42 ❤\n";
|
|||||||
|
|
||||||
### Built-in functions
|
### Built-in functions
|
||||||
|
|
||||||
The following standard methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on strings:
|
The following standard methods (defined in the [`MoreStringPackage`](#packages) but excluded if using a [raw `Engine`]) operate on strings:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
|
| ------------ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
|
||||||
@ -1300,7 +1328,7 @@ Arrays are disabled via the [`no_index`] feature.
|
|||||||
|
|
||||||
### Built-in functions
|
### Built-in functions
|
||||||
|
|
||||||
The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on arrays:
|
The following methods (defined in the [`BasicArrayPackage`](#packages) but excluded if using a [raw `Engine`]) operate on arrays:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
| ------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||||
@ -1420,7 +1448,7 @@ Object maps are disabled via the [`no_object`] feature.
|
|||||||
|
|
||||||
### Built-in functions
|
### Built-in functions
|
||||||
|
|
||||||
The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on object maps:
|
The following methods (defined in the [`BasicMapPackage`](#packages) but excluded if using a [raw `Engine`]) operate on object maps:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
@ -1545,14 +1573,14 @@ result == 3; // the object map is successfully used i
|
|||||||
-------------
|
-------------
|
||||||
[`timestamp`]: #timestamp-s
|
[`timestamp`]: #timestamp-s
|
||||||
|
|
||||||
Timestamps are provided by the standard library (excluded if using a [raw `Engine`]) via the `timestamp`
|
Timestamps are provided by the [`BasicTimePackage`](#packages) (excluded if using a [raw `Engine`]) via the `timestamp`
|
||||||
function.
|
function.
|
||||||
|
|
||||||
The Rust type of a timestamp is `std::time::Instant`. [`type_of()`] a timestamp returns `"timestamp"`.
|
The Rust type of a timestamp is `std::time::Instant`. [`type_of()`] a timestamp returns `"timestamp"`.
|
||||||
|
|
||||||
### Built-in functions
|
### Built-in functions
|
||||||
|
|
||||||
The following methods (defined in the standard library but excluded if using a [raw `Engine`]) operate on timestamps:
|
The following methods (defined in the [`BasicTimePackage`](#packages) but excluded if using a [raw `Engine`]) operate on timestamps:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ------------ | ---------------------------------- | -------------------------------------------------------- |
|
| ------------ | ---------------------------------- | -------------------------------------------------------- |
|
||||||
@ -1876,7 +1904,7 @@ Unlike C/C++, functions can be defined _anywhere_ within the global level. A fun
|
|||||||
prior to being used in a script; a statement in the script can freely call a function defined afterwards.
|
prior to being used in a script; a statement in the script can freely call a function defined afterwards.
|
||||||
This is similar to Rust and many other modern languages.
|
This is similar to Rust and many other modern languages.
|
||||||
|
|
||||||
### Functions overloading
|
### Function overloading
|
||||||
|
|
||||||
Functions can be _overloaded_ and are resolved purely upon the function's _name_ and the _number_ of parameters
|
Functions can be _overloaded_ and are resolved purely upon the function's _name_ and the _number_ of parameters
|
||||||
(but not parameter _types_, since all parameters are the same type - [`Dynamic`]).
|
(but not parameter _types_, since all parameters are the same type - [`Dynamic`]).
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
///! Test evaluating expressions
|
///! Test evaluating expressions
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use rhai::{Array, Engine, Map, RegisterFn, INT};
|
use rhai::{Array, CorePackage, Engine, Map, Package, RegisterFn, INT};
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@ -16,6 +16,16 @@ fn bench_engine_new_raw(bench: &mut Bencher) {
|
|||||||
bench.iter(|| Engine::new_raw());
|
bench.iter(|| Engine::new_raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_engine_new_raw_core(bench: &mut Bencher) {
|
||||||
|
let package = CorePackage::new();
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
let mut engine = Engine::new_raw();
|
||||||
|
engine.load_package(package.get());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn bench_engine_register_fn(bench: &mut Bencher) {
|
fn bench_engine_register_fn(bench: &mut Bencher) {
|
||||||
fn hello(a: INT, b: Array, c: Map) -> bool {
|
fn hello(a: INT, b: Array, c: Map) -> bool {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
///! Test 1,000 iterations
|
///! Test 1,000 iterations
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
use rhai::{Engine, OptimizationLevel, Scope, INT};
|
use rhai::{Engine, OptimizationLevel, INT};
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
@ -34,6 +34,8 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) {
|
|||||||
fibonacci(n-1) + fibonacci(n-2)
|
fibonacci(n-1) + fibonacci(n-2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fibonacci(20)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
@ -41,9 +43,5 @@ fn bench_iterations_fibonacci(bench: &mut Bencher) {
|
|||||||
|
|
||||||
let ast = engine.compile(script).unwrap();
|
let ast = engine.compile(script).unwrap();
|
||||||
|
|
||||||
bench.iter(|| {
|
bench.iter(|| engine.eval_ast::<INT>(&ast).unwrap());
|
||||||
engine
|
|
||||||
.call_fn::<_, INT>(&mut Scope::new(), &ast, "fibonacci", (20 as INT,))
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{packages::*, Engine, EvalAltResult, INT};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
fn main() -> Result<(), EvalAltResult> {
|
fn main() -> Result<(), EvalAltResult> {
|
||||||
let engine = Engine::new();
|
let mut engine = Engine::new_raw();
|
||||||
|
engine.load_package(ArithmeticPackage::new().get());
|
||||||
|
|
||||||
let result = engine.eval::<INT>("40 + 2")?;
|
let result = engine.eval::<INT>("40 + 2")?;
|
||||||
|
|
||||||
|
304
src/builtin.rs
304
src/builtin.rs
@ -48,7 +48,6 @@ macro_rules! reg_op {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
macro_rules! reg_op_result {
|
macro_rules! reg_op_result {
|
||||||
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||||
$(
|
$(
|
||||||
@ -57,7 +56,6 @@ macro_rules! reg_op_result {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
macro_rules! reg_op_result1 {
|
macro_rules! reg_op_result1 {
|
||||||
($self:expr, $x:expr, $op:expr, $v:ty, $( $y:ty ),*) => (
|
($self:expr, $x:expr, $op:expr, $v:ty, $( $y:ty ),*) => (
|
||||||
$(
|
$(
|
||||||
@ -98,7 +96,6 @@ impl Engine {
|
|||||||
/// Register the core built-in library.
|
/// Register the core built-in library.
|
||||||
pub(crate) fn register_core_lib(&mut self) {
|
pub(crate) fn register_core_lib(&mut self) {
|
||||||
// Checked add
|
// Checked add
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn add<T: Display + CheckedAdd>(x: T, y: T) -> Result<T, EvalAltResult> {
|
fn add<T: Display + CheckedAdd>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
x.checked_add(&y).ok_or_else(|| {
|
x.checked_add(&y).ok_or_else(|| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
@ -108,7 +105,6 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked subtract
|
// Checked subtract
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn sub<T: Display + CheckedSub>(x: T, y: T) -> Result<T, EvalAltResult> {
|
fn sub<T: Display + CheckedSub>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
x.checked_sub(&y).ok_or_else(|| {
|
x.checked_sub(&y).ok_or_else(|| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
@ -118,7 +114,6 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked multiply
|
// Checked multiply
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn mul<T: Display + CheckedMul>(x: T, y: T) -> Result<T, EvalAltResult> {
|
fn mul<T: Display + CheckedMul>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
x.checked_mul(&y).ok_or_else(|| {
|
x.checked_mul(&y).ok_or_else(|| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
@ -128,7 +123,6 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked divide
|
// Checked divide
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn div<T>(x: T, y: T) -> Result<T, EvalAltResult>
|
fn div<T>(x: T, y: T) -> Result<T, EvalAltResult>
|
||||||
where
|
where
|
||||||
T: Display + CheckedDiv + PartialEq + Zero,
|
T: Display + CheckedDiv + PartialEq + Zero,
|
||||||
@ -149,7 +143,6 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
|
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn neg<T: Display + CheckedNeg>(x: T) -> Result<T, EvalAltResult> {
|
fn neg<T: Display + CheckedNeg>(x: T) -> Result<T, EvalAltResult> {
|
||||||
x.checked_neg().ok_or_else(|| {
|
x.checked_neg().ok_or_else(|| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
@ -159,7 +152,6 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked absolute
|
// Checked absolute
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> Result<T, EvalAltResult> {
|
fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> Result<T, EvalAltResult> {
|
||||||
// FIX - We don't use Signed::abs() here because, contrary to documentation, it panics
|
// FIX - We don't use Signed::abs() here because, contrary to documentation, it panics
|
||||||
// when the number is ::MIN instead of returning ::MIN itself.
|
// when the number is ::MIN instead of returning ::MIN itself.
|
||||||
@ -175,32 +167,26 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Unchecked add - may panic on overflow
|
// Unchecked add - may panic on overflow
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
fn add_u<T: Add>(x: T, y: T) -> <T as Add>::Output {
|
fn add_u<T: Add>(x: T, y: T) -> <T as Add>::Output {
|
||||||
x + y
|
x + y
|
||||||
}
|
}
|
||||||
// Unchecked subtract - may panic on underflow
|
// Unchecked subtract - may panic on underflow
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
fn sub_u<T: Sub>(x: T, y: T) -> <T as Sub>::Output {
|
fn sub_u<T: Sub>(x: T, y: T) -> <T as Sub>::Output {
|
||||||
x - y
|
x - y
|
||||||
}
|
}
|
||||||
// Unchecked multiply - may panic on overflow
|
// Unchecked multiply - may panic on overflow
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
fn mul_u<T: Mul>(x: T, y: T) -> <T as Mul>::Output {
|
fn mul_u<T: Mul>(x: T, y: T) -> <T as Mul>::Output {
|
||||||
x * y
|
x * y
|
||||||
}
|
}
|
||||||
// Unchecked divide - may panic when dividing by zero
|
// Unchecked divide - may panic when dividing by zero
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
fn div_u<T: Div>(x: T, y: T) -> <T as Div>::Output {
|
fn div_u<T: Div>(x: T, y: T) -> <T as Div>::Output {
|
||||||
x / y
|
x / y
|
||||||
}
|
}
|
||||||
// Unchecked negative - may panic on overflow
|
// Unchecked negative - may panic on overflow
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
fn neg_u<T: Neg>(x: T) -> <T as Neg>::Output {
|
fn neg_u<T: Neg>(x: T) -> <T as Neg>::Output {
|
||||||
-x
|
-x
|
||||||
}
|
}
|
||||||
// Unchecked absolute - may panic on overflow
|
// Unchecked absolute - may panic on overflow
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
fn abs_u<T>(x: T) -> <T as Neg>::Output
|
fn abs_u<T>(x: T) -> <T as Neg>::Output
|
||||||
where
|
where
|
||||||
T: Neg + PartialOrd + Default + Into<<T as Neg>::Output>,
|
T: Neg + PartialOrd + Default + Into<<T as Neg>::Output>,
|
||||||
@ -236,7 +222,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checked left-shift
|
// Checked left-shift
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn shl<T: Display + CheckedShl>(x: T, y: INT) -> Result<T, EvalAltResult> {
|
fn shl<T: Display + CheckedShl>(x: T, y: INT) -> Result<T, EvalAltResult> {
|
||||||
// Cannot shift by a negative number of bits
|
// Cannot shift by a negative number of bits
|
||||||
if y < 0 {
|
if y < 0 {
|
||||||
@ -254,7 +239,6 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Checked right-shift
|
// Checked right-shift
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn shr<T: Display + CheckedShr>(x: T, y: INT) -> Result<T, EvalAltResult> {
|
fn shr<T: Display + CheckedShr>(x: T, y: INT) -> Result<T, EvalAltResult> {
|
||||||
// Cannot shift by a negative number of bits
|
// Cannot shift by a negative number of bits
|
||||||
if y < 0 {
|
if y < 0 {
|
||||||
@ -272,17 +256,14 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Unchecked left-shift - may panic if shifting by a negative number of bits
|
// Unchecked left-shift - may panic if shifting by a negative number of bits
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
fn shl_u<T: Shl<T>>(x: T, y: T) -> <T as Shl<T>>::Output {
|
fn shl_u<T: Shl<T>>(x: T, y: T) -> <T as Shl<T>>::Output {
|
||||||
x.shl(y)
|
x.shl(y)
|
||||||
}
|
}
|
||||||
// Unchecked right-shift - may panic if shifting by a negative number of bits
|
// Unchecked right-shift - may panic if shifting by a negative number of bits
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
fn shr_u<T: Shr<T>>(x: T, y: T) -> <T as Shr<T>>::Output {
|
fn shr_u<T: Shr<T>>(x: T, y: T) -> <T as Shr<T>>::Output {
|
||||||
x.shr(y)
|
x.shr(y)
|
||||||
}
|
}
|
||||||
// Checked modulo
|
// Checked modulo
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn modulo<T: Display + CheckedRem>(x: T, y: T) -> Result<T, EvalAltResult> {
|
fn modulo<T: Display + CheckedRem>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
x.checked_rem(&y).ok_or_else(|| {
|
x.checked_rem(&y).ok_or_else(|| {
|
||||||
EvalAltResult::ErrorArithmetic(
|
EvalAltResult::ErrorArithmetic(
|
||||||
@ -292,12 +273,10 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Unchecked modulo - may panic if dividing by zero
|
// Unchecked modulo - may panic if dividing by zero
|
||||||
#[cfg(any(feature = "unchecked", not(feature = "no_float")))]
|
|
||||||
fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output {
|
fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output {
|
||||||
x % y
|
x % y
|
||||||
}
|
}
|
||||||
// Checked power
|
// Checked power
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
fn pow_i_i(x: INT, y: INT) -> Result<INT, EvalAltResult> {
|
fn pow_i_i(x: INT, y: INT) -> Result<INT, EvalAltResult> {
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
{
|
{
|
||||||
@ -339,7 +318,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Unchecked integer power - may panic on overflow or if the power index is too high (> u32::MAX)
|
// Unchecked integer power - may panic on overflow or if the power index is too high (> u32::MAX)
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
fn pow_i_i_u(x: INT, y: INT) -> INT {
|
fn pow_i_i_u(x: INT, y: INT) -> INT {
|
||||||
x.pow(y as u32)
|
x.pow(y as u32)
|
||||||
}
|
}
|
||||||
@ -349,7 +327,6 @@ impl Engine {
|
|||||||
x.powf(y)
|
x.powf(y)
|
||||||
}
|
}
|
||||||
// Checked power
|
// Checked power
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
fn pow_f_i(x: FLOAT, y: INT) -> Result<FLOAT, EvalAltResult> {
|
fn pow_f_i(x: FLOAT, y: INT) -> Result<FLOAT, EvalAltResult> {
|
||||||
// Raise to power that is larger than an i32
|
// Raise to power that is larger than an i32
|
||||||
@ -411,34 +388,32 @@ impl Engine {
|
|||||||
reg_op!(self, "/", div_u, f32, f64);
|
reg_op!(self, "/", div_u, f32, f64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reg_cmp!(self, "<", lt, INT, String, char);
|
||||||
|
reg_cmp!(self, "<=", lte, INT, String, char);
|
||||||
|
reg_cmp!(self, ">", gt, INT, String, char);
|
||||||
|
reg_cmp!(self, ">=", gte, INT, String, char);
|
||||||
|
reg_cmp!(self, "==", eq, INT, String, char, bool);
|
||||||
|
reg_cmp!(self, "!=", ne, INT, String, char, bool);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
reg_cmp!(self, "<", lt, INT, String, char);
|
reg_cmp!(self, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
reg_cmp!(self, "<=", lte, INT, String, char);
|
reg_cmp!(self, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
reg_cmp!(self, ">", gt, INT, String, char);
|
reg_cmp!(self, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
reg_cmp!(self, ">=", gte, INT, String, char);
|
reg_cmp!(self, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
reg_cmp!(self, "==", eq, INT, String, char, bool);
|
reg_cmp!(self, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
reg_cmp!(self, "!=", ne, INT, String, char, bool);
|
reg_cmp!(self, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
{
|
||||||
{
|
reg_cmp!(self, "<", lt, f32, f64);
|
||||||
reg_cmp!(self, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
reg_cmp!(self, "<=", lte, f32, f64);
|
||||||
reg_cmp!(self, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
reg_cmp!(self, ">", gt, f32, f64);
|
||||||
reg_cmp!(self, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
reg_cmp!(self, ">=", gte, f32, f64);
|
||||||
reg_cmp!(self, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
reg_cmp!(self, "==", eq, f32, f64);
|
||||||
reg_cmp!(self, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
reg_cmp!(self, "!=", ne, f32, f64);
|
||||||
reg_cmp!(self, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
{
|
|
||||||
reg_cmp!(self, "<", lt, f32, f64);
|
|
||||||
reg_cmp!(self, "<=", lte, f32, f64);
|
|
||||||
reg_cmp!(self, ">", gt, f32, f64);
|
|
||||||
reg_cmp!(self, ">=", gte, f32, f64);
|
|
||||||
reg_cmp!(self, "==", eq, f32, f64);
|
|
||||||
reg_cmp!(self, "!=", ne, f32, f64);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `&&` and `||` are treated specially as they short-circuit.
|
// `&&` and `||` are treated specially as they short-circuit.
|
||||||
@ -517,59 +492,57 @@ impl Engine {
|
|||||||
self.register_fn("~", pow_f_i_u);
|
self.register_fn("~", pow_f_i_u);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
macro_rules! reg_un {
|
||||||
macro_rules! reg_un {
|
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||||
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
$(
|
||||||
$(
|
$self.register_fn($x, $op as fn(x: $y)->$y);
|
||||||
$self.register_fn($x, $op as fn(x: $y)->$y);
|
)*
|
||||||
)*
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
macro_rules! reg_un_result {
|
|
||||||
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
|
||||||
$(
|
|
||||||
$self.register_result_fn($x, $op as fn(x: $y)->Result<$y,EvalAltResult>);
|
|
||||||
)*
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
{
|
|
||||||
reg_un_result!(self, "-", neg, INT);
|
|
||||||
reg_un_result!(self, "abs", abs, INT);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
{
|
|
||||||
reg_un_result!(self, "-", neg, i8, i16, i32, i64);
|
|
||||||
reg_un_result!(self, "abs", abs, i8, i16, i32, i64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
{
|
|
||||||
reg_un!(self, "-", neg_u, INT);
|
|
||||||
reg_un!(self, "abs", abs_u, INT);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
{
|
|
||||||
reg_un!(self, "-", neg_u, i8, i16, i32, i64);
|
|
||||||
reg_un!(self, "abs", abs_u, i8, i16, i32, i64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
{
|
|
||||||
reg_un!(self, "-", neg_u, f32, f64);
|
|
||||||
reg_un!(self, "abs", abs_u, f32, f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
reg_un!(self, "!", not, bool);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
macro_rules! reg_un_result {
|
||||||
|
($self:expr, $x:expr, $op:expr, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_result_fn($x, $op as fn(x: $y)->Result<$y,EvalAltResult>);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
reg_un_result!(self, "-", neg, INT);
|
||||||
|
reg_un_result!(self, "abs", abs, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_un_result!(self, "-", neg, i8, i16, i32, i64, i128);
|
||||||
|
reg_un_result!(self, "abs", abs, i8, i16, i32, i64, i128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
{
|
||||||
|
reg_un!(self, "-", neg_u, INT);
|
||||||
|
reg_un!(self, "abs", abs_u, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_un!(self, "-", neg_u, i8, i16, i32, i64, i128);
|
||||||
|
reg_un!(self, "abs", abs_u, i8, i16, i32, i64, i128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_un!(self, "-", neg_u, f32, f64);
|
||||||
|
reg_un!(self, "abs", abs_u, f32, f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_un!(self, "!", not, bool);
|
||||||
|
|
||||||
self.register_fn("+", |x: String, y: String| x + &y); // String + String
|
self.register_fn("+", |x: String, y: String| x + &y); // String + String
|
||||||
self.register_fn("==", |_: (), _: ()| true); // () == ()
|
self.register_fn("==", |_: (), _: ()| true); // () == ()
|
||||||
|
|
||||||
@ -581,78 +554,76 @@ impl Engine {
|
|||||||
format!("{}", x)
|
format!("{}", x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_fn1 {
|
||||||
|
($self:expr, $x:expr, $op:expr, $r:ty, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
$self.register_fn($x, $op as fn(x: $y)->$r);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_fn1!(self, KEYWORD_PRINT, to_string, String, INT, bool);
|
||||||
|
reg_fn1!(self, FUNC_TO_STRING, to_string, String, INT, bool);
|
||||||
|
reg_fn1!(self, KEYWORD_PRINT, to_string, String, char, String);
|
||||||
|
reg_fn1!(self, FUNC_TO_STRING, to_string, String, char, String);
|
||||||
|
self.register_fn(KEYWORD_PRINT, || "".to_string());
|
||||||
|
self.register_fn(KEYWORD_PRINT, |_: ()| "".to_string());
|
||||||
|
self.register_fn(FUNC_TO_STRING, |_: ()| "".to_string());
|
||||||
|
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, INT, bool, ());
|
||||||
|
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, char, String);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
macro_rules! reg_fn1 {
|
reg_fn1!(self, KEYWORD_PRINT, to_string, String, i8, u8, i16, u16);
|
||||||
($self:expr, $x:expr, $op:expr, $r:ty, $( $y:ty ),*) => (
|
reg_fn1!(self, FUNC_TO_STRING, to_string, String, i8, u8, i16, u16);
|
||||||
$(
|
reg_fn1!(self, KEYWORD_PRINT, to_string, String, i32, u32, i64, u64);
|
||||||
$self.register_fn($x, $op as fn(x: $y)->$r);
|
reg_fn1!(self, FUNC_TO_STRING, to_string, String, i32, u32, i64, u64);
|
||||||
)*
|
reg_fn1!(self, KEYWORD_PRINT, to_string, String, i128, u128);
|
||||||
)
|
reg_fn1!(self, FUNC_TO_STRING, to_string, String, i128, u128);
|
||||||
}
|
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, i8, u8, i16, u16);
|
||||||
|
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, i32, u32, i64, u64);
|
||||||
|
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
reg_fn1!(self, KEYWORD_PRINT, to_string, String, INT, bool);
|
#[cfg(not(feature = "no_float"))]
|
||||||
reg_fn1!(self, FUNC_TO_STRING, to_string, String, INT, bool);
|
{
|
||||||
reg_fn1!(self, KEYWORD_PRINT, to_string, String, char, String);
|
reg_fn1!(self, KEYWORD_PRINT, to_string, String, f32, f64);
|
||||||
reg_fn1!(self, FUNC_TO_STRING, to_string, String, char, String);
|
reg_fn1!(self, FUNC_TO_STRING, to_string, String, f32, f64);
|
||||||
self.register_fn(KEYWORD_PRINT, || "".to_string());
|
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, f32, f64);
|
||||||
self.register_fn(KEYWORD_PRINT, |_: ()| "".to_string());
|
}
|
||||||
self.register_fn(FUNC_TO_STRING, |_: ()| "".to_string());
|
|
||||||
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, INT, bool, ());
|
|
||||||
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, char, String);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
{
|
||||||
{
|
reg_fn1!(self, KEYWORD_PRINT, to_debug, String, Array);
|
||||||
reg_fn1!(self, KEYWORD_PRINT, to_string, String, i8, u8, i16, u16);
|
reg_fn1!(self, FUNC_TO_STRING, to_debug, String, Array);
|
||||||
reg_fn1!(self, FUNC_TO_STRING, to_string, String, i8, u8, i16, u16);
|
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, Array);
|
||||||
reg_fn1!(self, KEYWORD_PRINT, to_string, String, i32, i64, u32, u64);
|
|
||||||
reg_fn1!(self, FUNC_TO_STRING, to_string, String, i32, i64, u32, u64);
|
|
||||||
reg_fn1!(self, KEYWORD_PRINT, to_string, String, i128, u128);
|
|
||||||
reg_fn1!(self, FUNC_TO_STRING, to_string, String, i128, u128);
|
|
||||||
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, i8, u8, i16, u16);
|
|
||||||
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, i32, i64, u32, u64);
|
|
||||||
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, i128, u128);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
// Register array iterator
|
||||||
{
|
self.register_iterator::<Array, _>(|a: &Dynamic| {
|
||||||
reg_fn1!(self, KEYWORD_PRINT, to_string, String, f32, f64);
|
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
|
||||||
reg_fn1!(self, FUNC_TO_STRING, to_string, String, f32, f64);
|
as Box<dyn Iterator<Item = Dynamic>>
|
||||||
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, f32, f64);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
self.register_fn(KEYWORD_PRINT, |x: &mut Map| format!("#{:?}", x));
|
||||||
|
self.register_fn(FUNC_TO_STRING, |x: &mut Map| format!("#{:?}", x));
|
||||||
|
self.register_fn(KEYWORD_DEBUG, |x: &mut Map| format!("#{:?}", x));
|
||||||
|
|
||||||
|
// Register map access functions
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
self.register_fn("keys", |map: Map| {
|
||||||
|
map.iter()
|
||||||
|
.map(|(k, _)| Dynamic::from(k.clone()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
});
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
self.register_fn("values", |map: Map| {
|
||||||
reg_fn1!(self, KEYWORD_PRINT, to_debug, String, Array);
|
map.into_iter().map(|(_, v)| v).collect::<Vec<_>>()
|
||||||
reg_fn1!(self, FUNC_TO_STRING, to_debug, String, Array);
|
});
|
||||||
reg_fn1!(self, KEYWORD_DEBUG, to_debug, String, Array);
|
|
||||||
|
|
||||||
// Register array iterator
|
|
||||||
self.register_iterator::<Array, _>(|a: &Dynamic| {
|
|
||||||
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
|
|
||||||
as Box<dyn Iterator<Item = Dynamic>>
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
{
|
|
||||||
self.register_fn(KEYWORD_PRINT, |x: &mut Map| format!("#{:?}", x));
|
|
||||||
self.register_fn(FUNC_TO_STRING, |x: &mut Map| format!("#{:?}", x));
|
|
||||||
self.register_fn(KEYWORD_DEBUG, |x: &mut Map| format!("#{:?}", x));
|
|
||||||
|
|
||||||
// Register map access functions
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
self.register_fn("keys", |map: Map| {
|
|
||||||
map.iter()
|
|
||||||
.map(|(k, _)| Dynamic::from(k.clone()))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
self.register_fn("values", |map: Map| {
|
|
||||||
map.into_iter().map(|(_, v)| v).collect::<Vec<_>>()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register range function
|
// Register range function
|
||||||
@ -909,6 +880,7 @@ impl Engine {
|
|||||||
reg_fn2x!(self, "push", push, &mut Array, (), String, Array, ());
|
reg_fn2x!(self, "push", push, &mut Array, (), String, Array, ());
|
||||||
reg_fn3!(self, "pad", pad, &mut Array, INT, (), INT, bool, char);
|
reg_fn3!(self, "pad", pad, &mut Array, INT, (), INT, bool, char);
|
||||||
reg_fn3!(self, "pad", pad, &mut Array, INT, (), String, Array, ());
|
reg_fn3!(self, "pad", pad, &mut Array, INT, (), String, Array, ());
|
||||||
|
reg_fn3!(self, "insert", ins, &mut Array, INT, (), INT, bool, char);
|
||||||
reg_fn3!(self, "insert", ins, &mut Array, INT, (), String, Array, ());
|
reg_fn3!(self, "insert", ins, &mut Array, INT, (), String, Array, ());
|
||||||
|
|
||||||
self.register_fn("append", |list: &mut Array, array: Array| {
|
self.register_fn("append", |list: &mut Array, array: Array| {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use crate::any::{Dynamic, Union};
|
use crate::any::{Dynamic, Union};
|
||||||
use crate::error::ParseErrorType;
|
use crate::error::ParseErrorType;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
|
use crate::packages::{CorePackage, Package, PackageLibrary, StandardPackage};
|
||||||
use crate::parser::{Expr, FnDef, ReturnType, Stmt, INT};
|
use crate::parser::{Expr, FnDef, ReturnType, Stmt, INT};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope};
|
||||||
@ -41,9 +42,9 @@ pub type FnAny =
|
|||||||
pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, Box<EvalAltResult>>;
|
pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync;
|
pub type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync;
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
pub type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub const MAX_CALL_STACK_DEPTH: usize = 28;
|
pub const MAX_CALL_STACK_DEPTH: usize = 28;
|
||||||
@ -221,6 +222,8 @@ impl DerefMut for FunctionsLib {
|
|||||||
///
|
///
|
||||||
/// Currently, `Engine` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, `Engine` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
|
/// A collection of all library packages loaded into the engine.
|
||||||
|
pub(crate) packages: Vec<PackageLibrary>,
|
||||||
/// A hashmap containing all compiled functions known to the engine.
|
/// A hashmap containing all compiled functions known to the engine.
|
||||||
pub(crate) functions: HashMap<u64, Box<FnAny>>,
|
pub(crate) functions: HashMap<u64, Box<FnAny>>,
|
||||||
|
|
||||||
@ -255,7 +258,8 @@ pub struct Engine {
|
|||||||
impl Default for Engine {
|
impl Default for Engine {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// Create the new scripting Engine
|
// Create the new scripting Engine
|
||||||
let mut engine = Engine {
|
let mut engine = Self {
|
||||||
|
packages: Vec::new(),
|
||||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT),
|
functions: HashMap::with_capacity(FUNCTIONS_COUNT),
|
||||||
type_iterators: HashMap::new(),
|
type_iterators: HashMap::new(),
|
||||||
type_names: None,
|
type_names: None,
|
||||||
@ -279,10 +283,11 @@ impl Default for Engine {
|
|||||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
engine.register_core_lib();
|
#[cfg(feature = "no_stdlib")]
|
||||||
|
engine.load_package(CorePackage::new().get());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_stdlib"))]
|
#[cfg(not(feature = "no_stdlib"))]
|
||||||
engine.register_stdlib();
|
engine.load_package(StandardPackage::new().get());
|
||||||
|
|
||||||
engine
|
engine
|
||||||
}
|
}
|
||||||
@ -442,9 +447,11 @@ impl Engine {
|
|||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Engine` with minimal configurations without the standard library etc.
|
/// Create a new `Engine` with _no_ built-in functions.
|
||||||
|
/// Use the `load_package` method to load packages of functions.
|
||||||
pub fn new_raw() -> Self {
|
pub fn new_raw() -> Self {
|
||||||
let mut engine = Engine {
|
Self {
|
||||||
|
packages: Vec::new(),
|
||||||
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
functions: HashMap::with_capacity(FUNCTIONS_COUNT / 2),
|
||||||
type_iterators: HashMap::new(),
|
type_iterators: HashMap::new(),
|
||||||
type_names: None,
|
type_names: None,
|
||||||
@ -463,11 +470,11 @@ impl Engine {
|
|||||||
optimization_level: OptimizationLevel::Full,
|
optimization_level: OptimizationLevel::Full,
|
||||||
|
|
||||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
engine.register_core_lib();
|
pub fn load_package(&mut self, package: PackageLibrary) {
|
||||||
|
self.packages.insert(0, package);
|
||||||
engine
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Control whether and how the `Engine` will optimize an AST after compilation
|
/// Control whether and how the `Engine` will optimize an AST after compilation
|
||||||
@ -571,7 +578,12 @@ impl Engine {
|
|||||||
// Search built-in's and external functions
|
// Search built-in's and external functions
|
||||||
let fn_spec = calc_fn_spec(fn_name, args.iter().map(|a| a.type_id()));
|
let fn_spec = calc_fn_spec(fn_name, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
if let Some(func) = self.functions.get(&fn_spec) {
|
if let Some(func) = self.functions.get(&fn_spec).or_else(|| {
|
||||||
|
self.packages
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.0.contains_key(&fn_spec))
|
||||||
|
.and_then(|p| p.0.get(&fn_spec))
|
||||||
|
}) {
|
||||||
// Run external function
|
// Run external function
|
||||||
let result = func(args, pos)?;
|
let result = func(args, pos)?;
|
||||||
|
|
||||||
@ -1540,7 +1552,12 @@ impl Engine {
|
|||||||
let arr = self.eval_expr(scope, fn_lib, expr, level)?;
|
let arr = self.eval_expr(scope, fn_lib, expr, level)?;
|
||||||
let tid = arr.type_id();
|
let tid = arr.type_id();
|
||||||
|
|
||||||
if let Some(iter_fn) = self.type_iterators.get(&tid) {
|
if let Some(iter_fn) = self.type_iterators.get(&tid).or_else(|| {
|
||||||
|
self.packages
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.1.contains_key(&tid))
|
||||||
|
.and_then(|p| p.1.get(&tid))
|
||||||
|
}) {
|
||||||
// Add the loop variable - variable name is copied
|
// Add the loop variable - variable name is copied
|
||||||
// TODO - avoid copying variable name
|
// TODO - avoid copying variable name
|
||||||
scope.push(name.clone(), ());
|
scope.push(name.clone(), ());
|
||||||
|
@ -117,10 +117,16 @@ pub struct Mut<T>(T);
|
|||||||
|
|
||||||
/// Identity dereferencing function.
|
/// Identity dereferencing function.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn identity<T>(data: T) -> T {
|
pub fn identity<T>(data: &mut T) -> &mut T {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clone dereferencing function.
|
||||||
|
#[inline]
|
||||||
|
pub fn cloned<T: Clone>(data: &mut T) -> T {
|
||||||
|
data.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// This macro counts the number of arguments via recursion.
|
/// This macro counts the number of arguments via recursion.
|
||||||
macro_rules! count_args {
|
macro_rules! count_args {
|
||||||
() => { 0_usize };
|
() => { 0_usize };
|
||||||
@ -128,7 +134,6 @@ macro_rules! count_args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This macro creates a closure wrapping a registered function.
|
/// This macro creates a closure wrapping a registered function.
|
||||||
#[macro_export]
|
|
||||||
macro_rules! make_func {
|
macro_rules! make_func {
|
||||||
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $clone:expr),*) => {
|
($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $clone:expr),*) => {
|
||||||
// ^ function name
|
// ^ function name
|
||||||
@ -254,8 +259,8 @@ macro_rules! def_register {
|
|||||||
//def_register!(imp_pop $($par => $mark => $param),*);
|
//def_register!(imp_pop $($par => $mark => $param),*);
|
||||||
};
|
};
|
||||||
($p0:ident $(, $p:ident)*) => {
|
($p0:ident $(, $p:ident)*) => {
|
||||||
def_register!(imp $p0 => $p0 => $p0 => Clone::clone $(, $p => $p => $p => Clone::clone)*);
|
def_register!(imp $p0 => $p0 => $p0 => cloned $(, $p => $p => $p => cloned)*);
|
||||||
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => Clone::clone)*);
|
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => cloned)*);
|
||||||
// handle the first parameter ^ first parameter passed through
|
// handle the first parameter ^ first parameter passed through
|
||||||
// ^ others passed by value (cloned)
|
// ^ others passed by value (cloned)
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ mod fn_call;
|
|||||||
mod fn_func;
|
mod fn_func;
|
||||||
mod fn_register;
|
mod fn_register;
|
||||||
mod optimize;
|
mod optimize;
|
||||||
|
pub mod packages;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod result;
|
mod result;
|
||||||
mod scope;
|
mod scope;
|
||||||
|
@ -3,6 +3,7 @@ use crate::engine::{
|
|||||||
calc_fn_spec, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL,
|
calc_fn_spec, Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL,
|
||||||
KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
|
use crate::packages::PackageLibrary;
|
||||||
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
@ -110,14 +111,23 @@ impl<'a> State<'a> {
|
|||||||
|
|
||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
fn call_fn(
|
fn call_fn(
|
||||||
|
packages: &Vec<PackageLibrary>,
|
||||||
functions: &HashMap<u64, Box<FnAny>>,
|
functions: &HashMap<u64, Box<FnAny>>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
||||||
// Search built-in's and external functions
|
// Search built-in's and external functions
|
||||||
|
let hash = calc_fn_spec(fn_name, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
functions
|
functions
|
||||||
.get(&calc_fn_spec(fn_name, args.iter().map(|a| a.type_id())))
|
.get(&hash)
|
||||||
|
.or_else(|| {
|
||||||
|
packages
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.0.contains_key(&hash))
|
||||||
|
.and_then(|p| p.0.get(&hash))
|
||||||
|
})
|
||||||
.map(|func| func(args, pos))
|
.map(|func| func(args, pos))
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
@ -576,7 +586,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
call_fn(&state.engine.functions, &id, &mut call_args, pos).ok()
|
call_fn(&state.engine.packages, &state.engine.functions, &id, &mut call_args, pos).ok()
|
||||||
.and_then(|result|
|
.and_then(|result|
|
||||||
result.or_else(|| {
|
result.or_else(|| {
|
||||||
if !arg_for_type_of.is_empty() {
|
if !arg_for_type_of.is_empty() {
|
||||||
|
442
src/packages/arithmetic.rs
Normal file
442
src/packages/arithmetic.rs
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_unary, Package, PackageLibrary, PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::fn_register::{map_dynamic as map, map_result as result};
|
||||||
|
use crate::parser::INT;
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
|
use num_traits::{
|
||||||
|
identities::Zero, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
|
||||||
|
CheckedShr, CheckedSub,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::stdlib::{
|
||||||
|
fmt::Display,
|
||||||
|
format,
|
||||||
|
ops::{Add, BitAnd, BitOr, BitXor, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub},
|
||||||
|
{i32, i64, u32},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checked add
|
||||||
|
fn add<T: Display + CheckedAdd>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
|
x.checked_add(&y).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Addition overflow: {} + {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Checked subtract
|
||||||
|
fn sub<T: Display + CheckedSub>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
|
x.checked_sub(&y).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Subtraction underflow: {} - {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Checked multiply
|
||||||
|
fn mul<T: Display + CheckedMul>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
|
x.checked_mul(&y).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Multiplication overflow: {} * {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Checked divide
|
||||||
|
fn div<T>(x: T, y: T) -> Result<T, EvalAltResult>
|
||||||
|
where
|
||||||
|
T: Display + CheckedDiv + PartialEq + Zero,
|
||||||
|
{
|
||||||
|
// Detect division by zero
|
||||||
|
if y == T::zero() {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Division by zero: {} / {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
x.checked_div(&y).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Division overflow: {} / {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Checked negative - e.g. -(i32::MIN) will overflow i32::MAX
|
||||||
|
fn neg<T: Display + CheckedNeg>(x: T) -> Result<T, EvalAltResult> {
|
||||||
|
x.checked_neg().ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Checked absolute
|
||||||
|
fn abs<T: Display + CheckedNeg + PartialOrd + Zero>(x: T) -> Result<T, EvalAltResult> {
|
||||||
|
// FIX - We don't use Signed::abs() here because, contrary to documentation, it panics
|
||||||
|
// when the number is ::MIN instead of returning ::MIN itself.
|
||||||
|
if x >= <T as Zero>::zero() {
|
||||||
|
Ok(x)
|
||||||
|
} else {
|
||||||
|
x.checked_neg().ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Unchecked add - may panic on overflow
|
||||||
|
fn add_u<T: Add>(x: T, y: T) -> <T as Add>::Output {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
// Unchecked subtract - may panic on underflow
|
||||||
|
fn sub_u<T: Sub>(x: T, y: T) -> <T as Sub>::Output {
|
||||||
|
x - y
|
||||||
|
}
|
||||||
|
// Unchecked multiply - may panic on overflow
|
||||||
|
fn mul_u<T: Mul>(x: T, y: T) -> <T as Mul>::Output {
|
||||||
|
x * y
|
||||||
|
}
|
||||||
|
// Unchecked divide - may panic when dividing by zero
|
||||||
|
fn div_u<T: Div>(x: T, y: T) -> <T as Div>::Output {
|
||||||
|
x / y
|
||||||
|
}
|
||||||
|
// Unchecked negative - may panic on overflow
|
||||||
|
fn neg_u<T: Neg>(x: T) -> <T as Neg>::Output {
|
||||||
|
-x
|
||||||
|
}
|
||||||
|
// Unchecked absolute - may panic on overflow
|
||||||
|
fn abs_u<T>(x: T) -> <T as Neg>::Output
|
||||||
|
where
|
||||||
|
T: Neg + PartialOrd + Default + Into<<T as Neg>::Output>,
|
||||||
|
{
|
||||||
|
// Numbers should default to zero
|
||||||
|
if x < Default::default() {
|
||||||
|
-x
|
||||||
|
} else {
|
||||||
|
x.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Bit operators
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// Checked left-shift
|
||||||
|
fn shl<T: Display + CheckedShl>(x: T, y: INT) -> Result<T, EvalAltResult> {
|
||||||
|
// Cannot shift by a negative number of bits
|
||||||
|
if y < 0 {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Left-shift by a negative number: {} << {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckedShl::checked_shl(&x, y as u32).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Left-shift by too many bits: {} << {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Checked right-shift
|
||||||
|
fn shr<T: Display + CheckedShr>(x: T, y: INT) -> Result<T, EvalAltResult> {
|
||||||
|
// Cannot shift by a negative number of bits
|
||||||
|
if y < 0 {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Right-shift by a negative number: {} >> {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckedShr::checked_shr(&x, y as u32).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Right-shift by too many bits: {} % {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Unchecked left-shift - may panic if shifting by a negative number of bits
|
||||||
|
fn shl_u<T: Shl<T>>(x: T, y: T) -> <T as Shl<T>>::Output {
|
||||||
|
x.shl(y)
|
||||||
|
}
|
||||||
|
// Unchecked right-shift - may panic if shifting by a negative number of bits
|
||||||
|
fn shr_u<T: Shr<T>>(x: T, y: T) -> <T as Shr<T>>::Output {
|
||||||
|
x.shr(y)
|
||||||
|
}
|
||||||
|
// Checked modulo
|
||||||
|
fn modulo<T: Display + CheckedRem>(x: T, y: T) -> Result<T, EvalAltResult> {
|
||||||
|
x.checked_rem(&y).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Modulo division by zero or overflow: {} % {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Unchecked modulo - may panic if dividing by zero
|
||||||
|
fn modulo_u<T: Rem>(x: T, y: T) -> <T as Rem>::Output {
|
||||||
|
x % y
|
||||||
|
}
|
||||||
|
// Checked power
|
||||||
|
fn pow_i_i(x: INT, y: INT) -> Result<INT, EvalAltResult> {
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
{
|
||||||
|
if y > (u32::MAX as INT) {
|
||||||
|
Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Integer raised to too large an index: {} ~ {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
} else if y < 0 {
|
||||||
|
Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Integer raised to a negative index: {} ~ {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
x.checked_pow(y as u32).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Power overflow: {} ~ {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
{
|
||||||
|
if y < 0 {
|
||||||
|
Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Integer raised to a negative index: {} ~ {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
x.checked_pow(y as u32).ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Power overflow: {} ~ {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Unchecked integer power - may panic on overflow or if the power index is too high (> u32::MAX)
|
||||||
|
fn pow_i_i_u(x: INT, y: INT) -> INT {
|
||||||
|
x.pow(y as u32)
|
||||||
|
}
|
||||||
|
// Floating-point power - always well-defined
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
fn pow_f_f(x: FLOAT, y: FLOAT) -> FLOAT {
|
||||||
|
x.powf(y)
|
||||||
|
}
|
||||||
|
// Checked power
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
fn pow_f_i(x: FLOAT, y: INT) -> Result<FLOAT, EvalAltResult> {
|
||||||
|
// Raise to power that is larger than an i32
|
||||||
|
if y > (i32::MAX as INT) {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Number raised to too large an index: {} ~ {}", x, y),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(x.powi(y as i32))
|
||||||
|
}
|
||||||
|
// Unchecked power - may be incorrect if the power index is too high (> i32::MAX)
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
fn pow_f_i_u(x: FLOAT, y: INT) -> FLOAT {
|
||||||
|
x.powi(y as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArithmeticPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for ArithmeticPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_unary_x { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_unary($lib, $op, $func::<$par>, result);)* };
|
||||||
|
}
|
||||||
|
macro_rules! reg_unary { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_unary($lib, $op, $func::<$par>, map);)* };
|
||||||
|
}
|
||||||
|
macro_rules! reg_op_x { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_binary($lib, $op, $func::<$par>, result);)* };
|
||||||
|
}
|
||||||
|
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_binary($lib, $op, $func::<$par>, map);)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for ArithmeticPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
// Checked basic arithmetic
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
reg_op_x!(lib, "+", add, INT);
|
||||||
|
reg_op_x!(lib, "-", sub, INT);
|
||||||
|
reg_op_x!(lib, "*", mul, INT);
|
||||||
|
reg_op_x!(lib, "/", div, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op_x!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op_x!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op_x!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op_x!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unchecked basic arithmetic
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "+", add_u, INT);
|
||||||
|
reg_op!(lib, "-", sub_u, INT);
|
||||||
|
reg_op!(lib, "*", mul_u, INT);
|
||||||
|
reg_op!(lib, "/", div_u, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic arithmetic for floating-point - no need to check
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "+", add_u, f32, f64);
|
||||||
|
reg_op!(lib, "-", sub_u, f32, f64);
|
||||||
|
reg_op!(lib, "*", mul_u, f32, f64);
|
||||||
|
reg_op!(lib, "/", div_u, f32, f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bit operations
|
||||||
|
reg_op!(lib, "|", binary_or, INT);
|
||||||
|
reg_op!(lib, "&", binary_and, INT);
|
||||||
|
reg_op!(lib, "^", binary_xor, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checked bit shifts
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
reg_op_x!(lib, "<<", shl, INT);
|
||||||
|
reg_op_x!(lib, ">>", shr, INT);
|
||||||
|
reg_op_x!(lib, "%", modulo, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op_x!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op_x!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op_x!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unchecked bit shifts
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "<<", shl_u, INT, INT);
|
||||||
|
reg_op!(lib, ">>", shr_u, INT, INT);
|
||||||
|
reg_op!(lib, "%", modulo_u, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checked power
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
reg_binary(lib, "~", pow_i_i, result);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
reg_binary(lib, "~", pow_f_i, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unchecked power
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
{
|
||||||
|
reg_binary(lib, "~", pow_i_i_u, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
reg_binary(lib, "~", pow_f_i_u, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floating-point modulo and power
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "%", modulo_u, f32, f64);
|
||||||
|
reg_binary(lib, "~", pow_f_f, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checked unary
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
reg_unary_x!(lib, "-", neg, INT);
|
||||||
|
reg_unary_x!(lib, "abs", abs, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_unary_x!(lib, "-", neg, i8, i16, i32, i64, i128);
|
||||||
|
reg_unary_x!(lib, "abs", abs, i8, i16, i32, i64, i128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unchecked unary
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
{
|
||||||
|
reg_unary!(lib, "-", neg_u, INT);
|
||||||
|
reg_unary!(lib, "abs", abs_u, INT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_unary!(lib, "-", neg_u, i8, i16, i32, i64, i128);
|
||||||
|
reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64, i128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floating-point unary
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_unary!(lib, "-", neg_u, f32, f64);
|
||||||
|
reg_unary!(lib, "abs", abs_u, f32, f64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
src/packages/array_basic.rs
Normal file
139
src/packages/array_basic.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary_mut, Package,
|
||||||
|
PackageLibrary, PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::any::{Dynamic, Variant};
|
||||||
|
use crate::engine::Array;
|
||||||
|
use crate::fn_register::{map_dynamic as map, map_identity as copy};
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::ops::Deref;
|
||||||
|
|
||||||
|
// Register array utility functions
|
||||||
|
fn push<T: Variant + Clone>(list: &mut Array, item: T) {
|
||||||
|
list.push(Dynamic::from(item));
|
||||||
|
}
|
||||||
|
fn ins<T: Variant + Clone>(list: &mut Array, position: INT, item: T) {
|
||||||
|
if position <= 0 {
|
||||||
|
list.insert(0, Dynamic::from(item));
|
||||||
|
} else if (position as usize) >= list.len() - 1 {
|
||||||
|
push(list, item);
|
||||||
|
} else {
|
||||||
|
list.insert(position as usize, Dynamic::from(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn pad<T: Variant + Clone>(list: &mut Array, len: INT, item: T) {
|
||||||
|
if len >= 0 {
|
||||||
|
while list.len() < len as usize {
|
||||||
|
push(list, item.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BasicArrayPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for BasicArrayPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_binary_mut($lib, $op, $func::<$par>, map);)* };
|
||||||
|
}
|
||||||
|
macro_rules! reg_tri { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_trinary_mut($lib, $op, $func::<$par>, map);)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for BasicArrayPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "push", push, INT, bool, char, String, Array, ());
|
||||||
|
reg_tri!(lib, "pad", pad, INT, bool, char, String, Array, ());
|
||||||
|
reg_tri!(lib, "insert", ins, INT, bool, char, String, Array, ());
|
||||||
|
|
||||||
|
reg_binary_mut(lib, "append", |x: &mut Array, y: Array| x.extend(y), map);
|
||||||
|
reg_binary(
|
||||||
|
lib,
|
||||||
|
"+",
|
||||||
|
|mut x: Array, y: Array| {
|
||||||
|
x.extend(y);
|
||||||
|
x
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "push", push, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_tri!(lib, "pad", pad, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
|
||||||
|
reg_tri!(lib, "insert", ins, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "push", push, f32, f64);
|
||||||
|
reg_tri!(lib, "pad", pad, f32, f64);
|
||||||
|
reg_tri!(lib, "insert", ins, f32, f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_unary_mut(
|
||||||
|
lib,
|
||||||
|
"pop",
|
||||||
|
|list: &mut Array| list.pop().unwrap_or_else(|| Dynamic::from_unit()),
|
||||||
|
copy,
|
||||||
|
);
|
||||||
|
reg_unary_mut(
|
||||||
|
lib,
|
||||||
|
"shift",
|
||||||
|
|list: &mut Array| {
|
||||||
|
if !list.is_empty() {
|
||||||
|
Dynamic::from_unit()
|
||||||
|
} else {
|
||||||
|
list.remove(0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copy,
|
||||||
|
);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"remove",
|
||||||
|
|list: &mut Array, len: INT| {
|
||||||
|
if len < 0 || (len as usize) >= list.len() {
|
||||||
|
Dynamic::from_unit()
|
||||||
|
} else {
|
||||||
|
list.remove(len as usize)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copy,
|
||||||
|
);
|
||||||
|
reg_unary_mut(lib, "len", |list: &mut Array| list.len() as INT, map);
|
||||||
|
reg_unary_mut(lib, "clear", |list: &mut Array| list.clear(), map);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"truncate",
|
||||||
|
|list: &mut Array, len: INT| {
|
||||||
|
if len >= 0 {
|
||||||
|
list.truncate(len as usize);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/packages/basic.rs
Normal file
33
src/packages/basic.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_binary_mut, reg_unary, Package, PackageLibrary,
|
||||||
|
PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::fn_register::map_dynamic as map;
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::ops::Deref;
|
||||||
|
|
||||||
|
pub struct BasicPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for BasicPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for BasicPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {}
|
||||||
|
}
|
173
src/packages/iter_basic.rs
Normal file
173
src/packages/iter_basic.rs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_trinary, reg_unary_mut, Package, PackageLibrary,
|
||||||
|
PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::any::{Dynamic, Union, Variant};
|
||||||
|
use crate::engine::{Array, Map};
|
||||||
|
use crate::fn_register::map_dynamic as map;
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::{
|
||||||
|
any::TypeId,
|
||||||
|
ops::{Add, Deref, Range},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register range function
|
||||||
|
fn reg_range<T: Variant + Clone>(lib: &mut PackageLibraryStore)
|
||||||
|
where
|
||||||
|
Range<T>: Iterator<Item = T>,
|
||||||
|
{
|
||||||
|
lib.1.insert(
|
||||||
|
TypeId::of::<Range<T>>(),
|
||||||
|
Box::new(|source: &Dynamic| {
|
||||||
|
Box::new(
|
||||||
|
source
|
||||||
|
.downcast_ref::<Range<T>>()
|
||||||
|
.cloned()
|
||||||
|
.unwrap()
|
||||||
|
.map(|x| x.into_dynamic()),
|
||||||
|
) as Box<dyn Iterator<Item = Dynamic>>
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register range function with step
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
|
struct StepRange<T>(T, T, T)
|
||||||
|
where
|
||||||
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
|
T: Variant + Clone + PartialOrd;
|
||||||
|
|
||||||
|
impl<T> Iterator for StepRange<T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
|
T: Variant + Clone + PartialOrd,
|
||||||
|
{
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<T> {
|
||||||
|
if self.0 < self.1 {
|
||||||
|
let v = self.0.clone();
|
||||||
|
self.0 = &v + &self.2;
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_step<T>(lib: &mut PackageLibraryStore)
|
||||||
|
where
|
||||||
|
for<'a> &'a T: Add<&'a T, Output = T>,
|
||||||
|
T: Variant + Clone + PartialOrd,
|
||||||
|
StepRange<T>: Iterator<Item = T>,
|
||||||
|
{
|
||||||
|
lib.1.insert(
|
||||||
|
TypeId::of::<StepRange<T>>(),
|
||||||
|
Box::new(|source: &Dynamic| {
|
||||||
|
Box::new(
|
||||||
|
source
|
||||||
|
.downcast_ref::<StepRange<T>>()
|
||||||
|
.cloned()
|
||||||
|
.unwrap()
|
||||||
|
.map(|x| x.into_dynamic()),
|
||||||
|
) as Box<dyn Iterator<Item = Dynamic>>
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BasicIteratorPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for BasicIteratorPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for BasicIteratorPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
{
|
||||||
|
// Register array iterator
|
||||||
|
lib.1.insert(
|
||||||
|
TypeId::of::<Array>(),
|
||||||
|
Box::new(|a: &Dynamic| {
|
||||||
|
Box::new(a.downcast_ref::<Array>().unwrap().clone().into_iter())
|
||||||
|
as Box<dyn Iterator<Item = Dynamic>>
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register map access functions
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
fn map_get_keys(map: &mut Map) -> Vec<Dynamic> {
|
||||||
|
map.iter()
|
||||||
|
.map(|(k, _)| Dynamic(Union::Str(Box::new(k.to_string()))))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
fn map_get_values(map: &mut Map) -> Vec<Dynamic> {
|
||||||
|
map.iter().map(|(_, v)| v.clone()).collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
reg_unary_mut(lib, "keys", map_get_keys, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
reg_unary_mut(lib, "values", map_get_values, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_range<T>(from: T, to: T) -> Range<T> {
|
||||||
|
from..to
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_range::<INT>(lib);
|
||||||
|
reg_binary(lib, "range", get_range::<INT>, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
macro_rules! reg_range {
|
||||||
|
($self:expr, $x:expr, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
reg_range::<$y>($self);
|
||||||
|
reg_binary($self, $x, get_range::<$y>, map);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_range!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_step::<INT>(lib);
|
||||||
|
reg_trinary(lib, "range", StepRange::<INT>, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
macro_rules! reg_step {
|
||||||
|
($self:expr, $x:expr, $( $y:ty ),*) => (
|
||||||
|
$(
|
||||||
|
reg_step::<$y>($self);
|
||||||
|
reg_trinary($self, $x, StepRange::<$y>, map);
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_step!(lib, "range", i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
src/packages/logic.rs
Normal file
113
src/packages/logic.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_binary_mut, reg_unary, Package, PackageLibrary,
|
||||||
|
PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::fn_register::map_dynamic as map;
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::ops::Deref;
|
||||||
|
|
||||||
|
// Comparison operators
|
||||||
|
pub fn lt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x < y
|
||||||
|
}
|
||||||
|
pub fn lte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x <= y
|
||||||
|
}
|
||||||
|
pub fn gt<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x > y
|
||||||
|
}
|
||||||
|
pub fn gte<T: PartialOrd>(x: T, y: T) -> bool {
|
||||||
|
x >= y
|
||||||
|
}
|
||||||
|
pub fn eq<T: PartialEq>(x: T, y: T) -> bool {
|
||||||
|
x == y
|
||||||
|
}
|
||||||
|
pub fn ne<T: PartialEq>(x: T, y: T) -> bool {
|
||||||
|
x != y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic operators
|
||||||
|
fn and(x: bool, y: bool) -> bool {
|
||||||
|
x && y
|
||||||
|
}
|
||||||
|
fn or(x: bool, y: bool) -> bool {
|
||||||
|
x || y
|
||||||
|
}
|
||||||
|
fn not(x: bool) -> bool {
|
||||||
|
!x
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_binary($lib, $op, $func::<$par>, map);)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LogicPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for LogicPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for LogicPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
reg_op!(lib, "<", lt, INT, char);
|
||||||
|
reg_op!(lib, "<=", lte, INT, char);
|
||||||
|
reg_op!(lib, ">", gt, INT, char);
|
||||||
|
reg_op!(lib, ">=", gte, INT, char);
|
||||||
|
reg_op!(lib, "==", eq, INT, char, bool, ());
|
||||||
|
reg_op!(lib, "!=", ne, INT, char, bool, ());
|
||||||
|
|
||||||
|
// Special versions for strings - at least avoid copying the first string
|
||||||
|
reg_binary_mut(lib, "<", |x: &mut String, y: String| *x < y, map);
|
||||||
|
reg_binary_mut(lib, "<=", |x: &mut String, y: String| *x <= y, map);
|
||||||
|
reg_binary_mut(lib, ">", |x: &mut String, y: String| *x > y, map);
|
||||||
|
reg_binary_mut(lib, ">=", |x: &mut String, y: String| *x >= y, map);
|
||||||
|
reg_binary_mut(lib, "==", |x: &mut String, y: String| *x == y, map);
|
||||||
|
reg_binary_mut(lib, "!=", |x: &mut String, y: String| *x != y, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "<", lt, f32, f64);
|
||||||
|
reg_op!(lib, "<=", lte, f32, f64);
|
||||||
|
reg_op!(lib, ">", gt, f32, f64);
|
||||||
|
reg_op!(lib, ">=", gte, f32, f64);
|
||||||
|
reg_op!(lib, "==", eq, f32, f64);
|
||||||
|
reg_op!(lib, "!=", ne, f32, f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `&&` and `||` are treated specially as they short-circuit.
|
||||||
|
// They are implemented as special `Expr` instances, not function calls.
|
||||||
|
//reg_op!(lib, "||", or, bool);
|
||||||
|
//reg_op!(lib, "&&", and, bool);
|
||||||
|
|
||||||
|
reg_binary(lib, "|", or, map);
|
||||||
|
reg_binary(lib, "&", and, map);
|
||||||
|
reg_unary(lib, "!", not, map);
|
||||||
|
}
|
||||||
|
}
|
75
src/packages/map_basic.rs
Normal file
75
src/packages/map_basic.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_binary_mut, reg_unary_mut, Package, PackageLibrary,
|
||||||
|
PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::any::Dynamic;
|
||||||
|
use crate::engine::Map;
|
||||||
|
use crate::fn_register::map_dynamic as map;
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::ops::Deref;
|
||||||
|
|
||||||
|
pub struct BasicMapPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for BasicMapPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for BasicMapPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
// Register map functions
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"has",
|
||||||
|
|map: &mut Map, prop: String| map.contains_key(&prop),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_unary_mut(lib, "len", |map: &mut Map| map.len() as INT, map);
|
||||||
|
reg_unary_mut(lib, "clear", |map: &mut Map| map.clear(), map);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"remove",
|
||||||
|
|x: &mut Map, name: String| x.remove(&name).unwrap_or_else(|| Dynamic::from_unit()),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"mixin",
|
||||||
|
|map1: &mut Map, map2: Map| {
|
||||||
|
map2.into_iter().for_each(|(key, value)| {
|
||||||
|
map1.insert(key, value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary(
|
||||||
|
lib,
|
||||||
|
"+",
|
||||||
|
|mut map1: Map, map2: Map| {
|
||||||
|
map2.into_iter().for_each(|(key, value)| {
|
||||||
|
map1.insert(key, value);
|
||||||
|
});
|
||||||
|
map1
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
154
src/packages/math_basic.rs
Normal file
154
src/packages/math_basic.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_unary, Package, PackageLibrary, PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::fn_register::{map_dynamic as map, map_result as result};
|
||||||
|
use crate::parser::INT;
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
|
use crate::stdlib::{i32, i64, ops::Deref};
|
||||||
|
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
const MAX_INT: INT = i32::MAX;
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
const MAX_INT: INT = i64::MAX;
|
||||||
|
|
||||||
|
pub struct BasicMathPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for BasicMathPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for BasicMathPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
// Advanced math functions
|
||||||
|
reg_unary(lib, "sin", |x: FLOAT| x.to_radians().sin(), map);
|
||||||
|
reg_unary(lib, "cos", |x: FLOAT| x.to_radians().cos(), map);
|
||||||
|
reg_unary(lib, "tan", |x: FLOAT| x.to_radians().tan(), map);
|
||||||
|
reg_unary(lib, "sinh", |x: FLOAT| x.to_radians().sinh(), map);
|
||||||
|
reg_unary(lib, "cosh", |x: FLOAT| x.to_radians().cosh(), map);
|
||||||
|
reg_unary(lib, "tanh", |x: FLOAT| x.to_radians().tanh(), map);
|
||||||
|
reg_unary(lib, "asin", |x: FLOAT| x.asin().to_degrees(), map);
|
||||||
|
reg_unary(lib, "acos", |x: FLOAT| x.acos().to_degrees(), map);
|
||||||
|
reg_unary(lib, "atan", |x: FLOAT| x.atan().to_degrees(), map);
|
||||||
|
reg_unary(lib, "asinh", |x: FLOAT| x.asinh().to_degrees(), map);
|
||||||
|
reg_unary(lib, "acosh", |x: FLOAT| x.acosh().to_degrees(), map);
|
||||||
|
reg_unary(lib, "atanh", |x: FLOAT| x.atanh().to_degrees(), map);
|
||||||
|
reg_unary(lib, "sqrt", |x: FLOAT| x.sqrt(), map);
|
||||||
|
reg_unary(lib, "exp", |x: FLOAT| x.exp(), map);
|
||||||
|
reg_unary(lib, "ln", |x: FLOAT| x.ln(), map);
|
||||||
|
reg_binary(lib, "log", |x: FLOAT, base: FLOAT| x.log(base), map);
|
||||||
|
reg_unary(lib, "log10", |x: FLOAT| x.log10(), map);
|
||||||
|
reg_unary(lib, "floor", |x: FLOAT| x.floor(), map);
|
||||||
|
reg_unary(lib, "ceiling", |x: FLOAT| x.ceil(), map);
|
||||||
|
reg_unary(lib, "round", |x: FLOAT| x.ceil(), map);
|
||||||
|
reg_unary(lib, "int", |x: FLOAT| x.trunc(), map);
|
||||||
|
reg_unary(lib, "fraction", |x: FLOAT| x.fract(), map);
|
||||||
|
reg_unary(lib, "is_nan", |x: FLOAT| x.is_nan(), map);
|
||||||
|
reg_unary(lib, "is_finite", |x: FLOAT| x.is_finite(), map);
|
||||||
|
reg_unary(lib, "is_infinite", |x: FLOAT| x.is_infinite(), map);
|
||||||
|
|
||||||
|
// Register conversion functions
|
||||||
|
reg_unary(lib, "to_float", |x: INT| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: f32| x as FLOAT, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_unary(lib, "to_float", |x: i8| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: u8| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: i16| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: u16| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: i32| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: u32| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: i64| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: u64| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: i128| x as FLOAT, map);
|
||||||
|
reg_unary(lib, "to_float", |x: u128| x as FLOAT, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_unary(lib, "to_int", |ch: char| ch as INT, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_unary(lib, "to_int", |x: i8| x as INT, map);
|
||||||
|
reg_unary(lib, "to_int", |x: u8| x as INT, map);
|
||||||
|
reg_unary(lib, "to_int", |x: i16| x as INT, map);
|
||||||
|
reg_unary(lib, "to_int", |x: u16| x as INT, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
{
|
||||||
|
reg_unary(lib, "to_int", |x: i32| x as INT, map);
|
||||||
|
reg_unary(lib, "to_int", |x: u64| x as INT, map);
|
||||||
|
|
||||||
|
#[cfg(feature = "only_i64")]
|
||||||
|
reg_unary(lib, "to_int", |x: u32| x as INT, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
reg_unary(
|
||||||
|
lib,
|
||||||
|
"to_int",
|
||||||
|
|x: f32| {
|
||||||
|
if x > (MAX_INT as f32) {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Integer overflow: to_int({})", x),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(x.trunc() as INT)
|
||||||
|
},
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
reg_unary(
|
||||||
|
lib,
|
||||||
|
"to_int",
|
||||||
|
|x: FLOAT| {
|
||||||
|
if x > (MAX_INT as FLOAT) {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Integer overflow: to_int({})", x),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(x.trunc() as INT)
|
||||||
|
},
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
{
|
||||||
|
reg_unary(lib, "to_int", |x: f32| x as INT, map);
|
||||||
|
reg_unary(lib, "to_int", |x: f64| x as INT, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
307
src/packages/mod.rs
Normal file
307
src/packages/mod.rs
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
use crate::any::{Dynamic, Variant};
|
||||||
|
use crate::engine::{calc_fn_spec, FnAny, FnCallArgs, IteratorFn};
|
||||||
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::token::Position;
|
||||||
|
|
||||||
|
use crate::stdlib::{
|
||||||
|
any::{type_name, TypeId},
|
||||||
|
boxed::Box,
|
||||||
|
collections::HashMap,
|
||||||
|
ops::Deref,
|
||||||
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod arithmetic;
|
||||||
|
mod array_basic;
|
||||||
|
mod iter_basic;
|
||||||
|
mod logic;
|
||||||
|
mod map_basic;
|
||||||
|
mod math_basic;
|
||||||
|
mod pkg_core;
|
||||||
|
mod pkg_std;
|
||||||
|
mod string_basic;
|
||||||
|
mod string_more;
|
||||||
|
mod time_basic;
|
||||||
|
|
||||||
|
pub use arithmetic::ArithmeticPackage;
|
||||||
|
pub use array_basic::BasicArrayPackage;
|
||||||
|
pub use iter_basic::BasicIteratorPackage;
|
||||||
|
pub use logic::LogicPackage;
|
||||||
|
pub use map_basic::BasicMapPackage;
|
||||||
|
pub use math_basic::BasicMathPackage;
|
||||||
|
pub use pkg_core::CorePackage;
|
||||||
|
pub use pkg_std::StandardPackage;
|
||||||
|
pub use string_basic::BasicStringPackage;
|
||||||
|
pub use string_more::MoreStringPackage;
|
||||||
|
pub use time_basic::BasicTimePackage;
|
||||||
|
|
||||||
|
pub trait Package: Deref {
|
||||||
|
fn new() -> Self;
|
||||||
|
fn init(lib: &mut PackageLibraryStore);
|
||||||
|
fn get(&self) -> PackageLibrary;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PackageLibraryStore = (HashMap<u64, Box<FnAny>>, HashMap<TypeId, Box<IteratorFn>>);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
pub type PackageLibrary = Rc<PackageLibraryStore>;
|
||||||
|
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
pub type PackageLibrary = Arc<PackageLibraryStore>;
|
||||||
|
|
||||||
|
fn check_num_args(
|
||||||
|
name: &str,
|
||||||
|
num_args: usize,
|
||||||
|
args: &mut FnCallArgs,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
if args.len() != num_args {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch(
|
||||||
|
name.to_string(),
|
||||||
|
num_args,
|
||||||
|
args.len(),
|
||||||
|
pos,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_new_package() -> PackageLibraryStore {
|
||||||
|
(HashMap::new(), HashMap::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_none<R>(
|
||||||
|
lib: &mut PackageLibraryStore,
|
||||||
|
fn_name: &'static str,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> R + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn() -> R + Send + Sync + 'static,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
let hash = calc_fn_spec(fn_name, ([] as [TypeId; 0]).iter().cloned());
|
||||||
|
|
||||||
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
|
check_num_args(fn_name, 0, args, pos)?;
|
||||||
|
|
||||||
|
let r = func();
|
||||||
|
map_result(r, pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.0.insert(hash, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_unary<T: Variant + Clone, R>(
|
||||||
|
lib: &mut PackageLibraryStore,
|
||||||
|
fn_name: &'static str,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(T) -> R + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(T) -> R + Send + Sync + 'static,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
//println!("register {}({})", fn_name, type_name::<T>());
|
||||||
|
|
||||||
|
let hash = calc_fn_spec(fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||||
|
|
||||||
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
|
check_num_args(fn_name, 1, args, pos)?;
|
||||||
|
|
||||||
|
let mut drain = args.iter_mut();
|
||||||
|
let x: &mut T = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
|
||||||
|
let r = func(x.clone());
|
||||||
|
map_result(r, pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.0.insert(hash, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_unary_mut<T: Variant + Clone, R>(
|
||||||
|
lib: &mut PackageLibraryStore,
|
||||||
|
fn_name: &'static str,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut T) -> R + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(&mut T) -> R + Send + Sync + 'static,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
//println!("register {}(&mut {})", fn_name, type_name::<T>());
|
||||||
|
|
||||||
|
let hash = calc_fn_spec(fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||||
|
|
||||||
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
|
check_num_args(fn_name, 1, args, pos)?;
|
||||||
|
|
||||||
|
let mut drain = args.iter_mut();
|
||||||
|
let x: &mut T = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
|
||||||
|
let r = func(x);
|
||||||
|
map_result(r, pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.0.insert(hash, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
|
||||||
|
lib: &mut PackageLibraryStore,
|
||||||
|
fn_name: &'static str,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> R + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(A, B) -> R + Send + Sync + 'static,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
//println!("register {}({}, {})", fn_name, type_name::<A>(), type_name::<B>());
|
||||||
|
|
||||||
|
let hash = calc_fn_spec(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
|
check_num_args(fn_name, 2, args, pos)?;
|
||||||
|
|
||||||
|
let mut drain = args.iter_mut();
|
||||||
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
|
||||||
|
let r = func(x.clone(), y.clone());
|
||||||
|
map_result(r, pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.0.insert(hash, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
|
||||||
|
lib: &mut PackageLibraryStore,
|
||||||
|
fn_name: &'static str,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> R + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> R + Send + Sync + 'static,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
//println!("register {}(&mut {}, {})", fn_name, type_name::<A>(), type_name::<B>());
|
||||||
|
|
||||||
|
let hash = calc_fn_spec(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
|
check_num_args(fn_name, 2, args, pos)?;
|
||||||
|
|
||||||
|
let mut drain = args.iter_mut();
|
||||||
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
|
||||||
|
let r = func(x, y.clone());
|
||||||
|
map_result(r, pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.0.insert(hash, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
|
||||||
|
lib: &mut PackageLibraryStore,
|
||||||
|
fn_name: &'static str,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> R + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> R + Send + Sync + 'static,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
//println!("register {}({}, {}, {})", fn_name, type_name::<A>(), type_name::<B>(), type_name::<C>());
|
||||||
|
|
||||||
|
let hash = calc_fn_spec(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||||
|
.iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
|
check_num_args(fn_name, 3, args, pos)?;
|
||||||
|
|
||||||
|
let mut drain = args.iter_mut();
|
||||||
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
let z: &mut C = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
|
||||||
|
let r = func(x.clone(), y.clone(), z.clone());
|
||||||
|
map_result(r, pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.0.insert(hash, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
|
||||||
|
lib: &mut PackageLibraryStore,
|
||||||
|
fn_name: &'static str,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> R + 'static,
|
||||||
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> R + Send + Sync + 'static,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ 'static,
|
||||||
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
) {
|
||||||
|
//println!("register {}(&mut {}, {}, {})", fn_name, type_name::<A>(), type_name::<B>(), type_name::<C>());
|
||||||
|
|
||||||
|
let hash = calc_fn_spec(
|
||||||
|
fn_name,
|
||||||
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||||
|
.iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
|
check_num_args(fn_name, 3, args, pos)?;
|
||||||
|
|
||||||
|
let mut drain = args.iter_mut();
|
||||||
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
let z: &mut C = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
|
|
||||||
|
let r = func(x, y.clone(), z.clone());
|
||||||
|
map_result(r, pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.0.insert(hash, f);
|
||||||
|
}
|
37
src/packages/pkg_core.rs
Normal file
37
src/packages/pkg_core.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use super::arithmetic::ArithmeticPackage;
|
||||||
|
use super::create_new_package;
|
||||||
|
use super::iter_basic::BasicIteratorPackage;
|
||||||
|
use super::logic::LogicPackage;
|
||||||
|
use super::string_basic::BasicStringPackage;
|
||||||
|
use super::{Package, PackageLibrary, PackageLibraryStore};
|
||||||
|
|
||||||
|
use crate::stdlib::ops::Deref;
|
||||||
|
|
||||||
|
pub struct CorePackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for CorePackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for CorePackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
ArithmeticPackage::init(lib);
|
||||||
|
LogicPackage::init(lib);
|
||||||
|
BasicStringPackage::init(lib);
|
||||||
|
BasicIteratorPackage::init(lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
}
|
40
src/packages/pkg_std.rs
Normal file
40
src/packages/pkg_std.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use super::array_basic::BasicArrayPackage;
|
||||||
|
use super::map_basic::BasicMapPackage;
|
||||||
|
use super::math_basic::BasicMathPackage;
|
||||||
|
use super::pkg_core::CorePackage;
|
||||||
|
use super::string_more::MoreStringPackage;
|
||||||
|
use super::time_basic::BasicTimePackage;
|
||||||
|
use super::{create_new_package, Package, PackageLibrary, PackageLibraryStore};
|
||||||
|
|
||||||
|
use crate::stdlib::ops::Deref;
|
||||||
|
|
||||||
|
pub struct StandardPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for StandardPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for StandardPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
CorePackage::init(lib);
|
||||||
|
BasicMathPackage::init(lib);
|
||||||
|
BasicArrayPackage::init(lib);
|
||||||
|
BasicMapPackage::init(lib);
|
||||||
|
BasicTimePackage::init(lib);
|
||||||
|
MoreStringPackage::init(lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
}
|
124
src/packages/string_basic.rs
Normal file
124
src/packages/string_basic.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_binary_mut, reg_none, reg_unary, reg_unary_mut, Package,
|
||||||
|
PackageLibrary, PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::engine::{Array, Map, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
||||||
|
use crate::fn_register::map_dynamic as map;
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::{
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
format,
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register print and debug
|
||||||
|
fn to_debug<T: Debug>(x: &mut T) -> String {
|
||||||
|
format!("{:?}", x)
|
||||||
|
}
|
||||||
|
fn to_string<T: Display>(x: &mut T) -> String {
|
||||||
|
format!("{}", x)
|
||||||
|
}
|
||||||
|
fn format_map(x: &mut Map) -> String {
|
||||||
|
format!("#{:?}", x)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_unary_mut($lib, $op, $func::<$par>, map);)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BasicStringPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for BasicStringPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for BasicStringPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
reg_op!(lib, KEYWORD_PRINT, to_string, String, INT, bool);
|
||||||
|
reg_op!(lib, FUNC_TO_STRING, to_string, String, INT, bool);
|
||||||
|
reg_op!(lib, KEYWORD_PRINT, to_string, String, char, String);
|
||||||
|
reg_op!(lib, FUNC_TO_STRING, to_string, String, char, String);
|
||||||
|
reg_none(lib, KEYWORD_PRINT, || "".to_string(), map);
|
||||||
|
reg_unary(lib, KEYWORD_PRINT, |_: ()| "".to_string(), map);
|
||||||
|
reg_unary(lib, FUNC_TO_STRING, |_: ()| "".to_string(), map);
|
||||||
|
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, INT, bool, ());
|
||||||
|
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, char, String);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, KEYWORD_PRINT, to_string, String, i8, u8, i16, u16);
|
||||||
|
reg_op!(lib, FUNC_TO_STRING, to_string, String, i8, u8, i16, u16);
|
||||||
|
reg_op!(lib, KEYWORD_PRINT, to_string, String, i32, u32, i64, u64);
|
||||||
|
reg_op!(lib, FUNC_TO_STRING, to_string, String, i32, u32, i64, u64);
|
||||||
|
reg_op!(lib, KEYWORD_PRINT, to_string, String, i128, u128);
|
||||||
|
reg_op!(lib, FUNC_TO_STRING, to_string, String, i128, u128);
|
||||||
|
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, i8, u8, i16, u16);
|
||||||
|
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, i32, u32, i64, u64);
|
||||||
|
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, KEYWORD_PRINT, to_string, String, f32, f64);
|
||||||
|
reg_op!(lib, FUNC_TO_STRING, to_string, String, f32, f64);
|
||||||
|
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, f32, f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, KEYWORD_PRINT, to_debug, String, Array);
|
||||||
|
reg_op!(lib, FUNC_TO_STRING, to_debug, String, Array);
|
||||||
|
reg_op!(lib, KEYWORD_DEBUG, to_debug, String, Array);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
reg_unary_mut(lib, KEYWORD_PRINT, format_map, map);
|
||||||
|
reg_unary_mut(lib, FUNC_TO_STRING, format_map, map);
|
||||||
|
reg_unary_mut(lib, KEYWORD_DEBUG, format_map, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_binary(
|
||||||
|
lib,
|
||||||
|
"+",
|
||||||
|
|mut s: String, ch: char| {
|
||||||
|
s.push(ch);
|
||||||
|
s
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary(
|
||||||
|
lib,
|
||||||
|
"+",
|
||||||
|
|mut s: String, s2: String| {
|
||||||
|
s.push_str(&s2);
|
||||||
|
s
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"append",
|
||||||
|
|s: &mut String, add: String| s.push_str(&add),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
259
src/packages/string_more.rs
Normal file
259
src/packages/string_more.rs
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_binary_mut, reg_trinary_mut, reg_unary, reg_unary_mut,
|
||||||
|
Package, PackageLibrary, PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::engine::Array;
|
||||||
|
use crate::fn_register::map_dynamic as map;
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::{fmt::Display, ops::Deref};
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
fn sub_string(s: &mut String, start: INT, len: INT) -> String {
|
||||||
|
let offset = if s.is_empty() || len <= 0 {
|
||||||
|
return "".to_string();
|
||||||
|
} else if start < 0 {
|
||||||
|
0
|
||||||
|
} else if (start as usize) >= s.chars().count() {
|
||||||
|
return "".to_string();
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let chars: Vec<_> = s.chars().collect();
|
||||||
|
|
||||||
|
let len = if offset + (len as usize) > chars.len() {
|
||||||
|
chars.len() - offset
|
||||||
|
} else {
|
||||||
|
len as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
chars[offset..][..len].into_iter().collect::<String>()
|
||||||
|
}
|
||||||
|
fn crop_string(s: &mut String, start: INT, len: INT) {
|
||||||
|
let offset = if s.is_empty() || len <= 0 {
|
||||||
|
s.clear();
|
||||||
|
return;
|
||||||
|
} else if start < 0 {
|
||||||
|
0
|
||||||
|
} else if (start as usize) >= s.chars().count() {
|
||||||
|
s.clear();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let chars: Vec<_> = s.chars().collect();
|
||||||
|
|
||||||
|
let len = if offset + (len as usize) > chars.len() {
|
||||||
|
chars.len() - offset
|
||||||
|
} else {
|
||||||
|
len as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
s.clear();
|
||||||
|
|
||||||
|
chars[offset..][..len]
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|&ch| s.push(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MoreStringPackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for MoreStringPackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reg_op { ($lib:expr, $op:expr, $func:ident, $($par:ty),*) => {
|
||||||
|
$(reg_binary($lib, $op, $func::<$par>, map);)* };
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for MoreStringPackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
reg_op!(lib, "+", append, INT, bool, char);
|
||||||
|
reg_binary_mut(lib, "+", |x: &mut String, _: ()| x.clone(), map);
|
||||||
|
|
||||||
|
reg_op!(lib, "+", prepend, INT, bool, char);
|
||||||
|
reg_binary(lib, "+", |_: (), y: String| y, map);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "+", append, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "+", prepend, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "+", append, f32, f64);
|
||||||
|
reg_op!(lib, "+", prepend, f32, f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
{
|
||||||
|
reg_binary(lib, "+", |x: String, y: Array| format!("{}{:?}", x, y), map);
|
||||||
|
reg_binary(lib, "+", |x: Array, y: String| format!("{:?}{}", x, y), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_unary_mut(lib, "len", |s: &mut String| s.chars().count() as INT, map);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"contains",
|
||||||
|
|s: &mut String, ch: char| s.contains(ch),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"contains",
|
||||||
|
|s: &mut String, find: String| s.contains(&find),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_trinary_mut(
|
||||||
|
lib,
|
||||||
|
"index_of",
|
||||||
|
|s: &mut String, ch: char, start: INT| {
|
||||||
|
let start = if start < 0 {
|
||||||
|
0
|
||||||
|
} else if (start as usize) >= s.chars().count() {
|
||||||
|
return -1 as INT;
|
||||||
|
} else {
|
||||||
|
s.chars().take(start as usize).collect::<String>().len()
|
||||||
|
};
|
||||||
|
|
||||||
|
s[start..]
|
||||||
|
.find(ch)
|
||||||
|
.map(|index| s[0..start + index].chars().count() as INT)
|
||||||
|
.unwrap_or(-1 as INT)
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"index_of",
|
||||||
|
|s: &mut String, ch: char| {
|
||||||
|
s.find(ch)
|
||||||
|
.map(|index| s[0..index].chars().count() as INT)
|
||||||
|
.unwrap_or(-1 as INT)
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_trinary_mut(
|
||||||
|
lib,
|
||||||
|
"index_of",
|
||||||
|
|s: &mut String, find: String, start: INT| {
|
||||||
|
let start = if start < 0 {
|
||||||
|
0
|
||||||
|
} else if (start as usize) >= s.chars().count() {
|
||||||
|
return -1 as INT;
|
||||||
|
} else {
|
||||||
|
s.chars().take(start as usize).collect::<String>().len()
|
||||||
|
};
|
||||||
|
|
||||||
|
s[start..]
|
||||||
|
.find(&find)
|
||||||
|
.map(|index| s[0..start + index].chars().count() as INT)
|
||||||
|
.unwrap_or(-1 as INT)
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"index_of",
|
||||||
|
|s: &mut String, find: String| {
|
||||||
|
s.find(&find)
|
||||||
|
.map(|index| s[0..index].chars().count() as INT)
|
||||||
|
.unwrap_or(-1 as INT)
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_unary_mut(lib, "clear", |s: &mut String| s.clear(), map);
|
||||||
|
reg_binary_mut(lib, "append", |s: &mut String, ch: char| s.push(ch), map);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"append",
|
||||||
|
|s: &mut String, add: String| s.push_str(&add),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_trinary_mut(lib, "sub_string", sub_string, map);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"sub_string",
|
||||||
|
|s: &mut String, start: INT| sub_string(s, start, s.len() as INT),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_trinary_mut(lib, "crop", crop_string, map);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"crop",
|
||||||
|
|s: &mut String, start: INT| crop_string(s, start, s.len() as INT),
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_binary_mut(
|
||||||
|
lib,
|
||||||
|
"truncate",
|
||||||
|
|s: &mut String, len: INT| {
|
||||||
|
if len >= 0 {
|
||||||
|
let chars: Vec<_> = s.chars().take(len as usize).collect();
|
||||||
|
s.clear();
|
||||||
|
chars.into_iter().for_each(|ch| s.push(ch));
|
||||||
|
} else {
|
||||||
|
s.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_trinary_mut(
|
||||||
|
lib,
|
||||||
|
"pad",
|
||||||
|
|s: &mut String, len: INT, ch: char| {
|
||||||
|
for _ in 0..s.chars().count() - len as usize {
|
||||||
|
s.push(ch);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_trinary_mut(
|
||||||
|
lib,
|
||||||
|
"replace",
|
||||||
|
|s: &mut String, find: String, sub: String| {
|
||||||
|
let new_str = s.replace(&find, &sub);
|
||||||
|
s.clear();
|
||||||
|
s.push_str(&new_str);
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
reg_unary_mut(
|
||||||
|
lib,
|
||||||
|
"trim",
|
||||||
|
|s: &mut String| {
|
||||||
|
let trimmed = s.trim();
|
||||||
|
|
||||||
|
if trimmed.len() < s.len() {
|
||||||
|
*s = trimmed.to_string();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
126
src/packages/time_basic.rs
Normal file
126
src/packages/time_basic.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use super::logic::{eq, gt, gte, lt, lte, ne};
|
||||||
|
use super::{
|
||||||
|
create_new_package, reg_binary, reg_none, reg_unary, Package, PackageLibrary,
|
||||||
|
PackageLibraryStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::fn_register::{map_dynamic as map, map_result as result};
|
||||||
|
use crate::parser::INT;
|
||||||
|
|
||||||
|
use crate::stdlib::{ops::Deref, time::Instant};
|
||||||
|
|
||||||
|
pub struct BasicTimePackage(PackageLibrary);
|
||||||
|
|
||||||
|
impl Deref for BasicTimePackage {
|
||||||
|
type Target = PackageLibrary;
|
||||||
|
|
||||||
|
fn deref(&self) -> &PackageLibrary {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package for BasicTimePackage {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut pkg = create_new_package();
|
||||||
|
Self::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> PackageLibrary {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(lib: &mut PackageLibraryStore) {
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
{
|
||||||
|
// Register date/time functions
|
||||||
|
reg_none(lib, "timestamp", || Instant::now(), map);
|
||||||
|
|
||||||
|
reg_binary(
|
||||||
|
lib,
|
||||||
|
"-",
|
||||||
|
|ts1: Instant, ts2: Instant| {
|
||||||
|
if ts2 > ts1 {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
return Ok(-(ts2 - ts1).as_secs_f64());
|
||||||
|
|
||||||
|
#[cfg(feature = "no_float")]
|
||||||
|
{
|
||||||
|
let seconds = (ts2 - ts1).as_secs();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
if seconds > (MAX_INT as u64) {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!(
|
||||||
|
"Integer overflow for timestamp duration: {}",
|
||||||
|
-(seconds as i64)
|
||||||
|
),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(-(seconds as INT));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
return Ok((ts1 - ts2).as_secs_f64());
|
||||||
|
|
||||||
|
#[cfg(feature = "no_float")]
|
||||||
|
{
|
||||||
|
let seconds = (ts1 - ts2).as_secs();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
if seconds > (MAX_INT as u64) {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!(
|
||||||
|
"Integer overflow for timestamp duration: {}",
|
||||||
|
seconds
|
||||||
|
),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(seconds as INT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_binary(lib, "<", lt::<Instant>, map);
|
||||||
|
reg_binary(lib, "<=", lte::<Instant>, map);
|
||||||
|
reg_binary(lib, ">", gt::<Instant>, map);
|
||||||
|
reg_binary(lib, ">=", gte::<Instant>, map);
|
||||||
|
reg_binary(lib, "==", eq::<Instant>, map);
|
||||||
|
reg_binary(lib, "!=", ne::<Instant>, map);
|
||||||
|
|
||||||
|
reg_unary(
|
||||||
|
lib,
|
||||||
|
"elapsed",
|
||||||
|
|timestamp: Instant| {
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
return Ok(timestamp.elapsed().as_secs_f64());
|
||||||
|
|
||||||
|
#[cfg(feature = "no_float")]
|
||||||
|
{
|
||||||
|
let seconds = timestamp.elapsed().as_secs();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
{
|
||||||
|
if seconds > (MAX_INT as u64) {
|
||||||
|
return Err(EvalAltResult::ErrorArithmetic(
|
||||||
|
format!("Integer overflow for timestamp.elapsed(): {}", seconds),
|
||||||
|
Position::none(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(seconds as INT);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -549,6 +549,8 @@ fn parse_paren_expr<'a>(
|
|||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
// ( xxx )
|
// ( xxx )
|
||||||
(Token::RightParen, _) => Ok(expr),
|
(Token::RightParen, _) => Ok(expr),
|
||||||
|
// ( <error>
|
||||||
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
||||||
// ( xxx ???
|
// ( xxx ???
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
(_, pos) => Err(PERR::MissingToken(
|
||||||
")".into(),
|
")".into(),
|
||||||
@ -568,7 +570,7 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
let mut args_expr_list = Vec::new();
|
let mut args_expr_list = Vec::new();
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
//id {EOF}
|
// id <EOF>
|
||||||
(Token::EOF, pos) => {
|
(Token::EOF, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
")".into(),
|
")".into(),
|
||||||
@ -576,6 +578,8 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
|
// id <error>
|
||||||
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)),
|
||||||
// id()
|
// id()
|
||||||
(Token::RightParen, _) => {
|
(Token::RightParen, _) => {
|
||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
@ -589,6 +593,13 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
args_expr_list.push(parse_expr(input, allow_stmt_expr)?);
|
args_expr_list.push(parse_expr(input, allow_stmt_expr)?);
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
|
(Token::RightParen, _) => {
|
||||||
|
eat_token(input, Token::RightParen);
|
||||||
|
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
||||||
|
}
|
||||||
|
(Token::Comma, _) => {
|
||||||
|
eat_token(input, Token::Comma);
|
||||||
|
}
|
||||||
(Token::EOF, pos) => {
|
(Token::EOF, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
")".into(),
|
")".into(),
|
||||||
@ -596,12 +607,8 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
|
|||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
(Token::RightParen, _) => {
|
(Token::LexError(err), pos) => {
|
||||||
eat_token(input, Token::RightParen);
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
||||||
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
|
|
||||||
}
|
|
||||||
(Token::Comma, _) => {
|
|
||||||
eat_token(input, Token::Comma);
|
|
||||||
}
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
@ -731,6 +738,7 @@ fn parse_index_expr<'a>(
|
|||||||
eat_token(input, Token::RightBracket);
|
eat_token(input, Token::RightBracket);
|
||||||
Ok(Expr::Index(lhs, Box::new(idx_expr), pos))
|
Ok(Expr::Index(lhs, Box::new(idx_expr), pos))
|
||||||
}
|
}
|
||||||
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)),
|
||||||
(_, pos) => Err(PERR::MissingToken(
|
(_, pos) => Err(PERR::MissingToken(
|
||||||
"]".into(),
|
"]".into(),
|
||||||
"for a matching [ in this index expression".into(),
|
"for a matching [ in this index expression".into(),
|
||||||
@ -752,16 +760,19 @@ fn parse_array_literal<'a>(
|
|||||||
arr.push(parse_expr(input, allow_stmt_expr)?);
|
arr.push(parse_expr(input, allow_stmt_expr)?);
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
|
(Token::Comma, _) => eat_token(input, Token::Comma),
|
||||||
|
(Token::RightBracket, _) => {
|
||||||
|
eat_token(input, Token::RightBracket);
|
||||||
|
break;
|
||||||
|
}
|
||||||
(Token::EOF, pos) => {
|
(Token::EOF, pos) => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::MissingToken("]".into(), "to end this array literal".into())
|
PERR::MissingToken("]".into(), "to end this array literal".into())
|
||||||
.into_err(*pos),
|
.into_err(*pos),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(Token::Comma, _) => eat_token(input, Token::Comma),
|
(Token::LexError(err), pos) => {
|
||||||
(Token::RightBracket, _) => {
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
||||||
eat_token(input, Token::RightBracket);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
@ -792,6 +803,9 @@ fn parse_map_literal<'a>(
|
|||||||
let (name, pos) = match input.next().unwrap() {
|
let (name, pos) = match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => (s, pos),
|
(Token::Identifier(s), pos) => (s, pos),
|
||||||
(Token::StringConst(s), pos) => (s, pos),
|
(Token::StringConst(s), pos) => (s, pos),
|
||||||
|
(Token::LexError(err), pos) => {
|
||||||
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
||||||
|
}
|
||||||
(_, pos) if map.is_empty() => {
|
(_, pos) if map.is_empty() => {
|
||||||
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(pos))
|
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(pos))
|
||||||
}
|
}
|
||||||
@ -803,6 +817,9 @@ fn parse_map_literal<'a>(
|
|||||||
|
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Colon, _) => (),
|
(Token::Colon, _) => (),
|
||||||
|
(Token::LexError(err), pos) => {
|
||||||
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
||||||
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
":".into(),
|
":".into(),
|
||||||
@ -834,6 +851,9 @@ fn parse_map_literal<'a>(
|
|||||||
)
|
)
|
||||||
.into_err(*pos))
|
.into_err(*pos))
|
||||||
}
|
}
|
||||||
|
(Token::LexError(err), pos) => {
|
||||||
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
||||||
|
}
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(*pos))
|
return Err(PERR::MissingToken("}".into(), MISSING_RBRACE.into()).into_err(*pos))
|
||||||
}
|
}
|
||||||
@ -980,7 +1000,7 @@ fn parse_unary<'a>(
|
|||||||
pos,
|
pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// {EOF}
|
// <EOF>
|
||||||
(Token::EOF, pos) => Err(PERR::UnexpectedEOF.into_err(*pos)),
|
(Token::EOF, pos) => Err(PERR::UnexpectedEOF.into_err(*pos)),
|
||||||
// All other tokens
|
// All other tokens
|
||||||
_ => parse_primary(input, allow_stmt_expr),
|
_ => parse_primary(input, allow_stmt_expr),
|
||||||
@ -1463,6 +1483,7 @@ fn parse_for<'a>(
|
|||||||
// for name in ...
|
// for name in ...
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::In, _) => (),
|
(Token::In, _) => (),
|
||||||
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::MissingToken("in".into(), "after the iteration variable".into())
|
PERR::MissingToken("in".into(), "after the iteration variable".into())
|
||||||
@ -1527,6 +1548,7 @@ fn parse_block<'a>(
|
|||||||
// Must start with {
|
// Must start with {
|
||||||
let pos = match input.next().unwrap() {
|
let pos = match input.next().unwrap() {
|
||||||
(Token::LeftBrace, pos) => pos,
|
(Token::LeftBrace, pos) => pos,
|
||||||
|
(Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::MissingToken("{".into(), "to start a statement block".into()).into_err(pos),
|
PERR::MissingToken("{".into(), "to start a statement block".into()).into_err(pos),
|
||||||
@ -1559,7 +1581,11 @@ fn parse_block<'a>(
|
|||||||
(Token::SemiColon, _) if !need_semicolon => (),
|
(Token::SemiColon, _) if !need_semicolon => (),
|
||||||
// { ... { stmt } ???
|
// { ... { stmt } ???
|
||||||
(_, _) if !need_semicolon => (),
|
(_, _) if !need_semicolon => (),
|
||||||
// { ... stmt ??? - error
|
// { ... stmt <error>
|
||||||
|
(Token::LexError(err), pos) => {
|
||||||
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
||||||
|
}
|
||||||
|
// { ... stmt ???
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
// Semicolons are not optional between statements
|
// Semicolons are not optional between statements
|
||||||
return Err(
|
return Err(
|
||||||
@ -1627,7 +1653,7 @@ fn parse_stmt<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// `return`/`throw` at {EOF}
|
// `return`/`throw` at <EOF>
|
||||||
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(None, return_type, *pos)),
|
(Token::EOF, pos) => Ok(Stmt::ReturnWithVal(None, return_type, *pos)),
|
||||||
// `return;` or `throw;`
|
// `return;` or `throw;`
|
||||||
(Token::SemiColon, _) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
(Token::SemiColon, _) => Ok(Stmt::ReturnWithVal(None, return_type, pos)),
|
||||||
@ -1673,6 +1699,9 @@ fn parse_fn<'a>(
|
|||||||
loop {
|
loop {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(s), pos) => params.push((s, pos)),
|
(Token::Identifier(s), pos) => params.push((s, pos)),
|
||||||
|
(Token::LexError(err), pos) => {
|
||||||
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
||||||
|
}
|
||||||
(_, pos) => return Err(PERR::MissingToken(")".into(), end_err).into_err(pos)),
|
(_, pos) => return Err(PERR::MissingToken(")".into(), end_err).into_err(pos)),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1682,6 +1711,9 @@ fn parse_fn<'a>(
|
|||||||
(Token::Identifier(_), pos) => {
|
(Token::Identifier(_), pos) => {
|
||||||
return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos))
|
return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos))
|
||||||
}
|
}
|
||||||
|
(Token::LexError(err), pos) => {
|
||||||
|
return Err(PERR::BadInput(err.to_string()).into_err(pos))
|
||||||
|
}
|
||||||
(_, pos) => return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos)),
|
(_, pos) => return Err(PERR::MissingToken(",".into(), sep_err).into_err(pos)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1782,7 +1814,11 @@ fn parse_global_level<'a>(
|
|||||||
(Token::SemiColon, _) if !need_semicolon => (),
|
(Token::SemiColon, _) if !need_semicolon => (),
|
||||||
// { stmt } ???
|
// { stmt } ???
|
||||||
(_, _) if !need_semicolon => (),
|
(_, _) if !need_semicolon => (),
|
||||||
// stmt ??? - error
|
// stmt <error>
|
||||||
|
(Token::LexError(err), pos) => {
|
||||||
|
return Err(PERR::BadInput(err.to_string()).into_err(*pos))
|
||||||
|
}
|
||||||
|
// stmt ???
|
||||||
(_, pos) => {
|
(_, pos) => {
|
||||||
// Semicolons are not optional between statements
|
// Semicolons are not optional between statements
|
||||||
return Err(
|
return Err(
|
||||||
|
@ -5,6 +5,7 @@ fn test_increment() -> Result<(), EvalAltResult> {
|
|||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("let x = 1; x += 2; x")?, 3);
|
assert_eq!(engine.eval::<INT>("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")?,
|
||||||
"testing"
|
"testing"
|
||||||
|
Loading…
Reference in New Issue
Block a user