From 44170697cbaf304641c4a3625b57c299e3247d85 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 22 Jun 2023 01:07:17 -0400 Subject: [PATCH] Begin forking with new features --- .github/workflows/ci.yml | 316 --------------------- CHANGELOG.md | 535 ----------------------------------- Cargo.toml | 21 +- README.md | 597 +-------------------------------------- src/context/mod.rs | 147 ++-------- src/error/display.rs | 1 + src/error/mod.rs | 12 + src/function/builtin.rs | 1 + src/interface/mod.rs | 18 +- src/lib.rs | 3 +- src/operator/mod.rs | 3 +- src/tree/mod.rs | 18 +- src/value/display.rs | 1 + src/value/mod.rs | 4 +- src/value/value_type.rs | 3 + tests/integration.rs | 28 +- 16 files changed, 80 insertions(+), 1628 deletions(-) delete mode 100644 .github/workflows/ci.yml delete mode 100644 CHANGELOG.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 6fb1f6e..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,316 +0,0 @@ -name: CI - -on: - pull_request: - push: - -env: - RUSTFLAGS: -Dwarnings - -jobs: - precheck_default: - name: Check default - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - rust: [stable] - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.os }} - - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all --bins --examples --lib - - precheck_all_features: - name: Check all features - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - rust: [stable] - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.os }} - - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features --all --bins --examples --tests --lib - - check_msrv: - needs: [precheck_default, precheck_all_features] - name: Check MSRV with all features - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@master - - - name: Install MSRV toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features --all --bins --examples --tests --lib - - check_benches: - needs: [precheck_default, precheck_all_features] - name: Check benches with all features - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-latest ] - rust: [ nightly ] - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.os }} - - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features --all --bins --benches --examples --tests --lib - - check_sync_readme: - needs: [precheck_default, precheck_all_features] - name: Check sync readme - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-latest ] - rust: [ stable ] - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.os }} - - - name: Install cargo-sync-readme - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-sync-readme - - - name: Sync readme check - uses: actions-rs/cargo@v1 - with: - command: sync-readme - args: --check - - check_platform_compatibility: - needs: [precheck_default, precheck_all_features] - name: Check platform compatibility - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macOS-latest, windows-latest] - rust: [stable] - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.os }} - - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features --all --bins --examples --tests --lib - - check_cli: - needs: [ precheck_default, precheck_all_features ] - name: Check CLI - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-latest ] - rust: [ stable ] - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.os }} - - - name: CLI - uses: GuillaumeFalourd/assert-command-line-output@v2 - with: - command_line: cargo run -- p = 2\; p + 3 - contains: 5 - expected_result: PASSED - - detailed_tests: - needs: [precheck_default, precheck_all_features] - name: Check, test, doc, format and lint with all features - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - rust: [stable, beta, nightly] - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - components: rustfmt, clippy - override: true - - - name: Rust cache - uses: Swatinem/rust-cache@v2 - with: - key: ${{ matrix.os }} - - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features --all --bins --examples --tests --lib - - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: --all-features --all - - - name: Docs - uses: actions-rs/cargo@v1 - with: - command: doc - args: --all-features - - - name: Format - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check - - - name: Lint - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-features --bins --examples --tests --lib - - coveralls_io: - needs: [detailed_tests] - name: Coverage - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: ⚡ Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - default: true - - - name: Install nightly toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - default: false - - - name: Install cargo-tarpaulin - uses: actions-rs/install@v0.1 - with: - crate: cargo-tarpaulin - version: latest - use-tool-cache: true - - - name: Coverage Report with tarpaulin - uses: actions-rs/cargo@v1 - with: - command: tarpaulin - args: --all-features --out Lcov --run-types Tests Doctests -- --test-threads 1 - - - name: Upload Coverage - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: ./lcov.info diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 43b931d..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,535 +0,0 @@ -# Change Log - -## Unreleased - -### Notes - -### Added - - * Methods to mutably iterate over operators (`Node::iter_operators_mut`) and all types of identifiers (`Node::iter_identifiers_mut`, `Node::iter_*_identifiers_mut`) (#136) - -### Removed - -### Changed - -### Fixed - -### Deprecated - -### Contributors - -My warmhearted thanks goes to: - - * [Ben Weinstein-Raun](https://github.com/benwr) - -## [11.0.0](https://github.com/ISibboI/evalexpr/compare/10.0.0...11.0.0) - 2023-06-01 - -### Notes - - * Due to the introduction of [GATs](https://blog.rust-lang.org/2022/10/28/gats-stabilization.html), the MSRV changes to `1.65.0` in this update. - -### Added - - * Builtin function `math::abs` (#130) - * A CLI for evaluating expressions. Install via `cargo install evalexpr`. (#133) - * The ability to parse integers from hex literals (#131) - -### Changed - - * The `IterVariablesContext` from using a lifetime parameter to GATs (#135) - -### Contributors - -My warmhearted thanks goes to: - - * [Heki](https://github.com/LinuxHeki) - * [Kaspar Schleiser](https://github.com/kaspar030) - -## [10.0.0](https://github.com/ISibboI/evalexpr/compare/9.1.0...10.0.0) - 2023-05-21 - -### Added - - * Builtin functions can now be disabled (#129) - -### Contributors - -My warmhearted thanks goes to: - - * [hexofyore](https://github.com/hexofyore) - -## [9.1.0](https://github.com/ISibboI/evalexpr/compare/9.0.0...9.1.0) - 2023-05-16 - -### Added - - * Builtin functions `contains` and `contains_any` (#127) - -### Contributors - -My warmhearted thanks goes to: - - * [nickisyourfan](https://github.com/nickisyourfan) - -## [9.0.0](https://github.com/ISibboI/evalexpr/compare/8.2.0...9.0.0) - 2023-04-13 - -### Fixed - - * Taking numbers to negative powers gave unexpected results (#120) - * **Update MSRV to 1.56.1 (2021 edition release)** to allow builds with the latest versions of all dependencies. - -### Contributors - -My warmhearted thanks goes to: - - * [Pham Nhat Huy](https://github.com/012e) - -## [8.2.0](https://github.com/ISibboI/evalexpr/compare/8.1.0...8.2.0) - 2023-04-13 - -### Added - - * `EvalExprError` now derives `Clone` (#116) - -### Changed - - * Occurrences of `f64` and `i64` have been replaced with the type aliases `FloatType` and `IntType` where applicable (#113) - -### Contributors - -My warmhearted thanks goes to: - - * [Natan Freeman](https://github.com/NatanFreeman) - * [Claus Matzinger](https://github.com/celaus) - -## [8.1.0](https://github.com/ISibboI/evalexpr/compare/8.0.0...8.1.0) - 2022-07-22 - -### Added - - * Get all variables or variable/value pairs in a context via a new trait `IterateVariablesContext` (#108) - * Functions `iter_(read/write)_variable_identifiers`, which iterate over all `ReadVariableIdentifier`s or all `WriteVariableIdentifier`s in an operator tree (#110) - -### Contributors - -My warmhearted thanks goes to: - - * [egel557](https://github.com/egel557) - * [Tobias Schmitt](https://github.com/tsmt09) - -## [8.0.0](https://github.com/ISibboI/evalexpr/compare/7.2.0...8.0.0) - 2022-07-06 - -### Added - - * Builtin functions to check for nan, infinity and subnormality in floats (#101) - * Builtin random function (#102) - * Implement `TryFrom` for all types a value can hold (#105) - * Split VariableIdentifier node into read and write variants (#106) - -### Contributors - -My warmhearted thanks goes to: - - * [Ophir LOJKINE](https://github.com/lovasoa) - * [Joe Grund](https://github.com/jgrund) - * [Luka Maljic](https://github.com/malj) - -## [7.2.0](https://github.com/ISibboI/evalexpr/compare/7.1.1...7.2.0) - 2022-03-16 - -### Added - - * The builtin function `if`, which mimics the if-else construct existing in many programming languages. - -### Contributors - -My warmhearted thanks goes to: - - * [Ophir LOJKINE](https://github.com/lovasoa) - -## [7.1.1](https://github.com/ISibboI/evalexpr/compare/7.1.0...7.1.1) - 2022-03-14 - -### Fixed - - * Set regex minimum version to `1.5.5`, as the previous versions contains a security vulnerability. - See https://groups.google.com/g/rustlang-security-announcements/c/NcNNL1Jq7Yw?pli=1. - This vulnerability does not affect this crate as of now, but if we ever allow passing parameters to the regex engine, it might. - -## [7.1.0](https://github.com/ISibboI/evalexpr/compare/7.0.1...7.1.0) - 2022-03-13 - -### Added - - * Bit shift functions `shl` and `shr`, same as Rust's shift functions on `i64`. - -### Contributors - -My warmhearted thanks goes to - - * [Diane Sparks](https://github.com/FractalDiane) - -## [7.0.1](https://github.com/ISibboI/evalexpr/compare/7.0.0...7.0.1) - 2022-02-20 - -### Changed - - * Updated the optional dependencies, and fixed them to a minimum tested version. - For simplicity, I fixed them to the newest version, but since I export none of them, this is luckily not breaking. - * Updated the dev-dependencies. - -## [7.0.0](https://github.com/ISibboI/evalexpr/compare/6.6.0...7.0.0) - 2022-01-13 - -### Changed - - * Made the `EvalexprError` enum `non_exhaustive`. - -### Fixed - - * Expressions that have dangling parenthese expressions such as `4(5)` now produce an error. - -### Contributors - -My warmhearted thanks goes to - - * [dbr/Ben](https://github.com/dbr) - -## [6.6.0](https://github.com/ISibboI/evalexpr/compare/6.5.0...6.6.0) - 2021-10-13 - -### Added - - * Bitwise operators as builtin functions `bitand`, `bitor`, `bitxor`, `bitnot` (#88) - * Public immutable and mutable accessor functions to the operator and children of a Node. - -### Contributors - -My warmhearted thanks goes to - - * [Michał Hanusek](https://github.com/hanusek) - * [Kai Giebeler](https://github.com/kawogi) - -## [6.5.0](https://github.com/ISibboI/evalexpr/compare/6.4.0...6.5.0) - 2021-08-16 - -### Added - - * Make `Function::new` able to accept closures (thanks to Jakub Dąbek) - -### Contributors - -My warmhearted thanks goes to - - * [Jakub Dąbek](https://github.com/jakubdabek) - * [LonnonjamesD](https://github.com/LonnonjamesD) - -## [6.4.0](https://github.com/ISibboI/evalexpr/compare/6.3.0...6.4.0) - 2021-07-21 - -### Notes - - * Minimum supported Rust version (MSRV) increased to `1.46.0` - * Increased test coverage by adding more test and ignoring untestable files - -### Added - - * Allow scientific notation in float literals - -### Changed - - * Made some functions `const`. This increased the MSRV - -### Fixed - - * `eval_number` methods returned `EvalexprError::ExpectedFloat` before, now they correctly return `EvalexprError::ExpectedNumber` - -### Contributors - -My warmhearted thanks goes to - - * [Dennis Marttinen](https://github.com/twelho) - -## [6.3.0](https://github.com/ISibboI/evalexpr/compare/6.2.1...6.3.0) - 2021-07-06 - -### Added - - * Implement more builtin math methods - -### Contributors - -My warmhearted thanks goes to - - * [Magnus Ulimoen](https://github.com/mulimoen) - -## [6.2.0](https://github.com/ISibboI/evalexpr/compare/6.1.1...6.2.0) - 2021-06-24 - -### Notes - - * Increased test coverage - -### Added - - * Implemented `Clone` for `HashMapContext` - -### Contributors - -My warmhearted thanks goes to - - * [Magnus Ulimoen](https://github.com/mulimoen) - -## [6.1.1](https://github.com/ISibboI/evalexpr/compare/6.1.0...6.1.1) - 2021-06-22 - -### Fixed - - * Improved syntax of documentation - -## [6.1.0](https://github.com/ISibboI/evalexpr/compare/6.0.0...6.1.0) - 2021-06-02 - -### Added - - * Macro `math_consts_context` adding all of Rust's `f64` constants - * All common math functions implemented by Rust's `f64` are now builtin - * Continuous integration and test coverage report - -### Contributors - -My warmhearted thanks goes to - - * [Edwin](https://github.com/olback) - -## [6.0.0](https://github.com/ISibboI/evalexpr/compare/5.1.0...6.0.0) - 2021-05-28 - -### Added - - * `#![forbid(unsafe_code)]` - * Made `Function` derive `Clone` - * Ensure that `Function` implements `Send` and `Sync` - -### Removed - - * `Cargo.lock` - -### Changed - - * Decomposed `Context` into `Context`, `ContextWithMutableVariables` and `ContextWithMutableFunctions` - * Replaced the `get_function` method of `Context` with a `call_function` method - -### Contributors - -My warmhearted thanks goes to - - * [dvtomas](https://github.com/dvtomas) - -## [5.1.0](https://github.com/ISibboI/evalexpr/compare/5.0.5...5.1.0) - 2021-05-28 - -### Added - - * Make `Node` cloneable - -### Contributors - -My warmhearted thanks goes to - - * [dvtomas](https://github.com/dvtomas) - -## [5.0.5](https://github.com/ISibboI/evalexpr/compare/5.0.4...5.0.5) - 2019-09-13 - -### Fixed - - * is-it-maintained badges had wrong repository definitions - * maintenance status was given wrongly - * move maintenance status to top - -## [5.0.4](https://github.com/ISibboI/evalexpr/compare/5.0.3...5.0.4) - 2019-09-13 - -### Added - - * maintenance badge - * is-it-maintained badges - -## [5.0.3](https://github.com/ISibboI/evalexpr/compare/5.0.2...5.0.3) - 2019-08-30 - -### Fixed - - * The `!=` operator was wrongfully parsed as Token::Eq - -### Contributors - - * [slientgoat](https://github.com/slientgoat) - -## [5.0.2](https://github.com/ISibboI/evalexpr/compare/5.0.1...5.0.2) - 2019-08-30 - -### Changed - - * Removed target.bench.dev-dependencies completely, as they can be just listed under the normal dev-dependencies - -## [5.0.1](https://github.com/ISibboI/evalexpr/compare/5.0.0...5.0.1) - 2019-08-30 - -### Fixed - - * Bench dependencies are now dev-dependencies so they are not listed on crates.io as normal dependencies anymore - -## [5.0.0](https://github.com/ISibboI/evalexpr/compare/4.1.0...5.0.0) - *'Sanity'* - 2019-08-30 - -### Notes - -Finally, 'Sanity' has been released, including a huge bunch of new features. -Notably, and providing a reason for the name of this release, function call and tuple semantics have improved a lot. -Functions now always take exactly one argument, but this can then be a tuple. -It is now possible to construct tuples of tuples, such that mode complex values can be constructed. -As of now there is no way to deconstruct them though. - -A lot has been done on string processing, special thanks for that goes to [bittrance](https://github.com/bittrance). -Specifically, under the feature flag `regex_support` two regex functions for strings are hiding now. -Also, the operators `+` and comparison operators have been fitted to support strings. - -Thanks to [lovasoa](https://github.com/lovasoa), we now have a nice macro for context creation. - -Thanks to [Atul9](https://github.com/Atul9), the crate is now Rust 2018 compliant. - -Thanks to [mestachs'](https://github.com/mestachs) request, we now have functions to iterate over identifiers within an expression. - -Internally, the structure of the operator tree changed from being `&dyn`-based to being `enum`-based. -Also, we have benchmarks now to observe performance changes in future releases. - -### Added - - * Iterator over all identifiers within an expression, including duplicates - * Iterators over only variable or only function identifiers within an expression, including duplicates - * Overload the `+` operator to concatenate strings - * Overload `<`, `<=`, `>` and `>=` for strings using lexical ordering (Note: `==` and `!=` compare strings as expected) - * Add `len`, `str::regex_matches`, `str::regex_replace`, `str::to_lowercase`, `str::to_uppercase`, `str::trim` functions for strings - * Add a macro for more convenient definition of contexts including the direct definition of static contexts - * Add API for value decomposition - * Allow using context operations in `eval` calls without context - * Operator assignment operators for each binary operation (`+=`, `-=`, ...) - * The `Operator` enum is now public for better error types - * Benchmarks for observing performance of future releases - -### Removed - - * Function arguments are not decomposed anymore. - The function implementation will receive exactly one argument now. - This allows the function to be called on a tuple properly. - -### Changed - - * Operators are an enum now instead of trait objects - * Update to Rust 2018 - * Updated dependencies - -### Fixed - - * Allow variable assignments in eval calls without context. - A `HashMapContext` is created automatically now. - * The error string for `ExpectedNumber` was wrong - * Operators panicked when adding a number to a string - * Some documentation was not updated for the 4.x releases - -### Contributors - -My warmhearted thanks goes to - - * [bittrance](https://github.com/bittrance) - * [lovasoa](https://github.com/lovasoa) - * [Atul9](https://github.com/Atul9) - * [mestachs](https://github.com/mestachs) - -## [4.1.0](https://github.com/ISibboI/evalexpr/compare/4.0.0...4.1.0) - 2019-03-31 - -### Added - - * Export `expect_function_argument_amount` - -## [4.0.0](https://github.com/ISibboI/evalexpr/compare/3.1.0...4.0.0) - 2019-03-30 - -### Added - - * String constants - -## [3.1.0](https://github.com/ISibboI/evalexpr/compare/3.0.0...3.1.0) - 2019-03-28 - -### Added - - * Add serde support to `HashMapContext` - * Make `HashMapContext` derive `Default` and `Debug` - -### Changed - - * Changed name of serde feature flag to `serde_support` - -## [3.0.0](https://github.com/ISibboI/evalexpr/compare/2.0.0...3.0.0) - 2019-03-28 - -### Notes - -The 3.0.0 update transforms the expression evaluator `evalexpr` to a tiny scripting language. -It allows assignments and chaining of expressions. -Some changes in this update are breaking, hence the major release. - -### Added - - * Methods `Node::eval__with_context_mut` and crate level `eval__with_context_mut` - * Empty type and corresponding shortcut methods. The empty type is emitted by empty expressions or empty subexpressions `()`. - * The assignment operator `=` - * The expression chaining operator `;` - -### Removed - - * Generic arguments from `Context` traits are now static to allow using trait objects of `Context` - * `EvalexprError::EmptyExpression` is not required anymore since empty expressions now evaluate to the empty type - -### Changed - - * Merge `ContextMut` trait into `Context` trait - -## [2.0.0](https://github.com/ISibboI/evalexpr/compare/1.2.0...2.0.0) - 2019-03-28 - -### Notes - -The 2.0.0 update is the first step to transform evalexpr to a tiny scripting language with support of at least variable assignment. -The main change for now is that `Configuration` is called `Context`, which seems to be a more proper naming for a set of variables that can not only be read, but also manipulated via expressions. -This update includes further renamings and some inconsistencies in the API were fixed. -For more details, see the following subsections. - -### Added - - * Add the `ContextMut` trait, that is a manipulable configuration/context - * Add `ContextNotManipulable` error variant for the `EmptyContext` - * Make the `TupleType` alias public - * Add the `ValueType` enum that represents the type of a value for easier comparisons and matchings - * Add `EvalexprResult` type that uses the `EvalexprError` type (renamed from `Error`) - * Add `Node::eval_number` and `Node::eval_number_with_context` to evaluate to int or float and silently converting to float - * Add `eval_number` and `eval_number_with_context` crate methods to evaluate to int or float and silently converting to float - -### Changed - - * Get rid of some unwraps to improve safety - * Rename `Error` to `EvalexprError` - * Rename `Configuration` to `Context` - * Rename `HashMapConfiguration` to `HashMapContext` and `EmptyConfiguration` to `EmptyContext` - * Rename `Value::as_float` to `Value::as_number` and add new `Value::as_float` that fails if value is an integer - -## [1.2.0](https://github.com/ISibboI/evalexpr/compare/1.1.0...1.2.0) - 2019-03-23 - -### Added - - * Add `serde` feature - * Implement `serde::de::Deserialize` for `Node` - * Document `serde` usage - * Add custom error type with a `String` message - -### Changed - - * Highlighting in documentation - -## [1.1.0](https://github.com/ISibboI/evalexpr/compare/1.0.0...1.1.0) - 2019-03-20 - -### Added - - * Internal aliases `IntType` and `FloatType` used by the `Value` enum are now public - * Type alias `TupleType` used to represent tuples was added - * Error types like `Error::ExpectedInt` for expecting each value type were added - * Shortcut functions like `eval_int` or `eval_int_with_configuration` to evaluate directly into a value type were added - * Documentation for the shortcut functions was added - * Functions to decompose `Value`s were added and documented - -### Removed - - * Integration tests were removed from shipped crate - -### Fixed - - * Wording of some documentation items was changed to improve readability - -## [1.0.0](https://github.com/ISibboI/evalexpr/tree/1.0.0) - 2019-03-20 - - * First stable release diff --git a/Cargo.toml b/Cargo.toml index c4aee93..1178d3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,13 @@ [package] -name = "evalexpr" -version = "11.0.0" -description = "A powerful arithmetic and boolean expression evaluator" -keywords = ["expression", "evaluate", "evaluator", "arithmetic", "boolean"] -categories = ["parsing", "game-engines"] -authors = ["isibboi "] -repository = "https://github.com/ISibboI/evalexpr.git" -homepage = "https://github.com/ISibboI/evalexpr" -documentation = "https://docs.rs/evalexpr" +name = "expressive" +version = "0.1.0" +description = "Fork of evalexpr" +authors = ["jeff "] +repository = "https://git.jeffa.io/jeff/expressive.git" +homepage = "https://git.jeffa.io/jeff/expressive" readme = "README.md" license = "MIT" edition = "2018" -rust-version = "1.65.0" - -[badges] -maintenance = { status = "actively-developed" } -is-it-maintained-issue-resolution = { repository = "ISibboI/evalexpr" } -is-it-maintained-open-issues = { repository = "ISibboI/evalexpr" } [lib] name = "evalexpr" diff --git a/README.md b/README.md index 3e4fbca..c7fea55 100644 --- a/README.md +++ b/README.md @@ -1,596 +1,3 @@ -# evalexpr +# expressive -[![Version](https://img.shields.io/crates/v/evalexpr.svg)](https://crates.io/crates/evalexpr) -[![Downloads](https://img.shields.io/crates/d/evalexpr.svg)](https://crates.io/crates/evalexpr) -[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) -[![Coverage Status](https://coveralls.io/repos/github/ISibboI/evalexpr/badge.svg?branch=main)](https://coveralls.io/github/ISibboI/evalexpr?branch=main) -[![](http://meritbadge.herokuapp.com/evalexpr)](https://crates.io/crates/evalexpr) -[![](https://docs.rs/evalexpr/badge.svg)](https://docs.rs/evalexpr) - -Evalexpr is an expression evaluator and tiny scripting language in Rust. -It has a small and easy to use interface and can be easily integrated into any application. -It is very lightweight and comes with no further dependencies. -Evalexpr is [available on crates.io](https://crates.io/crates/evalexpr), and its [API Documentation is available on docs.rs](https://docs.rs/evalexpr). - - -**Minimum Supported Rust Version:** 1.65.0 - - - - -## Quickstart - -Add `evalexpr` as dependency to your `Cargo.toml`: - -```toml -[dependencies] -evalexpr = "" -``` - -Then you can use `evalexpr` to **evaluate expressions** like this: - -```rust -use evalexpr::*; - -assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6))); -// `eval` returns a variant of the `Value` enum, -// while `eval_[type]` returns the respective type directly. -// Both can be used interchangeably. -assert_eq!(eval_int("1 + 2 + 3"), Ok(6)); -assert_eq!(eval("1 - 2 * 3"), Ok(Value::from(-5))); -assert_eq!(eval("1.0 + 2 * 3"), Ok(Value::from(7.0))); -assert_eq!(eval("true && 4 > 2"), Ok(Value::from(true))); -``` - -You can **chain** expressions and **assign** to variables like this: - -```rust -use evalexpr::*; - -let mut context = HashMapContext::new(); -// Assign 5 to a like this -assert_eq!(eval_empty_with_context_mut("a = 5", &mut context), Ok(EMPTY_VALUE)); -// The HashMapContext is type safe, so this will fail now -assert_eq!(eval_empty_with_context_mut("a = 5.0", &mut context), - Err(EvalexprError::expected_int(Value::from(5.0)))); -// We can check which value the context stores for a like this -assert_eq!(context.get_value("a"), Some(&Value::from(5))); -// And use the value in another expression like this -assert_eq!(eval_int_with_context_mut("a = a + 2; a", &mut context), Ok(7)); -// It is also possible to save a bit of typing by using an operator-assignment operator -assert_eq!(eval_int_with_context_mut("a += 2; a", &mut context), Ok(9)); -``` - -And you can use **variables** and **functions** in expressions like this: - -```rust -use evalexpr::*; - -let context = context_map! { - "five" => 5, - "twelve" => 12, - "f" => Function::new(|argument| { - if let Ok(int) = argument.as_int() { - Ok(Value::Int(int / 2)) - } else if let Ok(float) = argument.as_float() { - Ok(Value::Float(float / 2.0)) - } else { - Err(EvalexprError::expected_number(argument.clone())) - } - }), - "avg" => Function::new(|argument| { - let arguments = argument.as_tuple()?; - - if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) { - Ok(Value::Int((a + b) / 2)) - } else { - Ok(Value::Float((arguments[0].as_number()? + arguments[1].as_number()?) / 2.0)) - } - }) -}.unwrap(); // Do proper error handling here - -assert_eq!(eval_with_context("five + 8 > f(twelve)", &context), Ok(Value::from(true))); -// `eval_with_context` returns a variant of the `Value` enum, -// while `eval_[type]_with_context` returns the respective type directly. -// Both can be used interchangeably. -assert_eq!(eval_boolean_with_context("five + 8 > f(twelve)", &context), Ok(true)); -assert_eq!(eval_with_context("avg(2, 4) == 3", &context), Ok(Value::from(true))); -``` - -You can also **precompile** expressions like this: - -```rust -use evalexpr::*; - -let precompiled = build_operator_tree("a * b - c > 5").unwrap(); // Do proper error handling here - -let mut context = context_map! { - "a" => 6, - "b" => 2, - "c" => 3 -}.unwrap(); // Do proper error handling here -assert_eq!(precompiled.eval_with_context(&context), Ok(Value::from(true))); - -context.set_value("c".into(), 8.into()).unwrap(); // Do proper error handling here -assert_eq!(precompiled.eval_with_context(&context), Ok(Value::from(false))); -// `Node::eval_with_context` returns a variant of the `Value` enum, -// while `Node::eval_[type]_with_context` returns the respective type directly. -// Both can be used interchangeably. -assert_eq!(precompiled.eval_boolean_with_context(&context), Ok(false)); -``` - -## CLI - -While primarily meant to be used as a library, `evalexpr` is also available as a command line tool. -It can be installed and used as follows: - -```bash -cargo install evalexpr -evalexpr 2 + 3 # outputs `5` to stdout. -``` - -## Features - -### Operators - -This crate offers a set of binary and unary operators for building expressions. -Operators have a precedence to determine their order of evaluation, where operators of higher precedence are evaluated first. -The precedence should resemble that of most common programming languages, especially Rust. -Variables and values have a precedence of 200, and function literals have 190. - -Supported binary operators: - -| Operator | Precedence | Description | -|----------|------------|-------------| -| ^ | 120 | Exponentiation | -| * | 100 | Product | -| / | 100 | Division (integer if both arguments are integers, otherwise float) | -| % | 100 | Modulo (integer if both arguments are integers, otherwise float) | -| + | 95 | Sum or String Concatenation | -| - | 95 | Difference | -| < | 80 | Lower than | -| \> | 80 | Greater than | -| <= | 80 | Lower than or equal | -| \>= | 80 | Greater than or equal | -| == | 80 | Equal | -| != | 80 | Not equal | -| && | 75 | Logical and | -| || | 70 | Logical or | -| = | 50 | Assignment | -| += | 50 | Sum-Assignment or String-Concatenation-Assignment | -| -= | 50 | Difference-Assignment | -| *= | 50 | Product-Assignment | -| /= | 50 | Division-Assignment | -| %= | 50 | Modulo-Assignment | -| ^= | 50 | Exponentiation-Assignment | -| &&= | 50 | Logical-And-Assignment | -| ||= | 50 | Logical-Or-Assignment | -| , | 40 | Aggregation | -| ; | 0 | Expression Chaining | - -Supported unary operators: - -| Operator | Precedence | Description | -|----------|------------|-------------| -| - | 110 | Negation | -| ! | 110 | Logical not | - -Operators that take numbers as arguments can either take integers or floating point numbers. -If one of the arguments is a floating point number, all others are converted to floating point numbers as well, and the resulting value is a floating point number as well. -Otherwise, the result is an integer. -An exception to this is the exponentiation operator that always returns a floating point number. -Example: - -```rust -use evalexpr::*; - -assert_eq!(eval("1 / 2"), Ok(Value::from(0))); -assert_eq!(eval("1.0 / 2"), Ok(Value::from(0.5))); -assert_eq!(eval("2^2"), Ok(Value::from(4.0))); -``` - -#### The Aggregation Operator - -The aggregation operator aggregates a set of values into a tuple. -A tuple can contain arbitrary values, it is not restricted to a single type. -The operator is n-ary, so it supports creating tuples longer than length two. -Example: - -```rust -use evalexpr::*; - -assert_eq!(eval("1, \"b\", 3"), - Ok(Value::from(vec![Value::from(1), Value::from("b"), Value::from(3)]))); -``` - -To create nested tuples, use parentheses: - -```rust -use evalexpr::*; - -assert_eq!(eval("1, 2, (true, \"b\")"), Ok(Value::from(vec![ - Value::from(1), - Value::from(2), - Value::from(vec![ - Value::from(true), - Value::from("b") - ]) -]))); -``` - -#### The Assignment Operator - -This crate features the assignment operator, that allows expressions to store their result in a variable in the expression context. -If an expression uses the assignment operator, it must be evaluated with a mutable context. - -Note that assignments are type safe when using the `HashMapContext`. -That means that if an identifier is assigned a value of a type once, it cannot be assigned a value of another type. - -```rust -use evalexpr::*; - -let mut context = HashMapContext::new(); -assert_eq!(eval_with_context("a = 5", &context), Err(EvalexprError::ContextNotMutable)); -assert_eq!(eval_empty_with_context_mut("a = 5", &mut context), Ok(EMPTY_VALUE)); -assert_eq!(eval_empty_with_context_mut("a = 5.0", &mut context), - Err(EvalexprError::expected_int(5.0.into()))); -assert_eq!(eval_int_with_context("a", &context), Ok(5)); -assert_eq!(context.get_value("a"), Some(5.into()).as_ref()); -``` - -For each binary operator, there exists an equivalent operator-assignment operator. -Here are some examples: - -```rust -use evalexpr::*; - -assert_eq!(eval_int("a = 2; a *= 2; a += 2; a"), Ok(6)); -assert_eq!(eval_float("a = 2.2; a /= 2.0 / 4 + 1; a"), Ok(2.2 / (2.0 / 4.0 + 1.0))); -assert_eq!(eval_string("a = \"abc\"; a += \"def\"; a"), Ok("abcdef".to_string())); -assert_eq!(eval_boolean("a = true; a &&= false; a"), Ok(false)); -``` - -#### The Expression Chaining Operator - -The expression chaining operator works as one would expect from programming languages that use the semicolon to end statements, like `Rust`, `C` or `Java`. -It has the special feature that it returns the value of the last expression in the expression chain. -If the last expression is terminated by a semicolon as well, then `Value::Empty` is returned. -Expression chaining is useful together with assignment to create small scripts. - -```rust -use evalexpr::*; - -let mut context = HashMapContext::new(); -assert_eq!(eval("1;2;3;4;"), Ok(Value::Empty)); -assert_eq!(eval("1;2;3;4"), Ok(4.into())); - -// Initialization of variables via script -assert_eq!(eval_empty_with_context_mut("hp = 1; max_hp = 5; heal_amount = 3;", &mut context), - Ok(EMPTY_VALUE)); -// Precompile healing script -let healing_script = build_operator_tree("hp = min(hp + heal_amount, max_hp); hp").unwrap(); // Do proper error handling here -// Execute precompiled healing script -assert_eq!(healing_script.eval_int_with_context_mut(&mut context), Ok(4)); -assert_eq!(healing_script.eval_int_with_context_mut(&mut context), Ok(5)); -``` - -### Contexts - -An expression evaluator that just evaluates expressions would be useful already, but this crate can do more. -It allows using [variables](#variables), [assignments](#the-assignment-operator), [statement chaining](#the-expression-chaining-operator) and [user-defined functions](#user-defined-functions) within an expression. -When assigning to variables, the assignment is stored in a context. -When the variable is read later on, it is read from the context. -Contexts can be preserved between multiple calls to eval by creating them yourself. -Here is a simple example to show the difference between preserving and not preserving context between evaluations: - -```rust -use evalexpr::*; - -assert_eq!(eval("a = 5;"), Ok(Value::from(()))); -// The context is not preserved between eval calls -assert_eq!(eval("a"), Err(EvalexprError::VariableIdentifierNotFound("a".to_string()))); - -let mut context = HashMapContext::new(); -assert_eq!(eval_with_context_mut("a = 5;", &mut context), Ok(Value::from(()))); -// Assignments require mutable contexts -assert_eq!(eval_with_context("a = 6", &context), Err(EvalexprError::ContextNotMutable)); -// The HashMapContext is type safe -assert_eq!(eval_with_context_mut("a = 5.5", &mut context), - Err(EvalexprError::ExpectedInt { actual: Value::from(5.5) })); -// Reading a variable does not require a mutable context -assert_eq!(eval_with_context("a", &context), Ok(Value::from(5))); - -``` - -Note that the assignment is forgotten between the two calls to eval in the first example. -In the second part, the assignment is correctly preserved. -Note as well that to assign to a variable, the context needs to be passed as a mutable reference. -When passed as an immutable reference, an error is returned. - -Also, the `HashMapContext` is type safe. -This means that assigning to `a` again with a different type yields an error. -Type unsafe contexts may be implemented if requested. -For reading `a`, it is enough to pass an immutable reference. - -Contexts can also be manipulated in code. -Take a look at the following example: - -```rust -use evalexpr::*; - -let mut context = HashMapContext::new(); -// We can set variables in code like this... -context.set_value("a".into(), 5.into()); -// ...and read from them in expressions -assert_eq!(eval_int_with_context("a", &context), Ok(5)); -// We can write or overwrite variables in expressions... -assert_eq!(eval_with_context_mut("a = 10; b = 1.0;", &mut context), Ok(().into())); -// ...and read the value in code like this -assert_eq!(context.get_value("a"), Some(&Value::from(10))); -assert_eq!(context.get_value("b"), Some(&Value::from(1.0))); -``` - -Contexts are also required for user-defined functions. -Those can be passed one by one with the `set_function` method, but it might be more convenient to use the `context_map!` macro instead: - -```rust -use evalexpr::*; - -let context = context_map!{ - "f" => Function::new(|args| Ok(Value::from(args.as_int()? + 5))), -}.unwrap_or_else(|error| panic!("Error creating context: {}", error)); -assert_eq!(eval_int_with_context("f 5", &context), Ok(10)); -``` - -For more information about user-defined functions, refer to the respective [section](#user-defined-functions). - -### Builtin Functions - -This crate offers a set of builtin functions (see below for a full list). -They can be disabled if needed as follows: - -```rust -use evalexpr::*; -let mut context = HashMapContext::new(); -assert_eq!(eval_with_context("max(1,3)",&context),Ok(Value::from(3))); -context.set_builtin_functions_disabled(true).unwrap(); // Do proper error handling here -assert_eq!(eval_with_context("max(1,3)",&context),Err(EvalexprError::FunctionIdentifierNotFound(String::from("max")))); -``` - -Not all contexts support enabling or disabling builtin functions. -Specifically the `EmptyContext` has builtin functions disabled by default, and they cannot be enabled. -Symmetrically, the `EmptyContextWithBuiltinFunctions` has builtin functions enabled by default, and they cannot be disabled. - -| Identifier | Argument Amount | Argument Types | Description | -|----------------------|-----------------|-------------------------------|-------------| -| `min` | >= 1 | Numeric | Returns the minimum of the arguments | -| `max` | >= 1 | Numeric | Returns the maximum of the arguments | -| `len` | 1 | String/Tuple | Returns the character length of a string, or the amount of elements in a tuple (not recursively) | -| `floor` | 1 | Numeric | Returns the largest integer less than or equal to a number | -| `round` | 1 | Numeric | Returns the nearest integer to a number. Rounds half-way cases away from 0.0 | -| `ceil` | 1 | Numeric | Returns the smallest integer greater than or equal to a number | -| `if` | 3 | Boolean, Any, Any | If the first argument is true, returns the second argument, otherwise, returns the third | -| `contains` | 2 | Tuple, any non-tuple | Returns true if second argument exists in first tuple argument. | -| `contains_any` | 2 | Tuple, Tuple of any non-tuple | Returns true if one of the values in the second tuple argument exists in first tuple argument. | -| `typeof` | 1 | Any | returns "string", "float", "int", "boolean", "tuple", or "empty" depending on the type of the argument | -| `math::is_nan` | 1 | Numeric | Returns true if the argument is the floating-point value NaN, false if it is another floating-point value, and throws an error if it is not a number | -| `math::is_finite` | 1 | Numeric | Returns true if the argument is a finite floating-point number, false otherwise | -| `math::is_infinite` | 1 | Numeric | Returns true if the argument is an infinite floating-point number, false otherwise | -| `math::is_normal` | 1 | Numeric | Returns true if the argument is a floating-point number that is neither zero, infinite, [subnormal](https://en.wikipedia.org/wiki/Subnormal_number), or NaN, false otherwise | -| `math::ln` | 1 | Numeric | Returns the natural logarithm of the number | -| `math::log` | 2 | Numeric, Numeric | Returns the logarithm of the number with respect to an arbitrary base | -| `math::log2` | 1 | Numeric | Returns the base 2 logarithm of the number | -| `math::log10` | 1 | Numeric | Returns the base 10 logarithm of the number | -| `math::exp` | 1 | Numeric | Returns `e^(number)`, (the exponential function) | -| `math::exp2` | 1 | Numeric | Returns `2^(number)` | -| `math::pow` | 2 | Numeric, Numeric | Raises a number to the power of the other number | -| `math::cos` | 1 | Numeric | Computes the cosine of a number (in radians) | -| `math::acos` | 1 | Numeric | Computes the arccosine of a number. The return value is in radians in the range [0, pi] or NaN if the number is outside the range [-1, 1] | -| `math::cosh` | 1 | Numeric | Hyperbolic cosine function | -| `math::acosh` | 1 | Numeric | Inverse hyperbolic cosine function | -| `math::sin` | 1 | Numeric | Computes the sine of a number (in radians) | -| `math::asin` | 1 | Numeric | Computes the arcsine of a number. The return value is in radians in the range [-pi/2, pi/2] or NaN if the number is outside the range [-1, 1] | -| `math::sinh` | 1 | Numeric | Hyperbolic sine function | -| `math::asinh` | 1 | Numeric | Inverse hyperbolic sine function | -| `math::tan` | 1 | Numeric | Computes the tangent of a number (in radians) | -| `math::atan` | 1 | Numeric | Computes the arctangent of a number. The return value is in radians in the range [-pi/2, pi/2] | -| `math::atan2` | 2 | Numeric, Numeric | Computes the four quadrant arctangent in radians | -| `math::tanh` | 1 | Numeric | Hyperbolic tangent function | -| `math::atanh` | 1 | Numeric | Inverse hyperbolic tangent function. | -| `math::sqrt` | 1 | Numeric | Returns the square root of a number. Returns NaN for a negative number | -| `math::cbrt` | 1 | Numeric | Returns the cube root of a number | -| `math::hypot` | 2 | Numeric | Calculates the length of the hypotenuse of a right-angle triangle given legs of length given by the two arguments | -| `math::abs` | 1 | Numeric | Returns the absolute value of a number, returning an integer if the argument was an integer, and a float otherwise | -| `str::regex_matches` | 2 | String, String | Returns true if the first argument matches the regex in the second argument (Requires `regex_support` feature flag) | -| `str::regex_replace` | 3 | String, String, String | Returns the first argument with all matches of the regex in the second argument replaced by the third argument (Requires `regex_support` feature flag) | -| `str::to_lowercase` | 1 | String | Returns the lower-case version of the string | -| `str::to_uppercase` | 1 | String | Returns the upper-case version of the string | -| `str::trim` | 1 | String | Strips whitespace from the start and the end of the string | -| `str::from` | >= 0 | Any | Returns passed value as string | -| `bitand` | 2 | Int | Computes the bitwise and of the given integers | -| `bitor` | 2 | Int | Computes the bitwise or of the given integers | -| `bitxor` | 2 | Int | Computes the bitwise xor of the given integers | -| `bitnot` | 1 | Int | Computes the bitwise not of the given integer | -| `shl` | 2 | Int | Computes the given integer bitwise shifted left by the other given integer | -| `shr` | 2 | Int | Computes the given integer bitwise shifted right by the other given integer | -| `random` | 0 | Empty | Return a random float between 0 and 1. Requires the `rand` feature flag. | - -The `min` and `max` functions can deal with a mixture of integer and floating point arguments. -If the maximum or minimum is an integer, then an integer is returned. -Otherwise, a float is returned. - -The regex functions require the feature flag `regex_support`. - -### Values - -Operators take values as arguments and produce values as results. -Values can be booleans, integer or floating point numbers, strings, tuples or the empty type. -Values are denoted as displayed in the following table. - -| Value type | Example | -|------------|---------| -| `Value::String` | `"abc"`, `""`, `"a\"b\\c"` | -| `Value::Boolean` | `true`, `false` | -| `Value::Int` | `3`, `-9`, `0`, `135412`, `0xfe02`, `-0x1e` | -| `Value::Float` | `3.`, `.35`, `1.00`, `0.5`, `123.554`, `23e4`, `-2e-3`, `3.54e+2` | -| `Value::Tuple` | `(3, 55.0, false, ())`, `(1, 2)` | -| `Value::Empty` | `()` | - -Integers are internally represented as `i64`, and floating point numbers are represented as `f64`. -Tuples are represented as `Vec` and empty values are not stored, but represented by Rust's unit type `()` where necessary. - -There exist type aliases for some of the types. -They include `IntType`, `FloatType`, `TupleType` and `EmptyType`. - -Values can be constructed either directly or using the `From` trait. -They can be decomposed using the `Value::as_[type]` methods. -The type of a value can be checked using the `Value::is_[type]` methods. - -**Examples for constructing a value:** - -| Code | Result | -|------|--------| -| `Value::from(4)` | `Value::Int(4)` | -| `Value::from(4.4)` | `Value::Float(4.4)` | -| `Value::from(true)` | `Value::Boolean(true)` | -| `Value::from(vec![Value::from(3)])` | `Value::Tuple(vec![Value::Int(3)])` | - -**Examples for deconstructing a value:** - -| Code | Result | -|------|--------| -| `Value::from(4).as_int()` | `Ok(4)` | -| `Value::from(4.4).as_float()` | `Ok(4.4)` | -| `Value::from(true).as_int()` | `Err(Error::ExpectedInt {actual: Value::Boolean(true)})` | - -Values have a precedence of 200. - -### Variables - -This crate allows to compile parameterizable formulas by using variables. -A variable is a literal in the formula, that does not contain whitespace or can be parsed as value. -For working with variables, a [context](#contexts) is required. -It stores the mappings from variables to their values. - -Variables do not have fixed types in the expression itself, but are typed by the context. -Once a variable is assigned a value of a specific type, it cannot be assigned a value of another type. -This might change in the future and can be changed by using a type-unsafe context (not provided by this crate as of now). - -Here are some examples and counter-examples on expressions that are interpreted as variables: - -| Expression | Variable? | Explanation | -|------------|--------|-------------| -| `a` | yes | | -| `abc` | yes | | -| `a EvalexprResult`. -The definition needs to be included in the [`Context`](#contexts) that is used for evaluation. -As of now, functions cannot be defined within the expression, but that might change in the future. - -The function gets passed what ever value is directly behind it, be it a tuple or a single values. -If there is no value behind a function, it is interpreted as a variable instead. -More specifically, a function needs to be followed by either an opening brace `(`, another literal, or a value. -While not including special support for multi-valued functions, they can be realized by requiring a single tuple argument. - -Be aware that functions need to verify the types of values that are passed to them. -The `error` module contains some shortcuts for verification, and error types for passing a wrong value type. -Also, most numeric functions need to distinguish between being called with integers or floating point numbers, and act accordingly. - -Here are some examples and counter-examples on expressions that are interpreted as function calls: - -| Expression | Function? | Explanation | -|------------|--------|-------------| -| `a v` | yes | | -| `x 5.5` | yes | | -| `a (3, true)` | yes | | -| `a b 4` | yes | Call `a` with the result of calling `b` with `4` | -| `5 b` | no | Error, value cannot be followed by a literal | -| `12 3` | no | Error, value cannot be followed by a value | -| `a 5 6` | no | Error, function call cannot be followed by a value | - -Functions have a precedence of 190. - -### [Serde](https://serde.rs) - -To use this crate with serde, the `serde_support` feature flag has to be set. -This can be done like this in the `Cargo.toml`: - -```toml -[dependencies] -evalexpr = {version = "7", features = ["serde_support"]} -``` - -This crate implements `serde::de::Deserialize` for its type `Node` that represents a parsed expression tree. -The implementation expects a [serde `string`](https://serde.rs/data-model.html) as input. -Example parsing with [ron format](docs.rs/ron): - -```rust -extern crate ron; -use evalexpr::*; - -let mut context = context_map!{ - "five" => 5 -}.unwrap(); // Do proper error handling here - -// In ron format, strings are surrounded by " -let serialized_free = "\"five * five\""; -match ron::de::from_str::(serialized_free) { - Ok(free) => assert_eq!(free.eval_with_context(&context), Ok(Value::from(25))), - Err(error) => { - () // Handle error - } -} -``` - -With `serde`, expressions can be integrated into arbitrarily complex data. - -The crate also implements `Serialize` and `Deserialize` for the `HashMapContext`, -but note that only the variables get (de)serialized, not the functions. - -## License - -This crate is primarily distributed under the terms of the MIT license. -See [LICENSE](LICENSE) for details. - - - - -## No Panicking - -This crate makes extensive use of the `Result` pattern and is intended to never panic. -The *exception* are panics caused by *failed allocations*. -But unfortunately, Rust does not provide any features to prove this behavior. -The developer of this crate has not found a good solution to ensure no-panic behavior in any way. -Please report a panic in this crate immediately as issue on [github](https://github.com/ISibboI/evalexpr/issues). - -Even if the crate itself is panic free, it allows the user to define custom functions that are executed by the crate. -The user needs to ensure that the functions they provide to the crate never panic. - -## Untrusted input - -This crate was not built with untrusted input in mind, but due to its simplicity and freedom of panics it is likely secure, keeping the following in mind: - * Limit the length of the untrusted input. - * If a mutable context is maintained between evaluations of untrusted input, the untrusted input might fill it gradually until the application runs out of memory. - * If no context is provided, a temporary mutable context is implicitly provided. This is freed after evaluation of every single string, so gradual filling cannot happen. - * If no context or a mutable context is provided, and the `regex_support` feature is activated, the `regex_replace` builtin function can be used to build an exponentially sized string. - -## Contribution - -If you have any ideas for features or see any problems in the code, architecture, interface, algorithmics or documentation, please open an issue on [github](https://github.com/ISibboI/evalexpr/issues). -If there is already an issue describing what you want to say, please add a thumbs up or whatever emoji you think fits to the issue, so I know which ones I should prioritize. - -**Notes for contributors:** - - * This crate uses the [`sync-readme`](https://github.com/phaazon/cargo-sync-readme) cargo subcommand to keep the documentation in `src/lib.rs` and `README.md` in sync. - The subcommand only syncs from the documentation in `src/lib.rs` to `README.md`. - So please alter the documentation in the `src/lib.rs` rather than altering anything in between `` and `` in the `README.md`. +Fork of [evalexpr](https://github.com/ISibboI/evalexpr). diff --git a/src/context/mod.rs b/src/context/mod.rs index 65a5c0b..8d562c0 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -22,13 +22,6 @@ pub trait Context { /// Calls the function that is linked to the given identifier with the given argument. /// If no function with the given identifier is found, this method returns `EvalexprError::FunctionIdentifierNotFound`. fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult; - - /// Checks if builtin functions are disabled. - fn are_builtin_functions_disabled(&self) -> bool; - - /// Disables builtin functions if `disabled` is `true`, and enables them otherwise. - /// If the context does not support enabling or disabling builtin functions, an error is returned. - fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()>; } /// A context that allows to assign to variables. @@ -67,152 +60,46 @@ pub trait IterateVariablesContext { fn iter_variable_names(&self) -> Self::VariableNameIterator<'_>; } -/*/// A context that allows to retrieve functions programmatically. -pub trait GetFunctionContext: Context { - /// Returns the function that is linked to the given identifier. - /// - /// This might not be possible for all functions, as some might be hard-coded. - /// In this case, a special error variant should be returned (Not yet implemented). - fn get_function(&self, identifier: &str) -> Option<&Function>; -}*/ - -/// A context that returns `None` for each identifier. -/// Builtin functions are disabled and cannot be enabled. -#[derive(Debug, Default)] -pub struct EmptyContext; - -impl Context for EmptyContext { - fn get_value(&self, _identifier: &str) -> Option<&Value> { - None - } - - fn call_function(&self, identifier: &str, _argument: &Value) -> EvalexprResult { - Err(EvalexprError::FunctionIdentifierNotFound( - identifier.to_string(), - )) - } - - /// Builtin functions are always disabled for `EmptyContext`. - fn are_builtin_functions_disabled(&self) -> bool { - true - } - - /// Builtin functions can't be enabled for `EmptyContext`. - fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { - if disabled { - Ok(()) - } else { - Err(EvalexprError::BuiltinFunctionsCannotBeEnabled) - } - } -} - -impl IterateVariablesContext for EmptyContext { - type VariableIterator<'a> = iter::Empty<(String, Value)>; - type VariableNameIterator<'a> = iter::Empty; - - fn iter_variables(&self) -> Self::VariableIterator<'_> { - iter::empty() - } - - fn iter_variable_names(&self) -> Self::VariableNameIterator<'_> { - iter::empty() - } -} - -/// A context that returns `None` for each identifier. -/// Builtin functions are enabled and cannot be disabled. -#[derive(Debug, Default)] -pub struct EmptyContextWithBuiltinFunctions; - -impl Context for EmptyContextWithBuiltinFunctions { - fn get_value(&self, _identifier: &str) -> Option<&Value> { - None - } - - fn call_function(&self, identifier: &str, _argument: &Value) -> EvalexprResult { - Err(EvalexprError::FunctionIdentifierNotFound( - identifier.to_string(), - )) - } - - /// Builtin functions are always enabled for EmptyContextWithBuiltinFunctions. - fn are_builtin_functions_disabled(&self) -> bool { - false - } - - /// Builtin functions can't be disabled for EmptyContextWithBuiltinFunctions. - fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { - if disabled { - Err(EvalexprError::BuiltinFunctionsCannotBeDisabled) - } else { - Ok(()) - } - } -} - -impl IterateVariablesContext for EmptyContextWithBuiltinFunctions { - type VariableIterator<'a> = iter::Empty<(String, Value)>; - type VariableNameIterator<'a> = iter::Empty; - - fn iter_variables(&self) -> Self::VariableIterator<'_> { - iter::empty() - } - - fn iter_variable_names(&self) -> Self::VariableNameIterator<'_> { - iter::empty() - } -} - /// A context that stores its mappings in hash maps. /// /// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.* /// /// This context is type-safe, meaning that an identifier that is assigned a value of some type once cannot be assigned a value of another type. #[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] -pub struct HashMapContext { - variables: HashMap, - #[cfg_attr(feature = "serde_support", serde(skip))] - functions: HashMap, +pub struct ContextData { + map_name: String, - /// True if builtin functions are disabled. - without_builtin_functions: bool, + variables: HashMap, + + functions: HashMap, } -impl HashMapContext { +impl ContextData { /// Constructs a `HashMapContext` with no mappings. pub fn new() -> Self { Default::default() } } -impl Context for HashMapContext { +impl Context for ContextData { fn get_value(&self, identifier: &str) -> Option<&Value> { self.variables.get(identifier) } fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult { - if let Some(function) = self.functions.get(identifier) { - function.call(argument) - } else { - Err(EvalexprError::FunctionIdentifierNotFound( - identifier.to_string(), - )) + match identifier { + "map" => { + let map = Value::Map(self.variables.clone()); + self.variables.insert(identifier.to_string(), map); + Ok(Value::Empty) + }, + _ => todo!(), } } - fn are_builtin_functions_disabled(&self) -> bool { - self.without_builtin_functions - } - - fn set_builtin_functions_disabled(&mut self, disabled: bool) -> EvalexprResult<()> { - self.without_builtin_functions = disabled; - Ok(()) - } } -impl ContextWithMutableVariables for HashMapContext { +impl ContextWithMutableVariables for ContextData { fn set_value(&mut self, identifier: String, value: Value) -> EvalexprResult<()> { if let Some(existing_value) = self.variables.get_mut(&identifier) { if ValueType::from(&existing_value) == ValueType::from(&value) { @@ -229,14 +116,14 @@ impl ContextWithMutableVariables for HashMapContext { } } -impl ContextWithMutableFunctions for HashMapContext { +impl ContextWithMutableFunctions for ContextData { fn set_function(&mut self, identifier: String, function: Function) -> EvalexprResult<()> { self.functions.insert(identifier, function); Ok(()) } } -impl IterateVariablesContext for HashMapContext { +impl IterateVariablesContext for ContextData { type VariableIterator<'a> = std::iter::Map< std::collections::hash_map::Iter<'a, String, Value>, fn((&String, &Value)) -> (String, Value), diff --git a/src/error/display.rs b/src/error/display.rs index d7a4e75..e903f62 100644 --- a/src/error/display.rs +++ b/src/error/display.rs @@ -44,6 +44,7 @@ impl fmt::Display for EvalexprError { expected_len, actual ), ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual), + ExpectedMap { actual } => write!(f, "Expected a Value::Map, but got {:?}.", actual), AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."), PrecedenceViolation => write!( f, diff --git a/src/error/mod.rs b/src/error/mod.rs index e896973..47acf8a 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -91,6 +91,12 @@ pub enum EvalexprError { actual: Value, }, + /// A map value was expected. + ExpectedMap { + /// The actual value. + actual: Value, + }, + /// Tried to append a child to a leaf node. /// Leaf nodes cannot have children. AppendedToLeafNode, @@ -279,6 +285,11 @@ impl EvalexprError { pub fn expected_empty(actual: Value) -> Self { EvalexprError::ExpectedEmpty { actual } } + + /// Constructs `EvalexprError::ExpectedEmpty{actual}`. + pub fn expected_map(actual: Value) -> Self { + EvalexprError::ExpectedMap { actual } + } /// Constructs an error that expresses that the type of `expected` was expected, but `actual` was found. pub(crate) fn expected_type(expected: &Value, actual: Value) -> Self { @@ -289,6 +300,7 @@ impl EvalexprError { ValueType::Boolean => Self::expected_boolean(actual), ValueType::Tuple => Self::expected_tuple(actual), ValueType::Empty => Self::expected_empty(actual), + ValueType::Map => todo!(), } } diff --git a/src/function/builtin.rs b/src/function/builtin.rs index 7105743..fb64060 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -102,6 +102,7 @@ pub fn builtin_function(identifier: &str) -> Option { Value::Boolean(_) => "boolean", Value::Tuple(_) => "tuple", Value::Empty => "empty", + Value::Map(_) => "map", } .into()) })), diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 332b704..3650255 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -1,6 +1,6 @@ use crate::{ token, tree, value::TupleType, Context, ContextWithMutableVariables, EmptyType, EvalexprError, - EvalexprResult, FloatType, HashMapContext, IntType, Node, Value, EMPTY_VALUE, + EvalexprResult, FloatType, ContextData, IntType, Node, Value, EMPTY_VALUE, }; /// Evaluate the given expression string. @@ -15,7 +15,7 @@ use crate::{ /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval(string: &str) -> EvalexprResult { - eval_with_context_mut(string, &mut HashMapContext::new()) + eval_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string with the given context. @@ -91,21 +91,21 @@ pub fn build_operator_tree(string: &str) -> EvalexprResult { /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_string(string: &str) -> EvalexprResult { - eval_string_with_context_mut(string, &mut HashMapContext::new()) + eval_string_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string into an integer. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_int(string: &str) -> EvalexprResult { - eval_int_with_context_mut(string, &mut HashMapContext::new()) + eval_int_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string into a float. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_float(string: &str) -> EvalexprResult { - eval_float_with_context_mut(string, &mut HashMapContext::new()) + eval_float_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string into a float. @@ -113,28 +113,28 @@ pub fn eval_float(string: &str) -> EvalexprResult { /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_number(string: &str) -> EvalexprResult { - eval_number_with_context_mut(string, &mut HashMapContext::new()) + eval_number_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string into a boolean. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_boolean(string: &str) -> EvalexprResult { - eval_boolean_with_context_mut(string, &mut HashMapContext::new()) + eval_boolean_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string into a tuple. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_tuple(string: &str) -> EvalexprResult { - eval_tuple_with_context_mut(string, &mut HashMapContext::new()) + eval_tuple_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string into an empty value. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_empty(string: &str) -> EvalexprResult { - eval_empty_with_context_mut(string, &mut HashMapContext::new()) + eval_empty_with_context_mut(string, &mut ContextData::new()) } /// Evaluate the given expression string into a string with the given context. diff --git a/src/lib.rs b/src/lib.rs index b8ddb8c..e352b4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -561,8 +561,7 @@ extern crate serde_derive; pub use crate::{ context::{ - Context, ContextWithMutableFunctions, ContextWithMutableVariables, EmptyContext, - EmptyContextWithBuiltinFunctions, HashMapContext, IterateVariablesContext, + Context, ContextWithMutableFunctions, ContextWithMutableVariables, ContextData, IterateVariablesContext, }, error::{EvalexprError, EvalexprResult}, function::Function, diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 386357a..31f13b2 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -459,8 +459,7 @@ impl Operator { let arguments = &arguments[0]; match context.call_function(identifier, arguments) { - Err(EvalexprError::FunctionIdentifierNotFound(_)) - if !context.are_builtin_functions_disabled() => + Err(EvalexprError::FunctionIdentifierNotFound(_)) => { if let Some(builtin_function) = builtin_function(identifier) { builtin_function.call(arguments) diff --git a/src/tree/mod.rs b/src/tree/mod.rs index e835fd3..5422305 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,7 +1,7 @@ use crate::{ token::Token, value::{TupleType, EMPTY_VALUE}, - Context, ContextWithMutableVariables, EmptyType, FloatType, HashMapContext, IntType, + Context, ContextWithMutableVariables, EmptyType, FloatType, ContextData, IntType, }; use crate::{ @@ -346,7 +346,7 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval(&self) -> EvalexprResult { - self.eval_with_context_mut(&mut HashMapContext::new()) + self.eval_with_context_mut(&mut ContextData::new()) } /// Evaluates the operator tree rooted at this node into a string with an the given context. @@ -532,21 +532,21 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_string(&self) -> EvalexprResult { - self.eval_string_with_context_mut(&mut HashMapContext::new()) + self.eval_string_with_context_mut(&mut ContextData::new()) } /// Evaluates the operator tree rooted at this node into a float. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_float(&self) -> EvalexprResult { - self.eval_float_with_context_mut(&mut HashMapContext::new()) + self.eval_float_with_context_mut(&mut ContextData::new()) } /// Evaluates the operator tree rooted at this node into an integer. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_int(&self) -> EvalexprResult { - self.eval_int_with_context_mut(&mut HashMapContext::new()) + self.eval_int_with_context_mut(&mut ContextData::new()) } /// Evaluates the operator tree rooted at this node into a float. @@ -554,28 +554,28 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_number(&self) -> EvalexprResult { - self.eval_number_with_context_mut(&mut HashMapContext::new()) + self.eval_number_with_context_mut(&mut ContextData::new()) } /// Evaluates the operator tree rooted at this node into a boolean. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_boolean(&self) -> EvalexprResult { - self.eval_boolean_with_context_mut(&mut HashMapContext::new()) + self.eval_boolean_with_context_mut(&mut ContextData::new()) } /// Evaluates the operator tree rooted at this node into a tuple. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_tuple(&self) -> EvalexprResult { - self.eval_tuple_with_context_mut(&mut HashMapContext::new()) + self.eval_tuple_with_context_mut(&mut ContextData::new()) } /// Evaluates the operator tree rooted at this node into an empty value. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_empty(&self) -> EvalexprResult { - self.eval_empty_with_context_mut(&mut HashMapContext::new()) + self.eval_empty_with_context_mut(&mut ContextData::new()) } /// Returns the children of this node as a slice. diff --git a/src/value/display.rs b/src/value/display.rs index 599baa4..c8d2c96 100644 --- a/src/value/display.rs +++ b/src/value/display.rs @@ -23,6 +23,7 @@ impl Display for Value { write!(f, ")") }, Value::Empty => write!(f, "()"), + Value::Map(_) => write!(f, "{:?}", self), } } } diff --git a/src/value/mod.rs b/src/value/mod.rs index 05eaf50..d598557 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,5 +1,5 @@ use crate::error::{EvalexprError, EvalexprResult}; -use std::convert::TryFrom; +use std::{convert::TryFrom, collections::HashMap}; mod display; pub mod value_type; @@ -36,6 +36,8 @@ pub enum Value { Tuple(TupleType), /// An empty value. Empty, + /// Collection of key-value pairs. + Map(HashMap), } impl Value { diff --git a/src/value/value_type.rs b/src/value/value_type.rs index 4ea7395..ee11411 100644 --- a/src/value/value_type.rs +++ b/src/value/value_type.rs @@ -15,6 +15,8 @@ pub enum ValueType { Tuple, /// The `Value::Empty` type. Empty, + /// The `Value::Map` type. + Map, } impl From<&Value> for ValueType { @@ -26,6 +28,7 @@ impl From<&Value> for ValueType { Value::Boolean(_) => ValueType::Boolean, Value::Tuple(_) => ValueType::Tuple, Value::Empty => ValueType::Empty, + Value::Map(_) => ValueType::Map, } } } diff --git a/tests/integration.rs b/tests/integration.rs index 94e26a1..f43761e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -110,7 +110,7 @@ fn test_boolean_examples() { #[test] fn test_with_context() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); context .set_value("tr".into(), Value::Boolean(true)) .unwrap(); @@ -144,7 +144,7 @@ fn test_with_context() { #[test] fn test_functions() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); context .set_function( "sub2".to_string(), @@ -175,7 +175,7 @@ fn test_functions() { #[test] fn test_n_ary_functions() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); context .set_function( "sub2".into(), @@ -282,7 +282,7 @@ fn test_n_ary_functions() { #[test] fn test_capturing_functions() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); // this variable is captured by the function let three = 3; context @@ -601,7 +601,7 @@ fn test_no_panic() { #[test] fn test_shortcut_functions() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); context .set_value("string".into(), Value::from("a string")) .unwrap(); @@ -1284,7 +1284,7 @@ fn test_whitespace() { #[test] fn test_assignment() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); assert_eq!( eval_empty_with_context_mut("int = 3", &mut context), Ok(EMPTY_VALUE) @@ -1324,7 +1324,7 @@ fn test_assignment() { #[test] fn test_expression_chaining() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); assert_eq!( eval_int_with_context_mut("a = 5; a = a + 2; a", &mut context), Ok(7) @@ -1333,7 +1333,7 @@ fn test_expression_chaining() { #[test] fn test_strings() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); assert_eq!(eval("\"string\""), Ok(Value::from("string"))); assert_eq!( eval_with_context_mut("a = \"a string\"", &mut context), @@ -1441,7 +1441,7 @@ fn test_implicit_context() { #[test] fn test_operator_assignments() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); assert_eq!(eval_empty_with_context_mut("a = 5", &mut context), Ok(())); assert_eq!(eval_empty_with_context_mut("a += 5", &mut context), Ok(())); assert_eq!(eval_empty_with_context_mut("a -= 5", &mut context), Ok(())); @@ -1463,7 +1463,7 @@ fn test_operator_assignments() { Ok(()) ); - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); assert_eq!(eval_int_with_context_mut("a = 5; a", &mut context), Ok(5)); assert_eq!(eval_int_with_context_mut("a += 3; a", &mut context), Ok(8)); assert_eq!(eval_int_with_context_mut("a -= 5; a", &mut context), Ok(3)); @@ -1643,7 +1643,7 @@ fn test_hashmap_context_type_safety() { #[test] fn test_hashmap_context_clone_debug() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); // this variable is captured by the function let three = 3; context @@ -2228,7 +2228,7 @@ fn assignment_lhs_is_identifier() { let tree = build_operator_tree("a = 1").unwrap(); let operators: Vec<_> = tree.iter().map(|node| node.operator().clone()).collect(); - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); tree.eval_with_context_mut(&mut context).unwrap(); assert_eq!(context.get_value("a"), Some(&Value::Int(1))); @@ -2250,7 +2250,7 @@ fn assignment_lhs_is_identifier() { #[test] fn test_variable_assignment_and_iteration() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); eval_with_context_mut("a = 5; b = 5.0", &mut context).unwrap(); let mut variables: Vec<_> = context.iter_variables().collect(); @@ -2278,7 +2278,7 @@ fn test_negative_power() { #[test] fn test_builtin_functions_context() { - let mut context = HashMapContext::new(); + let mut context = ContextData::new(); // Builtin functions are enabled by default for HashMapContext. assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::from(3))); // Disabling builtin function in Context.