Merge pull request #123 from ISibboI/120-raising-a-number-to-a-negative-power-does-not-work-as-expected

Fix precedence of unary operators
This commit is contained in:
ISibboI 2023-04-13 15:51:22 +03:00 committed by GitHub
commit a951bc0193
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 247 additions and 85 deletions

View File

@ -1,62 +1,143 @@
name: Continuous Integration name: CI
on: [push, pull_request] on:
pull_request:
push:
env:
RUSTFLAGS: -Dwarnings
jobs: jobs:
build_test_format_lint: precheck_default:
name: ${{matrix.command.name}} (${{matrix.toolchain}}) name: Check default
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
toolchain: [stable, beta, nightly, 1.46.0] os: [ubuntu-latest]
command: rust: [stable]
- name: Check
command: check
args: --all-features
components: ""
- name: Test
command: test
args: --all-features
components: ""
include:
- toolchain: nightly
command:
name: Format
command: fmt
args: --all -- --check
components: rustfmt
- toolchain: nightly
command:
name: Lint
command: clippy
args: --all-features --tests --benches
components: clippy
- toolchain: stable
command:
name: Sync readme
command: sync-readme
args: --check
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@master
- name: ⚡ Cache
uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install toolchain - name: Install ${{ matrix.rust }}
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.rust }}
override: true override: true
default: true
components: ${{matrix.command.components}} - 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.56.1
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 - name: Install cargo-sync-readme
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -64,49 +145,93 @@ jobs:
command: install command: install
args: cargo-sync-readme args: cargo-sync-readme
- name: ${{matrix.command.name}} - name: Sync readme check
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: ${{matrix.command.command}} command: sync-readme
args: ${{matrix.command.args}} args: --check
coveralls_io: check_platform_compatibility:
name: Coverage needs: [precheck_default, precheck_all_features]
runs-on: ubuntu-latest name: Check platform compatibility
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
rust: [stable]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@master
- name: ⚡ Cache
uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install toolchain - name: Install ${{ matrix.rust }}
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: ${{ matrix.rust }}
override: true override: true
default: true
- name: Install cargo-tarpaulin - name: Rust cache
uses: actions-rs/install@v0.1 uses: Swatinem/rust-cache@v2
with: with:
crate: cargo-tarpaulin key: ${{ matrix.os }}
version: latest
use-tool-cache: true
- name: Coverage Report with tarpaulin - name: Check
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: tarpaulin command: check
args: --all --all-features --out Lcov -- --test-threads 1 args: --all-features --all --bins --examples --tests --lib
- name: Upload Coverage detailed_tests:
uses: coverallsapp/github-action@master 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: with:
github-token: ${{ secrets.GITHUB_TOKEN }} toolchain: ${{ matrix.rust }}
path-to-lcov: ./lcov.info 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

View File

@ -16,6 +16,19 @@
### Contributors ### Contributors
## [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 ## [8.2.0](https://github.com/ISibboI/evalexpr/compare/8.1.0...8.2.0) - 2023-04-13
### Added ### Added

View File

@ -11,7 +11,7 @@ documentation = "https://docs.rs/evalexpr"
readme = "README.md" readme = "README.md"
license = "MIT" license = "MIT"
edition = "2018" edition = "2018"
rust-version = "1.46.0" rust-version = "1.56.1"
[badges] [badges]
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }

View File

@ -173,6 +173,11 @@ impl Operator {
} }
} }
/// Returns true if this operator is unary, i.e. it requires exactly one argument.
pub(crate) fn is_unary(&self) -> bool {
self.max_argument_amount() == Some(1) && *self != Operator::RootNode
}
/// Evaluates the operator with the given arguments and context. /// Evaluates the operator with the given arguments and context.
pub(crate) fn eval<C: Context>( pub(crate) fn eval<C: Context>(
&self, &self,

View File

@ -459,8 +459,13 @@ impl Node {
} }
fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> EvalexprResult<()> { fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> EvalexprResult<()> {
// println!("Inserting {:?} into {:?}", node.operator, self.operator()); // println!(
if self.operator().precedence() < node.operator().precedence() || is_root_node // "Inserting {:?} into {:?}, is_root_node = {is_root_node}",
// node.operator(),
// self.operator()
// );
// println!("Self is {:?}", self);
if self.operator().precedence() < node.operator().precedence() || node.operator().is_unary() || is_root_node
// Right-to-left chaining // Right-to-left chaining
|| (self.operator().precedence() == node.operator().precedence() && !self.operator().is_left_to_right() && !node.operator().is_left_to_right()) || (self.operator().precedence() == node.operator().precedence() && !self.operator().is_left_to_right() && !node.operator().is_left_to_right())
{ {
@ -471,12 +476,15 @@ impl Node {
let last_child_operator = self.children.last().unwrap().operator(); let last_child_operator = self.children.last().unwrap().operator();
if last_child_operator.precedence() if last_child_operator.precedence()
< node.operator().precedence() < node.operator().precedence() || node.operator().is_unary()
// Right-to-left chaining // Right-to-left chaining
|| (last_child_operator.precedence() || (last_child_operator.precedence()
== node.operator().precedence() && !last_child_operator.is_left_to_right() && !node.operator().is_left_to_right()) == node.operator().precedence() && !last_child_operator.is_left_to_right() && !node.operator().is_left_to_right())
{ {
// println!("Recursing into {:?}", self.children.last().unwrap().operator()); // println!(
// "Recursing into {:?}",
// self.children.last().unwrap().operator()
// );
// Unwrap cannot fail because is_leaf being false and has_enough_children being true implies that the operator wants and has at least one child // Unwrap cannot fail because is_leaf being false and has_enough_children being true implies that the operator wants and has at least one child
self.children self.children
.last_mut() .last_mut()

View File

@ -2145,3 +2145,14 @@ fn test_variable_assignment_and_iteration() {
variables.sort_unstable(); variables.sort_unstable();
assert_eq!(variables, vec!["a".to_string(), "b".to_string()],); assert_eq!(variables, vec!["a".to_string(), "b".to_string()],);
} }
#[test]
fn test_negative_power() {
println!("{:?}", build_operator_tree("3^-2").unwrap());
assert_eq!(eval("3^-2"), Ok(Value::Float(1.0 / 9.0)));
assert_eq!(eval("3^(-2)"), Ok(Value::Float(1.0 / 9.0)));
assert_eq!(eval("-3^2"), Ok(Value::Float(-9.0)));
assert_eq!(eval("-(3)^2"), Ok(Value::Float(-9.0)));
assert_eq!(eval("(-3)^-2"), Ok(Value::Float(1.0 / 9.0)));
assert_eq!(eval("-(3^-2)"), Ok(Value::Float(-1.0 / 9.0)));
}